Merge "Revert "add media.swcodec to Bazel prod allowlist""
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index d19b543..ae8276f 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -19,6 +19,7 @@
         "aconfig_declarations.go",
         "aconfig_values.go",
         "aconfig_value_set.go",
+        "all_aconfig_declarations.go",
         "init.go",
         "java_aconfig_library.go",
         "testing.go",
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
new file mode 100644
index 0000000..6096c6c
--- /dev/null
+++ b/aconfig/all_aconfig_declarations.go
@@ -0,0 +1,63 @@
+// Copyright 2023 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 aconfig
+
+import (
+	"android/soong/android"
+)
+
+// A singleton module that collects all of the aconfig flags declared in the
+// tree into a single combined file for export to the external flag setting
+// server (inside Google it's Gantry).
+//
+// Note that this is ALL aconfig_declarations modules present in the tree, not just
+// ones that are relevant to the product currently being built, so that that infra
+// doesn't need to pull from multiple builds and merge them.
+func AllAconfigDeclarationsFactory() android.Singleton {
+	return &allAconfigDeclarationsSingleton{}
+}
+
+type allAconfigDeclarationsSingleton struct {
+	intermediatePath android.OutputPath
+}
+
+func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// Find all of the aconfig_declarations modules
+	var cacheFiles android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if !ctx.ModuleHasProvider(module, declarationsProviderKey) {
+			return
+		}
+		decl := ctx.ModuleProvider(module, declarationsProviderKey).(declarationsProviderData)
+		cacheFiles = append(cacheFiles, decl.IntermediatePath)
+	})
+
+	// Generate build action for aconfig
+	this.intermediatePath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRule,
+		Inputs:      cacheFiles,
+		Output:      this.intermediatePath,
+		Description: "all_aconfig_declarations",
+		Args: map[string]string{
+			"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
+		},
+	})
+	ctx.Phony("all_aconfig_declarations", this.intermediatePath)
+}
+
+func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("droid", this.intermediatePath)
+}
diff --git a/aconfig/init.go b/aconfig/init.go
index 6b433c9..161fd42 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -54,6 +54,15 @@
 			},
 			Restat: true,
 		})
+
+	// For all_aconfig_declarations
+	allDeclarationsRule = pctx.AndroidStaticRule("all_aconfig_declarations_dump",
+		blueprint.RuleParams{
+			Command: `${aconfig} dump --format protobuf --out ${out} ${cache_files}`,
+			CommandDeps: []string{
+				"${aconfig}",
+			},
+		}, "cache_files")
 )
 
 func init() {
@@ -67,4 +76,5 @@
 	ctx.RegisterModuleType("aconfig_values", ValuesFactory)
 	ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory)
 	ctx.RegisterModuleType("java_aconfig_library", JavaDeclarationsLibraryFactory)
+	ctx.RegisterParallelSingletonType("all_aconfig_declarations", AllAconfigDeclarationsFactory)
 }
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 34a6860..d320599 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -131,6 +131,7 @@
 		"external/brotli":                        Bp2BuildDefaultTrue,
 		"external/bsdiff":                        Bp2BuildDefaultTrueRecursively,
 		"external/bzip2":                         Bp2BuildDefaultTrueRecursively,
+		"external/clang/lib":                     Bp2BuildDefaultTrue,
 		"external/conscrypt":                     Bp2BuildDefaultTrue,
 		"external/e2fsprogs":                     Bp2BuildDefaultTrueRecursively,
 		"external/eigen":                         Bp2BuildDefaultTrueRecursively,
@@ -429,6 +430,7 @@
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
 		"external/bazelbuild-rules_license":/* recursive = */ true,
+		"external/bazelbuild-rules_go":/* recursive = */ true,
 		"external/bazelbuild-kotlin-rules":/* recursive = */ true,
 		"external/bazel-skylib":/* recursive = */ true,
 		"external/protobuf":/* recursive = */ false,
@@ -796,6 +798,13 @@
 
 		// for platform_compat_config
 		"process-compat-config",
+
+		// cc_* modules with rscript srcs
+		"rstest-latency",
+		"libRScpp_static",
+		"rs-headers",
+		"rs_script_api",
+		"libRSDispatch",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -940,12 +949,8 @@
 
 		//system/libvintf
 		// depends on apex-info-list-tinyxml, unconverted xsd_config Soong module type.
-		"libvintf",
-		"vintf",
 		"libassemblevintf",
 		"assemble_vintf",
-		"libvintffm",
-		"vintffm",
 		"checkvintf",
 
 		// depends on audio_policy_configuration_aidl_default, xsd_config module.
diff --git a/android/config.go b/android/config.go
index 839ff05..fa43962 100644
--- a/android/config.go
+++ b/android/config.go
@@ -701,6 +701,10 @@
 		if c.productVariables.DeviceArch != nil && *c.productVariables.DeviceArch == "riscv64" {
 			return false
 		}
+		// Disable Bazel when Kythe is running
+		if c.EmitXrefRules() {
+			return false
+		}
 		if c.IsEnvTrue("GLOBAL_THINLTO") {
 			return false
 		}
diff --git a/android/depset_generic.go b/android/depset_generic.go
index ae14d32..4f31b86 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -175,16 +175,6 @@
 // its transitive dependencies, in which case the ordering of the duplicated element is not
 // guaranteed).
 func (d *DepSet[T]) ToList() []T {
-	return d.toList(firstUnique[T])
-}
-
-// toList returns the DepSet flattened to a list.  The order in the list is based on the order
-// of the DepSet.  POSTORDER and PREORDER orders return a postordered or preordered left to right
-// flattened list.  TOPOLOGICAL returns a list that guarantees that elements of children are listed
-// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
-// its transitive dependencies, in which case the ordering of the duplicated element is not
-// guaranteed).  The firstUniqueFunc is used to remove duplicates from the list.
-func (d *DepSet[T]) toList(firstUniqueFunc func([]T) []T) []T {
 	if d == nil {
 		return nil
 	}
@@ -192,7 +182,7 @@
 	d.walk(func(paths []T) {
 		list = append(list, paths...)
 	})
-	list = firstUniqueFunc(list)
+	list = firstUniqueInPlace(list)
 	if d.reverse {
 		reverseSliceInPlace(list)
 	}
diff --git a/android/neverallow.go b/android/neverallow.go
index 41105e6..24031ba 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -258,10 +258,10 @@
 
 func createJavaExcludeStaticLibsRule() Rule {
 	return NeverAllow().
-		NotIn("build/soong").
+		NotIn("build/soong", "libcore", "frameworks/base/api").
 		ModuleType("java_library").
 		WithMatcher("exclude_static_libs", isSetMatcherInstance).
-		Because("exclude_static_libs property is only allowed for java modules defined in build/soong")
+		Because("exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api")
 }
 
 func neverallowMutator(ctx BottomUpMutatorContext) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 1639bbf..2a938b8 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -358,7 +358,7 @@
 			`),
 		},
 		expectedErrors: []string{
-			`exclude_static_libs property is only allowed for java modules defined in build/soong`,
+			`exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`,
 		},
 	},
 }
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 80aeb2e..1fadda0 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -84,25 +84,6 @@
 	}
 }
 
-// JavaApiLibraryName returns the name of .txt equivalent of a java_library, but does
-// not check if either module exists.
-// TODO: Return .txt (single-tree or multi-tree equivalents) based on config
-func JavaApiLibraryName(c Config, name string) string {
-	if c.BuildFromTextStub() {
-		return name + ".from-text"
-	}
-	return name
-}
-
-// JavaApiLibraryNames applies JavaApiLibraryName to the list of java_library names.
-func JavaApiLibraryNames(c Config, names []string) []string {
-	apiLibs := make([]string, len(names))
-	for i, name := range names {
-		apiLibs[i] = JavaApiLibraryName(c, name)
-	}
-	return apiLibs
-}
-
 func (k SdkKind) DefaultJavaLibraryName() string {
 	switch k {
 	case SdkPublic:
diff --git a/android/test_suites.go b/android/test_suites.go
index b48d71a..63a709f 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -68,7 +68,8 @@
 		FlagWithOutput("-o ", outputFile).
 		FlagWithArg("-P ", "host/testcases").
 		FlagWithArg("-C ", testCasesDir.String()).
-		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths())
+		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()).
+        Flag("-sha256")
 	rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
 
 	return outputFile
diff --git a/android/util.go b/android/util.go
index c4ce71a..50bf5aa 100644
--- a/android/util.go
+++ b/android/util.go
@@ -25,12 +25,12 @@
 )
 
 // CopyOf returns a new slice that has the same contents as s.
-func CopyOf(s []string) []string {
+func CopyOf[T any](s []T) []T {
 	// If the input is nil, return nil and not an empty list
 	if s == nil {
 		return s
 	}
-	return append([]string{}, s...)
+	return append([]T{}, s...)
 }
 
 // Concat returns a new slice concatenated from the two input slices. It does not change the input
@@ -42,6 +42,16 @@
 	return res
 }
 
+// JoinPathsWithPrefix converts the paths to strings, prefixes them
+// with prefix and then joins them separated by " ".
+func JoinPathsWithPrefix(paths []Path, prefix string) string {
+	strs := make([]string, len(paths))
+	for i := range paths {
+		strs[i] = paths[i].String()
+	}
+	return JoinWithPrefixAndSeparator(strs, prefix, " ")
+}
+
 // JoinWithPrefix prepends the prefix to each string in the list and
 // returns them joined together with " " as separator.
 func JoinWithPrefix(strs []string, prefix string) string {
@@ -278,22 +288,25 @@
 }
 
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
-// each.  It modifies the slice contents in place, and returns a subslice of the original slice.
+// each.  It does not modify the input slice.
 func FirstUniqueStrings(list []string) []string {
-	// Do not moodify the input in-place, operate on a copy instead.
-	list = CopyOf(list)
-	// 128 was chosen based on BenchmarkFirstUniqueStrings results.
-	if len(list) > 128 {
-		return firstUnique(list)
-	}
 	return firstUnique(list)
 }
 
 // firstUnique returns all unique elements of a slice, keeping the first copy of each.  It
-// modifies the slice contents in place, and returns a subslice of the original slice.
+// does not modify the input slice.
 func firstUnique[T comparable](slice []T) []T {
-	// 4 was chosen based on Benchmark_firstUnique results.
-	if len(slice) > 4 {
+	// Do not modify the input in-place, operate on a copy instead.
+	slice = CopyOf(slice)
+	return firstUniqueInPlace(slice)
+}
+
+// firstUniqueInPlace returns all unique elements of a slice, keeping the first copy of
+// each.  It modifies the slice contents in place, and returns a subslice of the original
+// slice.
+func firstUniqueInPlace[T comparable](slice []T) []T {
+	// 128 was chosen based on BenchmarkFirstUniqueStrings results.
+	if len(slice) > 128 {
 		return firstUniqueMap(slice)
 	}
 	return firstUniqueList(slice)
diff --git a/android/util_test.go b/android/util_test.go
index bee31a9..0e28b56 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -385,7 +385,7 @@
 	emptyList := []string{}
 	copyOfEmptyList := CopyOf(emptyList)
 	AssertBoolEquals(t, "Copy of an empty list should be an empty list and not nil", true, copyOfEmptyList != nil)
-	copyOfNilList := CopyOf(nil)
+	copyOfNilList := CopyOf([]string(nil))
 	AssertBoolEquals(t, "Copy of a nil list should be a nil list and not an empty list", true, copyOfNilList == nil)
 }
 
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index ab6e4a5..18cb9e1 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -1182,3 +1182,35 @@
 		},
 	})
 }
+
+func TestCCBinaryRscriptSrc(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: `cc_binary with rscript files in sources`,
+		blueprint: `
+{rule_name} {
+    name : "foo",
+    srcs : [
+        "ccSrc.cc",
+        "rsSrc.rscript",
+    ],
+    include_build_directory: false,
+}
+`,
+		targets: []testBazelTarget{
+			{"rscript_to_cpp", "foo_renderscript", AttrNameToString{
+				"srcs": `["rsSrc.rscript"]`,
+			}},
+			{"cc_binary", "foo", AttrNameToString{
+				"absolute_includes": `[
+        "frameworks/rs",
+        "frameworks/rs/cpp",
+    ]`,
+				"local_includes": `["."]`,
+				"srcs": `[
+        "ccSrc.cc",
+        "foo_renderscript",
+    ]`,
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index a16cfb3..2d61d53 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -1555,3 +1555,33 @@
 		},
 	})
 }
+
+func TestCCLibrarySharedRscriptSrc(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: ``,
+		Blueprint: `
+cc_library_shared{
+    name : "foo",
+    srcs : [
+        "ccSrc.cc",
+        "rsSrc.rscript",
+    ],
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("rscript_to_cpp", "foo_renderscript", AttrNameToString{
+				"srcs": `["rsSrc.rscript"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"absolute_includes": `[
+        "frameworks/rs",
+        "frameworks/rs/cpp",
+    ]`,
+				"local_includes": `["."]`,
+				"srcs": `[
+        "ccSrc.cc",
+        "foo_renderscript",
+    ]`,
+			})}})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index b473f27..18225df 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -2185,3 +2185,33 @@
 		},
 	})
 }
+
+func TestCCLibraryStaticRscriptSrc(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: `cc_library_static with rscript files in sources`,
+		Blueprint: `
+cc_library_static{
+    name : "foo",
+    srcs : [
+        "ccSrc.cc",
+        "rsSrc.rscript",
+    ],
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("rscript_to_cpp", "foo_renderscript", AttrNameToString{
+				"srcs": `["rsSrc.rscript"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"absolute_includes": `[
+        "frameworks/rs",
+        "frameworks/rs/cpp",
+    ]`,
+				"local_includes": `["."]`,
+				"srcs": `[
+        "ccSrc.cc",
+        "foo_renderscript",
+    ]`,
+			})}})
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index d09cdcd..5459595 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -38,7 +38,10 @@
 	protoSrcPartition   = "proto"
 	aidlSrcPartition    = "aidl"
 	syspropSrcPartition = "sysprop"
-	yaccSrcPartition    = "yacc"
+
+	yaccSrcPartition = "yacc"
+
+	rScriptSrcPartition = "renderScript"
 
 	stubsSuffix = "_stub_libs_current"
 )
@@ -149,8 +152,9 @@
 		// 		contains .l or .ll files we will need to find a way to add a
 		// 		LabelMapper for these that identifies these filegroups and
 		//		converts them appropriately
-		lSrcPartition:  bazel.LabelPartition{Extensions: []string{".l"}},
-		llSrcPartition: bazel.LabelPartition{Extensions: []string{".ll"}},
+		lSrcPartition:       bazel.LabelPartition{Extensions: []string{".l"}},
+		llSrcPartition:      bazel.LabelPartition{Extensions: []string{".ll"}},
+		rScriptSrcPartition: bazel.LabelPartition{Extensions: []string{".fs", ".rscript"}},
 		// C++ is the "catch-all" group, and comprises generated sources because we don't
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition:     bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
@@ -398,6 +402,9 @@
 	cppFlags bazel.StringListAttribute
 	srcs     bazel.LabelListAttribute
 
+	// xsd config sources
+	xsdInSrcs bazel.StringListAttribute
+
 	// Lex sources and options
 	lSrcs   bazel.LabelListAttribute
 	llSrcs  bazel.LabelListAttribute
@@ -412,6 +419,8 @@
 	yaccGenLocationHeader bazel.BoolAttribute
 	yaccGenPositionHeader bazel.BoolAttribute
 
+	rsSrcs bazel.LabelListAttribute
+
 	hdrs bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
@@ -426,8 +435,9 @@
 
 	includes BazelIncludes
 
-	protoSrcs bazel.LabelListAttribute
-	aidlSrcs  bazel.LabelListAttribute
+	protoSrcs   bazel.LabelListAttribute
+	aidlSrcs    bazel.LabelListAttribute
+	rscriptSrcs bazel.LabelListAttribute
 
 	stubsSymbolFile *string
 	stubsVersions   bazel.StringListAttribute
@@ -484,9 +494,14 @@
 func (ca *compilerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, props *BaseCompilerProperties) {
 	// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
 	// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
-	if srcsList, ok := parseSrcs(ctx, props); ok {
+	srcsList, xsdList, ok := parseSrcs(ctx, props)
+
+	if ok {
 		ca.srcs.SetSelectValue(axis, config, srcsList)
 	}
+	if len(xsdList) > 0 {
+		ca.xsdInSrcs.SetSelectValue(axis, config, xsdList)
+	}
 
 	localIncludeDirs := props.Local_include_dirs
 	if axis == bazel.NoConfigAxis {
@@ -582,17 +597,18 @@
 		ca.yaccSrc = bazel.MakeLabelAttribute(yacc.Value.Includes[0].Label)
 	}
 	ca.syspropSrcs = partitionedSrcs[syspropSrcPartition]
+	ca.rscriptSrcs = partitionedSrcs[rScriptSrcPartition]
 
 	ca.absoluteIncludes.DeduplicateAxesFromBase()
 	ca.localIncludes.DeduplicateAxesFromBase()
 }
 
 // Parse srcs from an arch or OS's props value.
-func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, bool) {
+func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, []string, bool) {
 	anySrcs := false
 	// Add srcs-like dependencies such as generated files.
 	// First create a LabelList containing these dependencies, then merge the values with srcs.
-	genSrcs, _ := android.PartitionXsdSrcs(ctx, props.Generated_sources)
+	genSrcs, xsd := android.PartitionXsdSrcs(ctx, props.Generated_sources)
 	generatedSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, genSrcs, props.Exclude_generated_sources)
 	if len(props.Generated_sources) > 0 || len(props.Exclude_generated_sources) > 0 {
 		anySrcs = true
@@ -604,7 +620,7 @@
 		anySrcs = true
 	}
 
-	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
+	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), xsd, anySrcs
 }
 
 func bp2buildStdVal(std *string, prefix string, useGnu bool) *string {
@@ -718,14 +734,6 @@
 	return ret
 }
 
-// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-cpp
-func xsdConfigCppTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
-	callback := func(xsd android.XsdConfigBp2buildTargets) string {
-		return xsd.CppBp2buildTargetName()
-	}
-	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
-}
-
 // bp2BuildParseBaseProps returns all compiler, linker, library attributes of a cc module..
 func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) baseAttributes {
 	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
@@ -762,15 +770,9 @@
 	for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
 		configs := axisToConfigs[axis]
 		for cfg := range configs {
-			var allHdrs []string
+			var allHdrs, allHdrsXsd []string
 			if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
-				ah, allHdrsXsd := android.PartitionXsdSrcs(ctx, baseCompilerProps.Generated_headers)
-				allHdrs = ah
-				// in the synthetic bp2build workspace, xsd sources are compiled to a static library
-				xsdCppConfigLibraryLabels := android.BazelLabelForModuleDepsWithFn(ctx, allHdrsXsd, xsdConfigCppTarget)
-				iwad := linkerAttrs.implementationWholeArchiveDeps.SelectValue(axis, cfg)
-				(&iwad).Append(xsdCppConfigLibraryLabels)
-				linkerAttrs.implementationWholeArchiveDeps.SetSelectValue(axis, cfg, bazel.FirstUniqueBazelLabelList(iwad))
+				allHdrs, allHdrsXsd = android.PartitionXsdSrcs(ctx, baseCompilerProps.Generated_headers)
 
 				if baseCompilerProps.Lex != nil {
 					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
@@ -784,14 +786,19 @@
 				aidlLibs.Append(android.BazelLabelForModuleDeps(ctx, baseCompilerProps.Aidl.Libs))
 			}
 
-			var exportHdrs []string
+			var exportHdrs, exportHdrsXsd []string
 
 			if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
-				exportHdrs = baseLinkerProps.Export_generated_headers
-
+				exportHdrs, exportHdrsXsd = android.PartitionXsdSrcs(ctx, baseLinkerProps.Export_generated_headers)
 				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module, axis, cfg, baseLinkerProps)
 			}
+
+			// in the synthetic bp2build workspace, xsd sources are compiled to a static library
+			xsdList := compilerAttrs.xsdInSrcs.SelectValue(axis, cfg)
+			allHdrsXsd = android.FirstUniqueStrings(append(xsdList, allHdrsXsd...))
 			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
+			xsdConfigLibs := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrsXsd, exportHdrsXsd, bazelLabelForXsdConfig)
+
 			implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
 			compilerAttrs.hdrs.SetSelectValue(axis, cfg, headers.export)
 
@@ -827,6 +834,15 @@
 					compilerAttrs.suffix.SetSelectValue(axis, cfg, suffix)
 				}
 			}
+
+			if len(allHdrsXsd) > 0 {
+				wholeStaticLibs := linkerAttrs.implementationWholeArchiveDeps.SelectValue(axis, cfg)
+				(&wholeStaticLibs).Append(xsdConfigLibs.implementation)
+				linkerAttrs.implementationWholeArchiveDeps.SetSelectValue(axis, cfg, wholeStaticLibs)
+				wholeStaticLibs = linkerAttrs.wholeArchiveDeps.SelectValue(axis, cfg)
+				(&wholeStaticLibs).Append(xsdConfigLibs.export)
+				linkerAttrs.wholeArchiveDeps.SetSelectValue(axis, cfg, wholeStaticLibs)
+			}
 		}
 	}
 
@@ -903,6 +919,12 @@
 	compilerAttrs.absoluteIncludes.Prepend = true
 	compilerAttrs.hdrs.Prepend = true
 
+	convertedRsSrcs, rsAbsIncludes, rsLocalIncludes := bp2buildRScript(ctx, module, compilerAttrs)
+	(&compilerAttrs).srcs.Add(&convertedRsSrcs)
+	(&compilerAttrs).absoluteIncludes.Append(rsAbsIncludes)
+	(&compilerAttrs).localIncludes.Append(rsLocalIncludes)
+	(&compilerAttrs).localIncludes.Value = android.FirstUniqueStrings(compilerAttrs.localIncludes.Value)
+
 	features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
 	features = features.Append(bp2buildLtoFeatures(ctx, module))
 	features = features.Append(convertHiddenVisibilityToFeatureBase(ctx, module))
@@ -1703,6 +1725,18 @@
 	return label
 }
 
+// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-cpp
+func xsdConfigCppTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
+	callback := func(xsd android.XsdConfigBp2buildTargets) string {
+		return xsd.CppBp2buildTargetName()
+	}
+	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
+}
+
+func bazelLabelForXsdConfig(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, xsdConfigCppTarget)
+}
+
 func bazelLabelForWholeDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps)
 }
diff --git a/cc/cc.go b/cc/cc.go
index 6a67e54..6087970 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -25,6 +25,7 @@
 	"strings"
 
 	"android/soong/ui/metrics/bp2build_metrics_proto"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -1922,9 +1923,37 @@
 		//TODO(b/278772861) support sanitizers in Bazel rules
 		return false
 	}
+	if !imageVariantSupportedByBazel(c) {
+		return false
+	}
+	if c.IsSdkVariant() {
+		return false
+	}
 	return c.bazelHandler != nil
 }
 
+func imageVariantSupportedByBazel(c *Module) bool {
+	if c.IsLlndk() {
+		return false
+	}
+	if c.InVendor() {
+		return false
+	}
+	if c.InProduct() {
+		return false
+	}
+	if c.InRamdisk() {
+		return false
+	}
+	if c.InVendorRamdisk() {
+		return false
+	}
+	if c.InRecovery() {
+		return false
+	}
+	return true
+}
+
 func allEnabledSanitizersSupportedByBazel(ctx android.BaseModuleContext, c *Module) bool {
 	if c.sanitize == nil {
 		return true
diff --git a/cc/config/global.go b/cc/config/global.go
index 8ff5f55..e450ba7 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -448,11 +448,12 @@
 	pctx.StaticVariable("RSLLVMPrebuiltsPath", "${RSClangBase}/${HostPrebuiltTag}/${RSClangVersion}/bin")
 	pctx.StaticVariable("RSIncludePath", "${RSLLVMPrebuiltsPath}/../lib64/clang/${RSReleaseVersion}/include")
 
-	pctx.PrefixedExistentPathsForSourcesVariable("RsGlobalIncludes", "-I",
-		[]string{
-			"external/clang/lib/Headers",
-			"frameworks/rs/script_api/include",
-		})
+	rsGlobalIncludes := []string{
+		"external/clang/lib/Headers",
+		"frameworks/rs/script_api/include",
+	}
+	pctx.PrefixedExistentPathsForSourcesVariable("RsGlobalIncludes", "-I", rsGlobalIncludes)
+	exportedVars.ExportStringList("RsGlobalIncludes", rsGlobalIncludes)
 
 	pctx.VariableFunc("CcWrapper", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("CC_WRAPPER"); override != "" {
diff --git a/cc/lto.go b/cc/lto.go
index 878c21f..547ebff 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -109,7 +109,7 @@
 
 		// If the module does not have a profile, be conservative and limit cross TU inline
 		// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
-		if !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
+		if !ctx.Darwin() && !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
 			flags.Local.LdFlags = append(flags.Local.LdFlags,
 				"-Wl,-plugin-opt,-import-instr-limit=5")
 		}
diff --git a/cc/rs.go b/cc/rs.go
index fbc86e2..6507259 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -15,11 +15,12 @@
 package cc
 
 import (
-	"android/soong/android"
 	"path/filepath"
 	"runtime"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/bazel"
 	"github.com/google/blueprint"
 )
 
@@ -132,3 +133,35 @@
 
 	return flags
 }
+
+type rscriptAttributes struct {
+	// Renderscript source files
+	Srcs bazel.LabelListAttribute
+}
+
+func bp2buildRScript(ctx android.Bp2buildMutatorContext, m *Module, ca compilerAttributes) (bazel.LabelAttribute, bazel.StringListAttribute, bazel.StringListAttribute) {
+	var rscriptAttrs rscriptAttributes
+	var rsAbsIncludes bazel.StringListAttribute
+	var localIncludes bazel.StringListAttribute
+	var rsModuleName string
+	var convertedRsSrcsLabel bazel.LabelAttribute
+
+	if !ca.rscriptSrcs.IsEmpty() {
+		rscriptAttrs.Srcs = ca.rscriptSrcs
+		rsModuleName = m.Name() + "_renderscript"
+
+		localIncludes.Value = []string{"."}
+		rsAbsIncludes.Value = []string{"frameworks/rs", "frameworks/rs/cpp"}
+		convertedRsSrcsLabel = bazel.LabelAttribute{Value: &bazel.Label{Label: rsModuleName}}
+
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "rscript_to_cpp",
+				Bzl_load_location: "//build/bazel/rules/cc:rscript_to_cpp.bzl",
+			},
+			android.CommonAttributes{Name: rsModuleName},
+			&rscriptAttrs)
+	}
+
+	return convertedRsSrcsLabel, rsAbsIncludes, localIncludes
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 7dedd60..62e31d1 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -104,6 +104,7 @@
 	Fuzzer
 	Memtag_heap
 	Memtag_stack
+	Memtag_globals
 	cfi // cfi is last to prevent it running before incompatible mutators
 )
 
@@ -116,6 +117,7 @@
 	Fuzzer,
 	Memtag_heap,
 	Memtag_stack,
+	Memtag_globals,
 	cfi, // cfi is last to prevent it running before incompatible mutators
 }
 
@@ -138,6 +140,8 @@
 		return "memtag_heap"
 	case Memtag_stack:
 		return "memtag_stack"
+	case Memtag_globals:
+		return "memtag_globals"
 	case Fuzzer:
 		return "fuzzer"
 	default:
@@ -156,6 +160,8 @@
 		return "memtag_heap"
 	case Memtag_stack:
 		return "memtag_stack"
+	case Memtag_globals:
+		return "memtag_globals"
 	case tsan:
 		return "thread"
 	case intOverflow:
@@ -177,7 +183,7 @@
 		sanitizer := &sanitizerSplitMutator{t}
 		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
-	case Memtag_heap, Memtag_stack, intOverflow:
+	case Memtag_heap, Memtag_stack, Memtag_globals, intOverflow:
 		// do nothing
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
@@ -218,6 +224,8 @@
 		return true
 	case Memtag_stack:
 		return true
+	case Memtag_globals:
+		return true
 	default:
 		return false
 	}
@@ -272,6 +280,9 @@
 	// Memory-tagging stack instrumentation, only available on arm64
 	// Adds instrumentation to detect stack buffer overflows and use-after-scope using MTE.
 	Memtag_stack *bool `android:"arch_variant"`
+	// Memory-tagging globals instrumentation, only available on arm64
+	// Adds instrumentation to detect global buffer overflows using MTE.
+	Memtag_globals *bool `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -347,6 +358,8 @@
 	Memtag_heap *bool `blueprint:"mutated"`
 	// Whether Memory-tagging stack instrumentation is enabled for this module
 	Memtag_stack *bool `blueprint:"mutated"`
+	// Whether Memory-tagging globals instrumentation is enabled for this module
+	Memtag_globals *bool `android:"arch_variant"`
 
 	// Whether a modifier for ASAN and HWASAN for write only instrumentation is enabled for this
 	// module
@@ -431,6 +444,7 @@
 	p.Integer_overflow = userProps.Integer_overflow
 	p.Memtag_heap = userProps.Memtag_heap
 	p.Memtag_stack = userProps.Memtag_stack
+	p.Memtag_globals = userProps.Memtag_globals
 	p.Safestack = userProps.Safestack
 	p.Scs = userProps.Scs
 	p.Scudo = userProps.Scudo
@@ -558,6 +572,10 @@
 			s.Memtag_stack = proptools.BoolPtr(true)
 		}
 
+		if found, globalSanitizers = removeFromList("memtag_globals", globalSanitizers); found && s.Memtag_globals == nil {
+			s.Memtag_globals = proptools.BoolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -638,6 +656,7 @@
 	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() || ctx.Host() {
 		s.Memtag_heap = nil
 		s.Memtag_stack = nil
+		s.Memtag_globals = nil
 	}
 
 	// Also disable CFI if ASAN is enabled.
@@ -647,6 +666,7 @@
 		// HWASAN and ASAN win against MTE.
 		s.Memtag_heap = nil
 		s.Memtag_stack = nil
+		s.Memtag_globals = nil
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
@@ -717,7 +737,8 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack) ||
+		Bool(s.Memtag_globals)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
@@ -888,7 +909,7 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
 	}
 
-	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack)) && ctx.binary() {
+	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack) || Bool(sanProps.Memtag_globals)) && ctx.binary() {
 		if Bool(sanProps.Diag.Memtag_heap) {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
 		} else {
@@ -1011,6 +1032,8 @@
 		return s.Properties.SanitizeMutated.Memtag_heap
 	case Memtag_stack:
 		return s.Properties.SanitizeMutated.Memtag_stack
+	case Memtag_globals:
+		return s.Properties.SanitizeMutated.Memtag_globals
 	case Fuzzer:
 		return s.Properties.SanitizeMutated.Fuzzer
 	default:
@@ -1027,6 +1050,7 @@
 		!sanitize.isSanitizerEnabled(scs) &&
 		!sanitize.isSanitizerEnabled(Memtag_heap) &&
 		!sanitize.isSanitizerEnabled(Memtag_stack) &&
+		!sanitize.isSanitizerEnabled(Memtag_globals) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
@@ -1048,10 +1072,12 @@
 		sanitize.Properties.SanitizeMutated.Address = bPtr
 		// For ASAN variant, we need to disable Memtag_stack
 		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_globals = nil
 	case Hwasan:
 		sanitize.Properties.SanitizeMutated.Hwaddress = bPtr
 		// For HWAsan variant, we need to disable Memtag_stack
 		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_globals = nil
 	case tsan:
 		sanitize.Properties.SanitizeMutated.Thread = bPtr
 	case intOverflow:
@@ -1065,6 +1091,8 @@
 	case Memtag_stack:
 		sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr
 		// We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
+	case Memtag_globals:
+		sanitize.Properties.Sanitize.Memtag_globals = bPtr
 	case Fuzzer:
 		sanitize.Properties.SanitizeMutated.Fuzzer = bPtr
 	default:
@@ -1552,6 +1580,13 @@
 			sanitizers = append(sanitizers, "memtag-stack")
 		}
 
+		if Bool(sanProps.Memtag_globals) {
+			sanitizers = append(sanitizers, "memtag-globals")
+			// TODO(mitchp): For now, enable memtag-heap with memtag-globals because the linker
+			// isn't new enough (https://reviews.llvm.org/differential/changeset/?ref=4243566).
+			sanitizers = append(sanitizers, "memtag-heap")
+		}
+
 		if Bool(sanProps.Fuzzer) {
 			sanitizers = append(sanitizers, "fuzzer-no-link")
 		}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 10a5762..22d64a2 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -725,18 +725,6 @@
 
 		// FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue
 		"frameworks/compile/slang",
-
-		// FIXME(b/260809113): 'prebuilts/clang/host/linux-x86/clang-dev' is a tool-generated symlink
-		// directory that contains a BUILD file. The bazel files finder code doesn't traverse into symlink dirs,
-		// and hence is not aware of this BUILD file and exclude it accordingly during symlink forest generation
-		// when checking against keepExistingBuildFiles allowlist.
-		//
-		// This is necessary because globs in //prebuilts/clang/host/linux-x86/BUILD
-		// currently assume no subpackages (keepExistingBuildFile is not recursive for that directory).
-		//
-		// This is a bandaid until we the symlink forest logic can intelligently exclude BUILD files found in
-		// source symlink dirs according to the keepExistingBuildFile allowlist.
-		"prebuilts/clang/host/linux-x86/clang-dev",
 	)
 	return excluded
 }
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 6ead589..57c7ae8 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -310,7 +310,7 @@
 	// Nested class loader context shouldn't have conditional part (it is allowed only at the top level).
 	for ver, _ := range nestedClcMap {
 		if ver != AnySdkVersion {
-			clcPaths := ComputeClassLoaderContextDependencies(nestedClcMap)
+			_, clcPaths := ComputeClassLoaderContextDependencies(nestedClcMap)
 			return fmt.Errorf("nested class loader context shouldn't have conditional part: %+v", clcPaths)
 		}
 	}
@@ -553,27 +553,28 @@
 	return true, nil
 }
 
-// Returns a slice of build paths for all possible dependencies that the class loader context may
-// refer to.
+// Returns a slice of library names and a slice of build paths for all possible dependencies that
+// the class loader context may refer to.
 // Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
-func ComputeClassLoaderContextDependencies(clcMap ClassLoaderContextMap) android.Paths {
-	var paths android.Paths
+func ComputeClassLoaderContextDependencies(clcMap ClassLoaderContextMap) (names []string, paths android.Paths) {
 	for _, clcs := range clcMap {
-		hostPaths := ComputeClassLoaderContextDependenciesRec(clcs)
-		paths = append(paths, hostPaths...)
+		currentNames, currentPaths := ComputeClassLoaderContextDependenciesRec(clcs)
+		names = append(names, currentNames...)
+		paths = append(paths, currentPaths...)
 	}
-	return android.FirstUniquePaths(paths)
+	return android.FirstUniqueStrings(names), android.FirstUniquePaths(paths)
 }
 
 // Helper function for ComputeClassLoaderContextDependencies() that handles recursion.
-func ComputeClassLoaderContextDependenciesRec(clcs []*ClassLoaderContext) android.Paths {
-	var paths android.Paths
+func ComputeClassLoaderContextDependenciesRec(clcs []*ClassLoaderContext) (names []string, paths android.Paths) {
 	for _, clc := range clcs {
-		subPaths := ComputeClassLoaderContextDependenciesRec(clc.Subcontexts)
+		subNames, subPaths := ComputeClassLoaderContextDependenciesRec(clc.Subcontexts)
+		names = append(names, clc.Name)
 		paths = append(paths, clc.Host)
+		names = append(names, subNames...)
 		paths = append(paths, subPaths...)
 	}
-	return paths
+	return names, paths
 }
 
 // Class loader contexts that come from Make via JSON dexpreopt.config. JSON CLC representation is
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 39b4652..7260abb 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -97,10 +97,11 @@
 
 	fixClassLoaderContext(m)
 
-	var havePaths android.Paths
+	var actualNames []string
+	var actualPaths android.Paths
 	var haveUsesLibsReq, haveUsesLibsOpt []string
 	if valid && validationError == nil {
-		havePaths = ComputeClassLoaderContextDependencies(m)
+		actualNames, actualPaths = ComputeClassLoaderContextDependencies(m)
 		haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
 	}
 
@@ -112,19 +113,26 @@
 	})
 
 	// Test that all expected build paths are gathered.
-	t.Run("paths", func(t *testing.T) {
-		wantPaths := []string{
+	t.Run("names and paths", func(t *testing.T) {
+		expectedNames := []string{
+			"a'", "a1", "a2", "a3", "android.hidl.base-V1.0-java", "android.hidl.manager-V1.0-java", "b",
+			"b1", "b2", "b3", "c", "c2", "d", "f",
+		}
+		expectedPaths := []string{
 			"out/soong/android.hidl.manager-V1.0-java.jar", "out/soong/android.hidl.base-V1.0-java.jar",
 			"out/soong/a.jar", "out/soong/b.jar", "out/soong/c.jar", "out/soong/d.jar",
 			"out/soong/a2.jar", "out/soong/b2.jar", "out/soong/c2.jar",
 			"out/soong/a1.jar", "out/soong/b1.jar",
 			"out/soong/f.jar", "out/soong/a3.jar", "out/soong/b3.jar",
 		}
-		actual := havePaths.Strings()
+		actualPathsStrs := actualPaths.Strings()
 		// The order does not matter.
-		sort.Strings(wantPaths)
-		sort.Strings(actual)
-		android.AssertArrayString(t, "", wantPaths, actual)
+		sort.Strings(expectedNames)
+		sort.Strings(actualNames)
+		android.AssertArrayString(t, "", expectedNames, actualNames)
+		sort.Strings(expectedPaths)
+		sort.Strings(actualPathsStrs)
+		android.AssertArrayString(t, "", expectedPaths, actualPathsStrs)
 	})
 
 	// Test the JSON passed to construct_context.py.
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 20737e3..29ae188 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -353,7 +353,7 @@
 		}
 
 		// Generate command that saves host and target class loader context in shell variables.
-		paths := ComputeClassLoaderContextDependencies(module.ClassLoaderContexts)
+		_, paths := ComputeClassLoaderContextDependencies(module.ClassLoaderContexts)
 		rule.Command().
 			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`).
@@ -630,5 +630,3 @@
 	}
 	return false
 }
-
-var copyOf = android.CopyOf
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index b470304..ada4712 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -368,6 +368,9 @@
 	Triage_assignee string `json:"triage_assignee,omitempty"`
 	// Specifies libs used to initialize ART (java only, 'use_none' for no initialization)
 	Use_platform_libs UsePlatformLibs `json:"use_platform_libs,omitempty"`
+	// Specifies whether fuzz target should check presubmitted code changes for crashes.
+	// Defaults to false.
+	Use_for_presubmit *bool `json:"use_for_presubmit,omitempty"`
 }
 
 type FuzzFrameworks struct {
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 495bc19..c767685 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -48,7 +48,6 @@
 
 	SandboxingDenyModuleList = []string{
 		"RsBalls-rscript",
-		"CtsRsBlasTestCases-rscript",
 		"pvmfw_fdt_template_rs",
 		"RSTest_v14-rscript",
 		"com.android.apex.test.bar_stripped",
@@ -95,7 +94,6 @@
 		"com.android.apex.test.baz_stripped",
 		"com.android.apex.test.foo_stripped",
 		"com.android.apex.test.sharedlibs_generated",
-		"CtsRenderscriptTestCases-rscript",
 		"BlueberryFacadeAndCertGeneratedStub_py",
 		"BlueberryFacadeGeneratedStub_cc",
 		"BlueberryFacadeGeneratedStub_h",
diff --git a/java/app_test.go b/java/app_test.go
index cf7d174..0f98416 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2697,7 +2697,7 @@
 	cmd := app.Rule("dexpreopt").RuleParams.Command
 	android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=")
 	android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd,
-		"--product-packages=out/soong/target/product/test_device/product_packages.txt")
+		"--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/product_packages.txt")
 }
 
 func TestDexpreoptBcp(t *testing.T) {
diff --git a/java/base.go b/java/base.go
index 75e25e3..f2ad5c2 100644
--- a/java/base.go
+++ b/java/base.go
@@ -2202,7 +2202,7 @@
 
 func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	switch ctx.ModuleType() {
-	case "java_library", "java_library_host", "java_library_static":
+	case "java_library", "java_library_host", "java_library_static", "tradefed_java_library_host":
 		if lib, ok := ctx.Module().(*Library); ok {
 			javaLibraryBp2Build(ctx, lib)
 		}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 108fdd4..50429b0 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -479,8 +479,6 @@
 		for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
 			// Add a dependency onto a possibly scope specific stub library.
 			scopeSpecificDependency := apiScope.scopeSpecificStubModule(ctx, additionalStubModule)
-			// Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable
-			scopeSpecificDependency = android.JavaApiLibraryName(ctx.Config(), scopeSpecificDependency)
 			tag := hiddenAPIStubsDependencyTag{apiScope: apiScope, fromAdditionalDependency: true}
 			ctx.AddVariationDependencies(nil, tag, scopeSpecificDependency)
 		}
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 9bdef74..2541f14 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -432,39 +432,3 @@
 	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
 	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
 }
-
-func TestBootclassFragment_LinkTextStub(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		prepareForTestWithBootclasspathFragment,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("mysdklibrary"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetBuildFromTextStub(true)
-		}),
-	).RunTestWithBp(t, `
-        bootclasspath_fragment {
-            name: "myfragment",
-            contents: ["mysdklibrary"],
-            hidden_api: {split_packages: ["*"]},
-            additional_stubs: [
-                "android-non-updatable",
-            ],
-        }
-        java_sdk_library {
-            name: "mysdklibrary",
-            srcs: ["a.java"],
-            shared_library: false,
-            public: {enabled: true},
-            system: {enabled: true},
-        }
-    `)
-
-	fragment := result.ModuleForTests("myfragment", "android_common")
-	ruleCommand := fragment.Rule("modularHiddenAPIStubFlagsFile").RuleParams.Command
-	android.AssertStringDoesContain(t, "Command expected to contain library as dependency stub dex",
-		ruleCommand, "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.module_lib.from-text/android_common/dex/android-non-updatable.stubs.module_lib.from-text.jar")
-	android.AssertStringDoesNotContain(t,
-		"Command not expected to contain multiple api_library as dependency stub dex", ruleCommand,
-		"--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.from-text/android_common/dex/android-non-updatable.stubs.from-text.jar")
-}
diff --git a/java/config/makevars.go b/java/config/makevars.go
index d383d98..4e09195 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -29,10 +29,8 @@
 
 	// These are used by make when LOCAL_PRIVATE_PLATFORM_APIS is set (equivalent to platform_apis in blueprint):
 	ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES",
-		strings.Join(android.JavaApiLibraryNames(ctx.Config(), LegacyCorePlatformBootclasspathLibraries), " "))
-	ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES",
-		android.JavaApiLibraryName(ctx.Config(), LegacyCorePlatformSystemModules),
-	)
+		strings.Join(LegacyCorePlatformBootclasspathLibraries, " "))
+	ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES", LegacyCorePlatformSystemModules)
 
 	ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
 	ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index e588c9a..998730e 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -390,11 +391,37 @@
 
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 
-	// "product_packages.txt" is generated by `build/make/core/Makefile`.
+	// The root "product_packages.txt" is generated by `build/make/core/Makefile`. It contains a list
+	// of all packages that are installed on the device. We use `grep` to filter the list by the app's
+	// dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime
+	// from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns.
 	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
+	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", "product_packages.txt")
+	appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp")
+	clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts)
+	sort.Strings(clcNames) // The order needs to be deterministic.
+	productPackagesRule := android.NewRuleBuilder(pctx, ctx)
+	if len(clcNames) > 0 {
+		productPackagesRule.Command().
+			Text("grep -F -x").
+			FlagForEachArg("-e ", clcNames).
+			Input(productPackages).
+			FlagWithOutput("> ", appProductPackagesStaging).
+			Text("|| true")
+	} else {
+		productPackagesRule.Command().
+			Text("rm -f").Output(appProductPackagesStaging).
+			Text("&&").
+			Text("touch").Output(appProductPackagesStaging)
+	}
+	productPackagesRule.Command().
+		Text("rsync --checksum").
+		Input(appProductPackagesStaging).
+		Output(appProductPackages)
+	productPackagesRule.Restat().Build("product_packages", "dexpreopt product_packages")
 
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, dexpreoptConfig, productPackages)
+		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 9100e87..28f50d7 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -250,8 +250,6 @@
 
 var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
 
-var copyOf = android.CopyOf
-
 func init() {
 	android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars)
 }
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index da9c997..c6b921b 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -647,7 +647,7 @@
 	// public version is provided by the art.module.public.api module. In those cases it is necessary
 	// to treat all those modules as they were the same name, otherwise it will result in multiple
 	// definitions of a single class being passed to hidden API processing which will cause an error.
-	if name == scope.nonUpdatablePrebuiltModule || name == android.JavaApiLibraryName(ctx.Config(), scope.nonUpdatableSourceModule) {
+	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
 		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
 		// java_sdk_library.
 		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
diff --git a/java/java.go b/java/java.go
index 3e6b96b..caafaa2 100644
--- a/java/java.go
+++ b/java/java.go
@@ -459,7 +459,7 @@
 		ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
 		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag,
-				android.JavaApiLibraryNames(ctx.Config(), config.LegacyCorePlatformBootclasspathLibraries)...,
+				config.LegacyCorePlatformBootclasspathLibraries...,
 			)
 		}
 		if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 04c6d05..6cb549e 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -93,16 +93,16 @@
 
 func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
 	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
-		return android.JavaApiLibraryName(ctx.Config(), config.LegacyCorePlatformSystemModules)
+		return config.LegacyCorePlatformSystemModules
 	} else {
-		return android.JavaApiLibraryName(ctx.Config(), config.StableCorePlatformSystemModules)
+		return config.StableCorePlatformSystemModules
 	}
 }
 
 func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string {
 	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
-		return android.JavaApiLibraryNames(ctx.Config(), config.LegacyCorePlatformBootclasspathLibraries)
+		return config.LegacyCorePlatformBootclasspathLibraries
 	} else {
-		return android.JavaApiLibraryNames(ctx.Config(), config.StableCorePlatformBootclasspathLibraries)
+		return config.StableCorePlatformBootclasspathLibraries
 	}
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index 6bbe872..0041af4 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -299,7 +299,7 @@
 func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	instrumentedApp *AndroidApp) {
 
-	srcJarArgs := copyOf(instrumentedApp.srcJarArgs)
+	srcJarArgs := android.CopyOf(instrumentedApp.srcJarArgs)
 	srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...)
 
 	for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) {
diff --git a/java/sdk.go b/java/sdk.go
index 7699aab..7c702c4 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -148,7 +148,7 @@
 	toModule := func(module string, aidl android.Path) sdkDep {
 		// Select the kind of system modules needed for the sdk version.
 		systemModulesKind := systemModuleKind(sdkVersion.Kind, android.FutureApiLevel)
-		systemModules := android.JavaApiLibraryName(ctx.Config(), fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind))
+		systemModules := fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind)
 		return sdkDep{
 			useModule:          true,
 			bootclasspath:      []string{module, config.DefaultLambdaStubsLibrary},
@@ -198,7 +198,7 @@
 		return sdkDep{
 			useModule:        true,
 			bootclasspath:    []string{android.SdkCore.DefaultJavaLibraryName(), config.DefaultLambdaStubsLibrary},
-			systemModules:    android.JavaApiLibraryName(ctx.Config(), "core-public-stubs-system-modules"),
+			systemModules:    "core-public-stubs-system-modules",
 			noFrameworksLibs: true,
 		}
 	case android.SdkModule:
diff --git a/rust/builder.go b/rust/builder.go
index bf009a5..c31bc88 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -217,6 +217,22 @@
 		envVars = append(envVars, "OUT_DIR=out")
 	}
 
+	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx))
+
+	if ctx.RustModule().compiler.CargoEnvCompat() {
+		if bin, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
+			envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx))
+		}
+		envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName())
+		envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName())
+		pkgVersion := ctx.RustModule().compiler.CargoPkgVersion()
+		if pkgVersion != "" {
+			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
+		}
+	}
+
+	envVars = append(envVars, "AR=${cc_config.ClangBin}/llvm-ar")
+
 	return envVars
 }
 
@@ -317,22 +333,6 @@
 		implicits = append(implicits, outputs.Paths()...)
 	}
 
-	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx))
-
-	if ctx.RustModule().compiler.CargoEnvCompat() {
-		if _, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
-			envVars = append(envVars, "CARGO_BIN_NAME="+strings.TrimSuffix(outputFile.Base(), outputFile.Ext()))
-		}
-		envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName())
-		envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName())
-		pkgVersion := ctx.RustModule().compiler.CargoPkgVersion()
-		if pkgVersion != "" {
-			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
-		}
-	}
-
-	envVars = append(envVars, "AR=${cc_config.ClangBin}/llvm-ar")
-
 	if flags.Clippy {
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
 		ctx.Build(pctx, android.BuildParams{
diff --git a/tests/lib.sh b/tests/lib.sh
index b5dea99..4aaf272 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -144,6 +144,7 @@
   symlink_directory prebuilts/jdk
   symlink_directory external/bazel-skylib
   symlink_directory external/bazelbuild-rules_android
+  symlink_directory external/bazelbuild-rules_go
   symlink_directory external/bazelbuild-rules_license
   symlink_directory external/bazelbuild-kotlin-rules
 
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 43a9f0f..bec43ae 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -16,7 +16,8 @@
 # mock client.
 "$TOP/build/soong/tests/apex_comparison_tests.sh"
 "$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
-TEST_BAZEL=true extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+# TODO(b/289141798): uncomment the first dcla_apex_comparison_test.sh
+#TEST_BAZEL=true extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
 #BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 94fe51d..19987f2 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -23,194 +23,204 @@
   exit 1
 fi
 
-tmp_dir="$(mktemp -d tmp.XXXXXX)"
+function setup {
+  tmp_dir="$(mktemp -d tmp.XXXXXX)"
+  trap 'cleanup "${tmp_dir}"' EXIT
+  echo "${tmp_dir}"
+}
+
 function cleanup {
+  tmp_dir="$1"; shift
   rm -rf "${tmp_dir}"
 }
-trap cleanup EXIT
-
-out_dir=$tmp_dir
-droid_target=droid
-
-debug=false
-if [ $debug = "true" ]; then
-  out_dir=out
-  droid_target=
-fi
 
 function run_soong {
-  TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \
-    build/soong/soong_ui.bash --make-mode "$@"
+  target_product="$1";shift
+  out_dir="$1"; shift
+  targets="$1"; shift
+  if [ "$#" -ge 1 ]; then
+    apps=$1; shift
+    TARGET_PRODUCT="${target_product}" TARGET_BUILD_VARIANT=userdebug OUT_DIR="${out_dir}" TARGET_BUILD_UNBUNDLED=true TARGET_BUILD_APPS=$apps build/soong/soong_ui.bash --make-mode ${targets}
+  else
+    TARGET_PRODUCT="${target_product}" TARGET_BUILD_VARIANT=userdebug OUT_DIR="${out_dir}" build/soong/soong_ui.bash --make-mode ${targets}
+  fi
 }
 
-# m droid, build sbom later in case additional dependencies might be built and included in partition images.
-run_soong $droid_target dump.erofs lz4
-
-product_out=$out_dir/target/product/vsoc_x86_64
-sbom_test=$product_out/sbom_test
-mkdir $sbom_test
-cp $product_out/*.img $sbom_test
-
-# m sbom
-run_soong sbom
-
-# Generate installed file list from .img files in PRODUCT_OUT
-dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
-lz4=$out_dir/host/linux-x86/bin/lz4
-
-declare -A diff_excludes
-diff_excludes[odm]="-I /odm/lib/modules"
-diff_excludes[vendor]=\
-"-I /vendor/lib64/libkeystore2_crypto.so \
- -I /vendor/lib/modules \
- -I /vendor/odm"
-diff_excludes[system]=\
-"-I /bin \
- -I /bugreports \
- -I /cache \
- -I /d \
- -I /etc \
- -I /init \
- -I /odm/app \
- -I /odm/bin \
- -I /odm_dlkm/etc \
- -I /odm/etc \
- -I /odm/firmware \
- -I /odm/framework \
- -I /odm/lib \
- -I /odm/lib64 \
- -I /odm/overlay \
- -I /odm/priv-app \
- -I /odm/usr \
- -I /sdcard \
- -I /system/lib64/android.hardware.confirmationui@1.0.so \
- -I /system/lib64/android.hardware.confirmationui-V1-ndk.so \
- -I /system/lib64/android.hardware.keymaster@4.1.so \
- -I /system/lib64/android.hardware.security.rkp-V3-ndk.so \
- -I /system/lib64/android.hardware.security.sharedsecret-V1-ndk.so \
- -I /system/lib64/android.security.compat-ndk.so \
- -I /system/lib64/libkeymaster4_1support.so \
- -I /system/lib64/libkeymaster4support.so \
- -I /system/lib64/libkeymint.so \
- -I /system/lib64/libkeystore2_aaid.so \
- -I /system/lib64/libkeystore2_apc_compat.so \
- -I /system/lib64/libkeystore2_crypto.so \
- -I /system/lib64/libkeystore-attestation-application-id.so \
- -I /system/lib64/libkm_compat_service.so \
- -I /system/lib64/libkm_compat.so \
- -I /system/lib64/vndk-29 \
- -I /system/lib64/vndk-sp-29 \
- -I /system/lib/vndk-29 \
- -I /system/lib/vndk-sp-29 \
- -I /system/usr/icu \
- -I /vendor_dlkm/etc"
-
 function diff_files {
-   file_list_file="$1"; shift
-   files_in_spdx_file="$1"; shift
-   partition_name="$1"; shift
-   exclude=
-   if [ -v 'diff_excludes[$partition_name]' ]; then
-     exclude=${diff_excludes[$partition_name]}
-   fi
-
-   diff "$file_list_file" "$files_in_spdx_file" $exclude
-   if [ $? != "0" ]; then
-     echo Found diffs in $f and SBOM.
-     exit 1
-   else
-     echo No diffs.
-   fi
- }
-
-# Example output of dump.erofs is as below, and the data used in the test start
-# at line 11. Column 1 is inode id, column 2 is inode type and column 3 is name.
-# Each line is captured in variable "entry", awk is used to get type and name.
-# Output of dump.erofs:
-#     File : /
-#     Size: 160  On-disk size: 160  directory
-#     NID: 39   Links: 10   Layout: 2   Compression ratio: 100.00%
-#     Inode size: 64   Extent size: 0   Xattr size: 16
-#     Uid: 0   Gid: 0  Access: 0755/rwxr-xr-x
-#     Timestamp: 2023-02-14 01:15:54.000000000
-#
-#            NID TYPE  FILENAME
-#             39    2  .
-#             39    2  ..
-#             47    2  app
-#        1286748    2  bin
-#        1286754    2  etc
-#        5304814    2  lib
-#        5309056    2  lib64
-#        5309130    2  media
-#        5388910    2  overlay
-#        5479537    2  priv-app
-EROFS_IMAGES="\
-  $sbom_test/product.img \
-  $sbom_test/system.img \
-  $sbom_test/system_ext.img \
-  $sbom_test/system_dlkm.img \
-  $sbom_test/system_other.img \
-  $sbom_test/odm.img \
-  $sbom_test/odm_dlkm.img \
-  $sbom_test/vendor.img \
-  $sbom_test/vendor_dlkm.img"
-for f in $EROFS_IMAGES; do
-  partition_name=$(basename $f | cut -d. -f1)
-  file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
-  files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
-  rm "$file_list_file" > /dev/null 2>&1
-  all_dirs="/"
-  while [ ! -z "$all_dirs" ]; do
-    dir=$(echo "$all_dirs" | cut -d ' ' -f1)
-    all_dirs=$(echo "$all_dirs" | cut -d ' ' -f1 --complement -s)
-    entries=$($dump_erofs --ls --path "$dir" $f | tail -n +11)
-    while read -r entry; do
-      inode_type=$(echo $entry | awk -F ' ' '{print $2}')
-      name=$(echo $entry | awk -F ' ' '{print $3}')
-      case $inode_type in
-        "2")  # directory
-          all_dirs=$(echo "$all_dirs $dir/$name" | sed 's/^\s*//')
-          ;;
-        "1"|"7")  # 1: file, 7: symlink
-          (
-          if [ "$partition_name" != "system" ]; then
-            # system partition is mounted to /, not to prepend partition name.
-            printf %s "/$partition_name"
-          fi
-          echo "$dir/$name" | sed 's#^//#/#'
-          ) >> "$file_list_file"
-          ;;
-      esac
-    done <<< "$entries"
-  done
-  sort -n -o "$file_list_file" "$file_list_file"
-
-  grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file"
-  if [ "$partition_name" = "system" ]; then
-    # system partition is mounted to /, so include FileName starts with /root/ too.
-    grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file"
+  file_list_file="$1"; shift
+  files_in_spdx_file="$1"; shift
+  partition_name="$1"; shift
+  exclude=
+  if [ -v 'diff_excludes[$partition_name]' ]; then
+   exclude=${diff_excludes[$partition_name]}
   fi
-  sort -n -o "$files_in_spdx_file" "$files_in_spdx_file"
 
-  echo ============ Diffing files in $f and SBOM
-  diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
-done
+  diff "$file_list_file" "$files_in_spdx_file" $exclude
+  if [ $? != "0" ]; then
+   echo Found diffs in $f and SBOM.
+   exit 1
+  else
+   echo No diffs.
+  fi
+}
 
-RAMDISK_IMAGES="$product_out/ramdisk.img"
-for f in $RAMDISK_IMAGES; do
-  partition_name=$(basename $f | cut -d. -f1)
-  file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
-  files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
-  # lz4 decompress $f to stdout
-  # cpio list all entries like ls -l
-  # grep filter normal files and symlinks
-  # awk get entry names
-  # sed remove partition name from entry names
-  $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file"
+function test_sbom_aosp_cf_x86_64_phone {
+  # Setup
+  out_dir="$(setup)"
 
-  grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file"
+  # Test
+  # m droid, build sbom later in case additional dependencies might be built and included in partition images.
+  run_soong "aosp_cf_x86_64_phone" "${out_dir}" "droid dump.erofs lz4"
 
-  echo ============ Diffing files in $f and SBOM
-  diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
-done
\ No newline at end of file
+  product_out=$out_dir/target/product/vsoc_x86_64
+  sbom_test=$product_out/sbom_test
+  mkdir -p $sbom_test
+  cp $product_out/*.img $sbom_test
+
+  # m sbom
+  run_soong "aosp_cf_x86_64_phone" "${out_dir}" sbom
+
+  # Generate installed file list from .img files in PRODUCT_OUT
+  dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
+  lz4=$out_dir/host/linux-x86/bin/lz4
+
+  declare -A diff_excludes
+  diff_excludes[vendor]="-I /vendor/lib64/libkeystore2_crypto.so"
+  diff_excludes[system]="\
+    -I /bin \
+    -I /bugreports \
+    -I /cache \
+    -I /d \
+    -I /etc \
+    -I /init \
+    -I /odm/app \
+    -I /odm/bin \
+    -I /odm_dlkm/etc \
+    -I /odm/etc \
+    -I /odm/firmware \
+    -I /odm/framework \
+    -I /odm/lib \
+    -I /odm/lib64 \
+    -I /odm/overlay \
+    -I /odm/priv-app \
+    -I /odm/usr \
+    -I /sdcard \
+    -I /system/lib64/android.hardware.confirmationui@1.0.so \
+    -I /system/lib64/android.hardware.confirmationui-V1-ndk.so \
+    -I /system/lib64/android.hardware.keymaster@4.1.so \
+    -I /system/lib64/android.hardware.security.rkp-V3-ndk.so \
+    -I /system/lib64/android.hardware.security.sharedsecret-V1-ndk.so \
+    -I /system/lib64/android.security.compat-ndk.so \
+    -I /system/lib64/libkeymaster4_1support.so \
+    -I /system/lib64/libkeymaster4support.so \
+    -I /system/lib64/libkeymint.so \
+    -I /system/lib64/libkeystore2_aaid.so \
+    -I /system/lib64/libkeystore2_apc_compat.so \
+    -I /system/lib64/libkeystore2_crypto.so \
+    -I /system/lib64/libkeystore-attestation-application-id.so \
+    -I /system/lib64/libkm_compat_service.so \
+    -I /system/lib64/libkm_compat.so \
+    -I /system/lib64/vndk-29 \
+    -I /system/lib64/vndk-sp-29 \
+    -I /system/lib/vndk-29 \
+    -I /system/lib/vndk-sp-29 \
+    -I /system/usr/icu \
+    -I /vendor_dlkm/etc"
+
+  # Example output of dump.erofs is as below, and the data used in the test start
+  # at line 11. Column 1 is inode id, column 2 is inode type and column 3 is name.
+  # Each line is captured in variable "entry", awk is used to get type and name.
+  # Output of dump.erofs:
+  #     File : /
+  #     Size: 160  On-disk size: 160  directory
+  #     NID: 39   Links: 10   Layout: 2   Compression ratio: 100.00%
+  #     Inode size: 64   Extent size: 0   Xattr size: 16
+  #     Uid: 0   Gid: 0  Access: 0755/rwxr-xr-x
+  #     Timestamp: 2023-02-14 01:15:54.000000000
+  #
+  #            NID TYPE  FILENAME
+  #             39    2  .
+  #             39    2  ..
+  #             47    2  app
+  #        1286748    2  bin
+  #        1286754    2  etc
+  #        5304814    2  lib
+  #        5309056    2  lib64
+  #        5309130    2  media
+  #        5388910    2  overlay
+  #        5479537    2  priv-app
+  EROFS_IMAGES="\
+    $sbom_test/product.img \
+    $sbom_test/system.img \
+    $sbom_test/system_ext.img \
+    $sbom_test/system_dlkm.img \
+    $sbom_test/system_other.img \
+    $sbom_test/odm.img \
+    $sbom_test/odm_dlkm.img \
+    $sbom_test/vendor.img \
+    $sbom_test/vendor_dlkm.img"
+  for f in $EROFS_IMAGES; do
+    partition_name=$(basename $f | cut -d. -f1)
+    file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
+    files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+    rm "$file_list_file" > /dev/null 2>&1 || true
+    all_dirs="/"
+    while [ ! -z "$all_dirs" ]; do
+      dir=$(echo "$all_dirs" | cut -d ' ' -f1)
+      all_dirs=$(echo "$all_dirs" | cut -d ' ' -f1 --complement -s)
+      entries=$($dump_erofs --ls --path "$dir" $f | tail -n +11)
+      while read -r entry; do
+        inode_type=$(echo $entry | awk -F ' ' '{print $2}')
+        name=$(echo $entry | awk -F ' ' '{print $3}')
+        case $inode_type in
+          "2")  # directory
+            all_dirs=$(echo "$all_dirs $dir/$name" | sed 's/^\s*//')
+            ;;
+          "1"|"7")  # 1: file, 7: symlink
+            (
+            if [ "$partition_name" != "system" ]; then
+              # system partition is mounted to /, not to prepend partition name.
+              printf %s "/$partition_name"
+            fi
+            echo "$dir/$name" | sed 's#^//#/#'
+            ) >> "$file_list_file"
+            ;;
+        esac
+      done <<< "$entries"
+    done
+    sort -n -o "$file_list_file" "$file_list_file"
+
+    grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file"
+    if [ "$partition_name" = "system" ]; then
+      # system partition is mounted to /, so include FileName starts with /root/ too.
+      grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file"
+    fi
+    sort -n -o "$files_in_spdx_file" "$files_in_spdx_file"
+
+    echo ============ Diffing files in $f and SBOM
+    diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
+  done
+
+  RAMDISK_IMAGES="$product_out/ramdisk.img"
+  for f in $RAMDISK_IMAGES; do
+    partition_name=$(basename $f | cut -d. -f1)
+    file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
+    files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+    # lz4 decompress $f to stdout
+    # cpio list all entries like ls -l
+    # grep filter normal files and symlinks
+    # awk get entry names
+    # sed remove partition name from entry names
+    $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file"
+
+    grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file"
+
+    echo ============ Diffing files in $f and SBOM
+    diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
+  done
+
+  # Teardown
+  cleanup "${out_dir}"
+}
+
+test_sbom_aosp_cf_x86_64_phone
\ No newline at end of file
diff --git a/ui/build/config.go b/ui/build/config.go
index 0259009..e642772 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1375,6 +1375,12 @@
 }
 
 func (c *configImpl) UseRBE() bool {
+	authType, _ := c.rbeAuth()
+	// Do not use RBE with prod credentials in scenarios when stubby doesn't exist, since
+	// its unlikely that we will be able to obtain necessary creds without stubby.
+	if !c.StubbyExists() && strings.Contains(authType, "use_google_prod_creds"){
+		return false
+	}
 	if v, ok := c.Environment().Get("USE_RBE"); ok {
 		v = strings.TrimSpace(v)
 		if v != "" && v != "false" {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 07d6188..a4cf7fb 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -532,13 +532,15 @@
 	runMicrofactory(ctx, config, "bpglob", "github.com/google/blueprint/bootstrap/bpglob",
 		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
-	ninja := func(name, ninjaFile string, targets ...string) {
-		ctx.BeginTrace(metrics.RunSoong, name)
+	ninja := func(targets ...string) {
+		ctx.BeginTrace(metrics.RunSoong, "bootstrap")
 		defer ctx.EndTrace()
 
 		if config.IsPersistentBazelEnabled() {
 			bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace"), config.GetBazeliskBazelVersion())
-			bazelProxy.Start()
+			if err := bazelProxy.Start(); err != nil {
+				ctx.Fatalf("Failed to create bazel proxy")
+			}
 			defer bazelProxy.Close()
 		}
 
@@ -556,7 +558,7 @@
 			"-w", "missingoutfile=err",
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
-			"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
+			"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
 		}
 
 		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
@@ -565,7 +567,7 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
-		cmd := Command(ctx, config, "soong "+name,
+		cmd := Command(ctx, config, "soong bootstrap",
 			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
 
 		var ninjaEnv Environment
@@ -606,7 +608,7 @@
 		targets = append(targets, config.SoongNinjaFile())
 	}
 
-	ninja("bootstrap", "bootstrap.ninja", targets...)
+	ninja(targets...)
 
 	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
 	distFile(ctx, config, config.SoongVarsFile(), "soong")
diff --git a/ui/status/kati.go b/ui/status/kati.go
index 1485c8d..dbb0ce3 100644
--- a/ui/status/kati.go
+++ b/ui/status/kati.go
@@ -24,7 +24,7 @@
 )
 
 var katiError = regexp.MustCompile(`^(\033\[1m)?[^ ]+:[0-9]+: (\033\[31m)?error:`)
-var katiIncludeRe = regexp.MustCompile(`^(\[(\d+)/(\d+)] )?((including [^ ]+|initializing (build|packaging) system|finishing (build|packaging) rules|writing (build|packaging) rules) ...)$`)
+var katiIncludeRe = regexp.MustCompile(`^(\[(\d+)/(\d+)] )?((including [^ ]+|initializing (legacy Make module parser|packaging system)|finishing (legacy Make module parsing|packaging rules)|writing (legacy Make module|packaging) rules) ...)$`)
 var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
 var katiNinjaMissing = regexp.MustCompile("^[^ ]+ is missing, regenerating...$")
 
diff --git a/ui/status/kati_test.go b/ui/status/kati_test.go
index f2cb813..fd1b4b5 100644
--- a/ui/status/kati_test.go
+++ b/ui/status/kati_test.go
@@ -65,7 +65,7 @@
 	parser.parseLine("out/build-aosp_arm.ninja is missing, regenerating...")
 	output.Expect(t, Counts{})
 
-	parser.parseLine("[1/1] initializing build system ...")
+	parser.parseLine("[1/1] initializing legacy Make module parser ...")
 	output.Expect(t, Counts{
 		TotalActions:    1,
 		RunningActions:  1,
@@ -86,14 +86,14 @@
 	parser.parseLine(msg)
 
 	// Start the next line to flush the previous result
-	parser.parseLine("[4/5] finishing build rules ...")
+	parser.parseLine("[4/5] finishing legacy Make module parsing ...")
 
 	msg += "\n"
 	if output.result.Output != msg {
 		t.Errorf("output for action did not match:\nwant: %q\n got: %q\n", msg, output.result.Output)
 	}
 
-	parser.parseLine("[5/5] writing build rules ...")
+	parser.parseLine("[5/5] writing legacy Make module rules ...")
 	parser.parseLine("*kati*: verbose msg")
 	parser.flushAction()
 
@@ -118,7 +118,7 @@
 		st: status.StartTool(),
 	}
 
-	parser.parseLine("[1/1] initializing build system ...")
+	parser.parseLine("[1/1] initializing legacy Make module parser ...")
 	parser.parseLine("[2/5] including out/soong/Android-aosp_arm.mk ...")
 	output.Expect(t, Counts{
 		TotalActions:    5,
@@ -145,7 +145,7 @@
 		FinishedActions: 3,
 	})
 
-	parser.parseLine("[3/5] finishing build rules ...")
+	parser.parseLine("[3/5] finishing legacy Make module parsing ...")
 
 	output.Expect(t, Counts{
 		TotalActions:    7,
@@ -164,7 +164,7 @@
 		st: status.StartTool(),
 	}
 
-	parser.parseLine("[1/1] initializing build system ...")
+	parser.parseLine("[1/1] initializing legacy Make module parser ...")
 	parser.parseLine("[2/5] inclduing out/soong/Android-aosp_arm.mk ...")
 	parser.parseLine("build/make/tools/Android.mk:19: error: testing")
 	parser.flushAction()
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
index 06a4064..3880b04 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -53,6 +53,13 @@
 	done            chan bool
 	sigwinch        chan os.Signal
 	sigwinchHandled chan bool
+
+	// Once there is a failure, we stop printing command output so the error
+	// is easier to find
+	haveFailures bool
+	// If we are dropping errors, then at the end, we report a message to go
+	// look in the verbose log if you want that command output.
+	postFailureActionCount int
 }
 
 // NewSmartStatusOutput returns a StatusOutput that represents the
@@ -165,12 +172,20 @@
 		}
 	}
 
+	s.statusLine(progress)
+
+	// Stop printing when there are failures, but don't skip actions that also have their own errors.
 	if output != "" {
-		s.statusLine(progress)
-		s.requestLine()
-		s.print(output)
-	} else {
-		s.statusLine(progress)
+		if !s.haveFailures || result.Error != nil {
+			s.requestLine()
+			s.print(output)
+		} else {
+			s.postFailureActionCount++
+		}
+	}
+
+	if result.Error != nil {
+		s.haveFailures = true
 	}
 }
 
@@ -187,6 +202,15 @@
 
 	s.stopSigwinch()
 
+	if s.postFailureActionCount > 0 {
+		s.requestLine()
+		if s.postFailureActionCount == 1 {
+			s.print(fmt.Sprintf("There was 1 action that completed after the action that failed. See verbose.log.gz for its output."))
+		} else {
+			s.print(fmt.Sprintf("There were %d actions that completed after the action that failed. See verbose.log.gz for their output.", s.postFailureActionCount))
+		}
+	}
+
 	s.requestLine()
 
 	s.runningActions = nil
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index b9057d2..8dd1809 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -295,3 +295,159 @@
 		t.Errorf("want:\n%q\ngot:\n%q", w, g)
 	}
 }
+
+func TestSmartStatusDoesntHideAfterSucecss(t *testing.T) {
+	os.Setenv(tableHeightEnVar, "")
+
+	smart := &fakeSmartTerminal{termWidth: 40}
+	stat := NewStatusOutput(smart, "", false, false, false)
+	smartStat := stat.(*smartStatusOutput)
+	smartStat.sigwinchHandled = make(chan bool)
+
+	runner := newRunner(stat, 2)
+
+	action1 := &status.Action{Description: "action1"}
+	result1 := status.ActionResult{
+		Action: action1,
+		Output: "Output1",
+	}
+
+	action2 := &status.Action{Description: "action2"}
+	result2 := status.ActionResult{
+		Action: action2,
+		Output: "Output2",
+	}
+
+	runner.startAction(action1)
+	runner.startAction(action2)
+	runner.finishAction(result1)
+	runner.finishAction(result2)
+
+	stat.Flush()
+
+	w := "\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\nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nOutput2\n"
+
+	if g := smart.String(); g != w {
+		t.Errorf("want:\n%q\ngot:\n%q", w, g)
+	}
+}
+
+func TestSmartStatusHideAfterFailure(t *testing.T) {
+	os.Setenv(tableHeightEnVar, "")
+
+	smart := &fakeSmartTerminal{termWidth: 40}
+	stat := NewStatusOutput(smart, "", false, false, false)
+	smartStat := stat.(*smartStatusOutput)
+	smartStat.sigwinchHandled = make(chan bool)
+
+	runner := newRunner(stat, 2)
+
+	action1 := &status.Action{Description: "action1"}
+	result1 := status.ActionResult{
+		Action: action1,
+		Output: "Output1",
+		Error:  fmt.Errorf("Error1"),
+	}
+
+	action2 := &status.Action{Description: "action2"}
+	result2 := status.ActionResult{
+		Action: action2,
+		Output: "Output2",
+	}
+
+	runner.startAction(action1)
+	runner.startAction(action2)
+	runner.finishAction(result1)
+	runner.finishAction(result2)
+
+	stat.Flush()
+
+	w := "\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\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"
+
+	if g := smart.String(); g != w {
+		t.Errorf("want:\n%q\ngot:\n%q", w, g)
+	}
+}
+
+func TestSmartStatusHideAfterFailurePlural(t *testing.T) {
+	os.Setenv(tableHeightEnVar, "")
+
+	smart := &fakeSmartTerminal{termWidth: 40}
+	stat := NewStatusOutput(smart, "", false, false, false)
+	smartStat := stat.(*smartStatusOutput)
+	smartStat.sigwinchHandled = make(chan bool)
+
+	runner := newRunner(stat, 2)
+
+	action1 := &status.Action{Description: "action1"}
+	result1 := status.ActionResult{
+		Action: action1,
+		Output: "Output1",
+		Error:  fmt.Errorf("Error1"),
+	}
+
+	action2 := &status.Action{Description: "action2"}
+	result2 := status.ActionResult{
+		Action: action2,
+		Output: "Output2",
+	}
+
+	action3 := &status.Action{Description: "action3"}
+	result3 := status.ActionResult{
+		Action: action3,
+		Output: "Output3",
+	}
+
+	runner.startAction(action1)
+	runner.startAction(action2)
+	runner.startAction(action3)
+	runner.finishAction(result1)
+	runner.finishAction(result2)
+	runner.finishAction(result3)
+
+	stat.Flush()
+
+	w := "\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[  0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"
+
+	if g := smart.String(); g != w {
+		t.Errorf("want:\n%q\ngot:\n%q", w, g)
+	}
+}
+
+func TestSmartStatusDontHideErrorAfterFailure(t *testing.T) {
+	os.Setenv(tableHeightEnVar, "")
+
+	smart := &fakeSmartTerminal{termWidth: 40}
+	stat := NewStatusOutput(smart, "", false, false, false)
+	smartStat := stat.(*smartStatusOutput)
+	smartStat.sigwinchHandled = make(chan bool)
+
+	runner := newRunner(stat, 2)
+
+	action1 := &status.Action{Description: "action1"}
+	result1 := status.ActionResult{
+		Action: action1,
+		Output: "Output1",
+		Error:  fmt.Errorf("Error1"),
+	}
+
+	action2 := &status.Action{Description: "action2"}
+	result2 := status.ActionResult{
+		Action: action2,
+		Output: "Output2",
+		Error:  fmt.Errorf("Error1"),
+	}
+
+	runner.startAction(action1)
+	runner.startAction(action2)
+	runner.finishAction(result1)
+	runner.finishAction(result2)
+
+	stat.Flush()
+
+	w := "\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\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nFAILED: \nOutput2\n"
+
+	if g := smart.String(); g != w {
+		t.Errorf("want:\n%q\ngot:\n%q", w, g)
+	}
+}