Merge "Add flag to skip writing output from soong_zip." into main
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/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
index 5985103..7449d67 100644
--- a/aidl_library/aidl_library.go
+++ b/aidl_library/aidl_library.go
@@ -108,17 +108,17 @@
 	// The direct aidl files of the module
 	Srcs android.Paths
 	// The include dirs to the direct aidl files and those provided from transitive aidl_library deps
-	IncludeDirs android.DepSet
+	IncludeDirs android.DepSet[android.Path]
 	// The direct hdrs and hdrs from transitive deps
-	Hdrs android.DepSet
+	Hdrs android.DepSet[android.Path]
 }
 
 // AidlLibraryProvider provides the srcs and the transitive include dirs
 var AidlLibraryProvider = blueprint.NewProvider(AidlLibraryInfo{})
 
 func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	includeDirsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
-	hdrsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
+	includeDirsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER)
+	hdrsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER)
 
 	if len(lib.properties.Srcs) == 0 && len(lib.properties.Hdrs) == 0 {
 		ctx.ModuleErrorf("at least srcs or hdrs prop must be non-empty")
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
index d9dd245..0205629 100644
--- a/aidl_library/aidl_library_test.go
+++ b/aidl_library/aidl_library_test.go
@@ -52,7 +52,7 @@
 		t,
 		"aidl include dirs",
 		[]string{"package_foo/a", "package_bar/x"},
-		actualInfo.IncludeDirs.ToList().Strings(),
+		android.Paths(actualInfo.IncludeDirs.ToList()).Strings(),
 	)
 
 	android.AssertPathsRelativeToTopEquals(
@@ -101,7 +101,7 @@
 		t,
 		"aidl include dirs",
 		[]string{"package_foo", "package_bar"},
-		actualInfo.IncludeDirs.ToList().Strings(),
+		android.Paths(actualInfo.IncludeDirs.ToList()).Strings(),
 	)
 
 	android.AssertPathsRelativeToTopEquals(
diff --git a/android/Android.bp b/android/Android.bp
index 2ccccf0..fc5198f 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -49,7 +49,6 @@
         "defaults.go",
         "defs.go",
         "depset_generic.go",
-        "depset_paths.go",
         "deptag.go",
         "expand.go",
         "filegroup.go",
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/arch.go b/android/arch.go
index 4b4691b..152016c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1884,10 +1884,10 @@
 		buildTargets = filterMultilibTargets(targets, "lib64")
 		// Reverse the targets so that the first architecture can depend on the second
 		// architecture module in order to merge the outputs.
-		reverseSliceInPlace(buildTargets)
+		ReverseSliceInPlace(buildTargets)
 	case "darwin_universal_common_first":
 		archTargets := filterMultilibTargets(targets, "lib64")
-		reverseSliceInPlace(archTargets)
+		ReverseSliceInPlace(archTargets)
 		buildTargets = append(getCommonTargets(targets), archTargets...)
 	default:
 		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index f4b368b..5d93f06 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -92,10 +92,11 @@
 type ApexConfigKey struct {
 	WithinApex     bool
 	ApexSdkVersion string
+	ApiDomain      string
 }
 
 func (c ApexConfigKey) String() string {
-	return fmt.Sprintf("%s_%s", withinApexToString(c.WithinApex), c.ApexSdkVersion)
+	return fmt.Sprintf("%s_%s_%s", withinApexToString(c.WithinApex), c.ApexSdkVersion, c.ApiDomain)
 }
 
 func withinApexToString(withinApex bool) string {
@@ -175,8 +176,6 @@
 	// (for example, that it is MixedBuildBuildable).
 	IsModuleNameAllowed(moduleName string, withinApex bool) bool
 
-	IsModuleDclaAllowed(moduleName string) bool
-
 	// Returns the bazel output base (the root directory for all bazel intermediate outputs).
 	OutputBase() string
 
@@ -315,10 +314,6 @@
 	return true
 }
 
-func (m MockBazelContext) IsModuleDclaAllowed(_ string) bool {
-	return true
-}
-
 func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
 
 func (m MockBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
@@ -460,10 +455,6 @@
 	return false
 }
 
-func (n noopBazelContext) IsModuleDclaAllowed(_ string) bool {
-	return false
-}
-
 func (m noopBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
 	return []*bazel.BuildStatement{}
 }
@@ -597,17 +588,13 @@
 	if context.bazelEnabledModules[moduleName] {
 		return true
 	}
-	if withinApex && context.IsModuleDclaAllowed(moduleName) {
+	if withinApex && context.bazelDclaEnabledModules[moduleName] {
 		return true
 	}
 
 	return false
 }
 
-func (context *mixedBuildBazelContext) IsModuleDclaAllowed(moduleName string) bool {
-	return context.bazelDclaEnabledModules[moduleName]
-}
-
 func pwdPrefix() string {
 	// Darwin doesn't have /proc
 	if runtime.GOOS != "darwin" {
@@ -737,6 +724,7 @@
         "@//build/bazel/rules/apex:within_apex": attr.within_apex,
         "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version,
         "@//build/bazel/rules/apex:apex_name": apex_name,
+        "@//build/bazel/rules/apex:api_domain": attr.api_domain,
     }
 
     return outputs
@@ -749,6 +737,7 @@
         "@//build/bazel/rules/apex:within_apex",
         "@//build/bazel/rules/apex:min_sdk_version",
         "@//build/bazel/rules/apex:apex_name",
+        "@//build/bazel/rules/apex:api_domain",
     ],
 )
 
@@ -762,6 +751,7 @@
         "os"      : attr.string(mandatory = True),
         "within_apex" : attr.bool(default = False),
         "apex_sdk_version" : attr.string(mandatory = True),
+        "api_domain" : attr.string(mandatory = True),
         "deps"    : attr.label_list(cfg = _config_node_transition, allow_files = True),
         "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
     },
@@ -823,6 +813,7 @@
     os = "%s",
     within_apex = %s,
     apex_sdk_version = "%s",
+    api_domain = "%s",
     deps = [%s],
     testonly = True, # Unblocks testonly deps.
 )
@@ -856,6 +847,11 @@
 		osString := configTokens[1]
 		withinApex := "False"
 		apexSdkVerString := ""
+		apiDomainString := ""
+		if osString == "android" {
+			// api domains are meaningful only for device variants
+			apiDomainString = "system"
+		}
 		targetString := fmt.Sprintf("%s_%s", osString, archString)
 		if len(configTokens) > 2 {
 			targetString += "_" + configTokens[2]
@@ -867,9 +863,13 @@
 			targetString += "_" + configTokens[3]
 			apexSdkVerString = configTokens[3]
 		}
+		if len(configTokens) > 4 {
+			apiDomainString = configTokens[4]
+			targetString += "_" + apiDomainString
+		}
 		allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
 		labelsString := strings.Join(labels, ",\n            ")
-		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, withinApex, apexSdkVerString,
+		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, withinApex, apexSdkVerString, apiDomainString,
 			labelsString)
 	}
 
@@ -974,11 +974,14 @@
 
   within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex")
   apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version")
+  api_domain = buildoptions.get("//build/bazel/rules/apex:api_domain")
 
   if within_apex:
     config_key += "|within_apex"
   if apex_sdk_version != None and len(apex_sdk_version) > 0:
     config_key += "|" + apex_sdk_version
+  if api_domain != None and len(api_domain) > 0:
+    config_key += "|" + api_domain
 
   return config_key
 
@@ -1267,6 +1270,12 @@
 		// because this would cause circular dependency. So, until we move aquery processing
 		// to the 'android' package, we need to handle special cases here.
 		switch buildStatement.Mnemonic {
+		case "RepoMappingManifest":
+			// It appears RepoMappingManifest files currently have
+			// non-deterministic content. Just emit empty files for
+			// now because they're unused.
+			out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
+			WriteFileRuleVerbatim(ctx, out, "")
 		case "FileWrite", "SourceSymlinkManifest":
 			out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
 			WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents)
@@ -1385,6 +1394,10 @@
 		keyString += "|" + key.configKey.apexKey.ApexSdkVersion
 	}
 
+	if len(key.configKey.apexKey.ApiDomain) > 0 {
+		keyString += "|" + key.configKey.apexKey.ApiDomain
+	}
+
 	return keyString
 }
 
@@ -1403,6 +1416,7 @@
 		configKey.apexKey = ApexConfigKey{
 			WithinApex:     apexKey.WithinApex,
 			ApexSdkVersion: apexKey.ApexSdkVersion,
+			ApiDomain:      apexKey.ApiDomain,
 		}
 	}
 
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index a81a878..65cd5a8 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -58,11 +58,12 @@
 	apexKey := ApexConfigKey{
 		WithinApex:     true,
 		ApexSdkVersion: "29",
+		ApiDomain:      "myapex",
 	}
 	cfg_foo := configKey{"arm64_armv8-a", Android, apexKey}
 	cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android}
 	cmd_results := []string{
-		`@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`,
+		`@//foo:foo|arm64_armv8-a|android|within_apex|29|myapex>>out/foo/foo.txt`,
 		`@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
 	}
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{cqueryCmd: strings.Join(cmd_results, "\n")})
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 872e908..2f5ff64 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -198,7 +198,7 @@
 	}
 	labels := expandSrcsForBazel(ctx, paths, excluded)
 	labels.Excludes = excludeLabels.Includes
-	labels = transformSubpackagePaths(ctx, labels)
+	labels = TransformSubpackagePaths(ctx.Config(), ctx.ModuleDir(), labels)
 	return labels
 }
 
@@ -237,7 +237,7 @@
 // if the "async_safe" directory is actually a package and not just a directory.
 //
 // In particular, paths that extend into packages are transformed into absolute labels beginning with //.
-func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label {
+func transformSubpackagePath(cfg Config, dir string, path bazel.Label) bazel.Label {
 	var newPath bazel.Label
 
 	// Don't transform OriginalModuleName
@@ -281,7 +281,7 @@
 	for i := len(pathComponents) - 1; i >= 0; i-- {
 		pathComponent := pathComponents[i]
 		var sep string
-		if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) {
+		if !foundPackageBoundary && isPackageBoundary(cfg, dir, pathComponents, i) {
 			sep = ":"
 			foundPackageBoundary = true
 		} else {
@@ -295,7 +295,7 @@
 	}
 	if foundPackageBoundary {
 		// Ensure paths end up looking like //bionic/... instead of //./bionic/...
-		moduleDir := ctx.ModuleDir()
+		moduleDir := dir
 		if strings.HasPrefix(moduleDir, ".") {
 			moduleDir = moduleDir[1:]
 		}
@@ -313,13 +313,13 @@
 
 // Transform paths to acknowledge package boundaries
 // See transformSubpackagePath() for more information
-func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList {
+func TransformSubpackagePaths(cfg Config, dir string, paths bazel.LabelList) bazel.LabelList {
 	var newPaths bazel.LabelList
 	for _, include := range paths.Includes {
-		newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include))
+		newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(cfg, dir, include))
 	}
 	for _, exclude := range paths.Excludes {
-		newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude))
+		newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(cfg, dir, exclude))
 	}
 	return newPaths
 }
diff --git a/android/bazel_paths_test.go b/android/bazel_paths_test.go
index 450bf76..60c0a14 100644
--- a/android/bazel_paths_test.go
+++ b/android/bazel_paths_test.go
@@ -175,7 +175,7 @@
 		"./z/b.c": "z/b.c",
 	}
 	for in, out := range pairs {
-		actual := transformSubpackagePath(ctx, bazel.Label{Label: in}).Label
+		actual := transformSubpackagePath(ctx.Config(), ctx.ModuleDir(), bazel.Label{Label: in}).Label
 		if actual != out {
 			t.Errorf("expected:\n%v\nactual:\n%v", out, actual)
 		}
diff --git a/android/config.go b/android/config.go
index 8c5f6e3..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
 		}
@@ -1645,10 +1649,6 @@
 	return Bool(c.productVariables.Aml_abis)
 }
 
-func (c *config) FlattenApex() bool {
-	return Bool(c.productVariables.Flatten_apex)
-}
-
 func (c *config) ForceApexSymlinkOptimization() bool {
 	return Bool(c.productVariables.ForceApexSymlinkOptimization)
 }
diff --git a/android/configured_jars.go b/android/configured_jars.go
index 53fef05..1281bae 100644
--- a/android/configured_jars.go
+++ b/android/configured_jars.go
@@ -311,4 +311,9 @@
 	return ConfiguredJarList{}
 }
 
+// IsConfiguredJarForPlatform returns true if the given apex name is a special name for the platform.
+func IsConfiguredJarForPlatform(apex string) bool {
+	return apex == "platform" || apex == "system_ext"
+}
+
 var earlyBootJarsKey = NewOnceKey("earlyBootJars")
diff --git a/android/depset_generic.go b/android/depset_generic.go
index f00e462..45c1937 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -16,10 +16,9 @@
 
 import (
 	"fmt"
-	"reflect"
 )
 
-// depSet is designed to be conceptually compatible with Bazel's depsets:
+// DepSet is designed to be conceptually compatible with Bazel's depsets:
 // https://docs.bazel.build/versions/master/skylark/depsets.html
 
 type DepSetOrder int
@@ -43,142 +42,114 @@
 	}
 }
 
-// A depSet efficiently stores a slice of an arbitrary type from transitive dependencies without
-// copying. It is stored as a DAG of depSet nodes, each of which has some direct contents and a list
-// of dependency depSet nodes.
+type depSettableType comparable
+
+// A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies without
+// copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list
+// of dependency DepSet nodes.
 //
-// A depSet has an order that will be used to walk the DAG when ToList() is called.  The order
+// A DepSet has an order that will be used to walk the DAG when ToList() is called.  The order
 // can be POSTORDER, PREORDER, or TOPOLOGICAL.  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
+// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
 // duplicated element is not guaranteed).
 //
-// A depSet is created by newDepSet or newDepSetBuilder.Build from the slice for direct contents
-// and the *depSets of dependencies. A depSet is immutable once created.
-//
-// This object uses reflection to remain agnostic to the type it contains.  It should be replaced
-// with generics once those exist in Go.  Callers should generally use a thin wrapper around depSet
-// that provides type-safe methods like DepSet for Paths.
-type depSet struct {
+// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the slice for direct contents
+// and the *DepSets of dependencies. A DepSet is immutable once created.
+type DepSet[T depSettableType] struct {
 	preorder   bool
 	reverse    bool
 	order      DepSetOrder
-	direct     interface{}
-	transitive []*depSet
+	direct     []T
+	transitive []*DepSet[T]
 }
 
-type depSetInterface interface {
-	embeddedDepSet() *depSet
-}
-
-func (d *depSet) embeddedDepSet() *depSet {
-	return d
-}
-
-var _ depSetInterface = (*depSet)(nil)
-
-// newDepSet returns an immutable depSet with the given order, direct and transitive contents.
-// direct must be a slice, but is not type-safe due to the lack of generics in Go.  It can be a
-// nil slice, but not a nil interface{}, i.e. []string(nil) but not nil.
-func newDepSet(order DepSetOrder, direct interface{}, transitive interface{}) *depSet {
-	var directCopy interface{}
-	transitiveDepSet := sliceToDepSets(transitive, order)
-
-	if order == TOPOLOGICAL {
-		directCopy = reverseSlice(direct)
-		reverseSliceInPlace(transitiveDepSet)
-	} else {
-		directCopy = copySlice(direct)
+// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
+func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] {
+	var directCopy []T
+	var transitiveCopy []*DepSet[T]
+	for _, t := range transitive {
+		if t.order != order {
+			panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
+				order, t.order))
+		}
 	}
 
-	return &depSet{
+	if order == TOPOLOGICAL {
+		// TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output.
+		// Pre-reverse the inputs here so their order is maintained in the output.
+		directCopy = ReverseSlice(direct)
+		transitiveCopy = ReverseSlice(transitive)
+	} else {
+		directCopy = append([]T(nil), direct...)
+		transitiveCopy = append([]*DepSet[T](nil), transitive...)
+	}
+
+	return &DepSet[T]{
 		preorder:   order == PREORDER,
 		reverse:    order == TOPOLOGICAL,
 		order:      order,
 		direct:     directCopy,
-		transitive: transitiveDepSet,
+		transitive: transitiveCopy,
 	}
 }
 
-// depSetBuilder is used to create an immutable depSet.
-type depSetBuilder struct {
+// DepSetBuilder is used to create an immutable DepSet.
+type DepSetBuilder[T depSettableType] struct {
 	order      DepSetOrder
-	direct     reflect.Value
-	transitive []*depSet
+	direct     []T
+	transitive []*DepSet[T]
 }
 
-// newDepSetBuilder returns a depSetBuilder to create an immutable depSet with the given order and
-// type, represented by a slice of type that will be in the depSet.
-func newDepSetBuilder(order DepSetOrder, typ interface{}) *depSetBuilder {
-	empty := reflect.Zero(reflect.TypeOf(typ))
-	return &depSetBuilder{
-		order:  order,
-		direct: empty,
+// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order and
+// type, represented by a slice of type that will be in the DepSet.
+func NewDepSetBuilder[T depSettableType](order DepSetOrder) *DepSetBuilder[T] {
+	return &DepSetBuilder[T]{
+		order: order,
 	}
 }
 
-// sliceToDepSets converts a slice of any type that implements depSetInterface (by having a depSet
-// embedded in it) into a []*depSet.
-func sliceToDepSets(in interface{}, order DepSetOrder) []*depSet {
-	slice := reflect.ValueOf(in)
-	length := slice.Len()
-	out := make([]*depSet, length)
-	for i := 0; i < length; i++ {
-		vi := slice.Index(i)
-		depSetIntf, ok := vi.Interface().(depSetInterface)
-		if !ok {
-			panic(fmt.Errorf("element %d is a %s, not a depSetInterface", i, vi.Type()))
-		}
-		depSet := depSetIntf.embeddedDepSet()
-		if depSet.order != order {
-			panic(fmt.Errorf("incompatible order, new depSet is %s but transitive depSet is %s",
-				order, depSet.order))
-		}
-		out[i] = depSet
-	}
-	return out
-}
-
-// DirectSlice adds direct contents to the depSet being built by a depSetBuilder. Newly added direct
-// contents are to the right of any existing direct contents.  The argument must be a slice, but
-// is not type-safe due to the lack of generics in Go.
-func (b *depSetBuilder) DirectSlice(direct interface{}) *depSetBuilder {
-	b.direct = reflect.AppendSlice(b.direct, reflect.ValueOf(direct))
+// DirectSlice adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents.
+func (b *DepSetBuilder[T]) DirectSlice(direct []T) *DepSetBuilder[T] {
+	b.direct = append(b.direct, direct...)
 	return b
 }
 
-// Direct adds direct contents to the depSet being built by a depSetBuilder. Newly added direct
-// contents are to the right of any existing direct contents.  The argument must be the same type
-// as the element of the slice passed to newDepSetBuilder, but is not type-safe due to the lack of
-// generics in Go.
-func (b *depSetBuilder) Direct(direct interface{}) *depSetBuilder {
-	b.direct = reflect.Append(b.direct, reflect.ValueOf(direct))
+// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents.
+func (b *DepSetBuilder[T]) Direct(direct ...T) *DepSetBuilder[T] {
+	b.direct = append(b.direct, direct...)
 	return b
 }
 
 // Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
-// transitive contents are to the right of any existing transitive contents.  The argument can
-// be any slice of type that has depSet embedded in it.
-func (b *depSetBuilder) Transitive(transitive interface{}) *depSetBuilder {
-	depSets := sliceToDepSets(transitive, b.order)
-	b.transitive = append(b.transitive, depSets...)
+// transitive contents are to the right of any existing transitive contents.
+func (b *DepSetBuilder[T]) Transitive(transitive ...*DepSet[T]) *DepSetBuilder[T] {
+	for _, t := range transitive {
+		if t.order != b.order {
+			panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
+				b.order, t.order))
+		}
+	}
+	b.transitive = append(b.transitive, transitive...)
 	return b
 }
 
-// Returns the depSet being built by this depSetBuilder.  The depSetBuilder retains its contents
+// Returns the DepSet being built by this DepSetBuilder.  The DepSetBuilder retains its contents
 // for creating more depSets.
-func (b *depSetBuilder) Build() *depSet {
-	return newDepSet(b.order, b.direct.Interface(), b.transitive)
+func (b *DepSetBuilder[T]) Build() *DepSet[T] {
+	return NewDepSet(b.order, b.direct, b.transitive)
 }
 
 // walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
 // otherwise postordered.
-func (d *depSet) walk(visit func(interface{})) {
-	visited := make(map[*depSet]bool)
+func (d *DepSet[T]) walk(visit func([]T)) {
+	visited := make(map[*DepSet[T]]bool)
 
-	var dfs func(d *depSet)
-	dfs = func(d *depSet) {
+	var dfs func(d *DepSet[T])
+	dfs = func(d *DepSet[T]) {
 		visited[d] = true
 		if d.preorder {
 			visit(d.direct)
@@ -197,155 +168,23 @@
 	dfs(d)
 }
 
-// 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
+// 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).
-//
-// This method uses a reflection-based implementation to find the unique elements in slice, which
-// is around 3x slower than a concrete implementation.  Type-safe wrappers around depSet can
-// provide their own implementation of ToList that calls depSet.toList with a method that
-// uses a concrete implementation.
-func (d *depSet) ToList() interface{} {
-	return d.toList(firstUnique)
-}
-
-// 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) toList(firstUniqueFunc func(interface{}) interface{}) interface{} {
+func (d *DepSet[T]) ToList() []T {
 	if d == nil {
 		return nil
 	}
-	slice := reflect.Zero(reflect.TypeOf(d.direct))
-	d.walk(func(paths interface{}) {
-		slice = reflect.AppendSlice(slice, reflect.ValueOf(paths))
+	var list []T
+	d.walk(func(paths []T) {
+		list = append(list, paths...)
 	})
-	list := slice.Interface()
-	list = firstUniqueFunc(list)
+	list = firstUniqueInPlace(list)
 	if d.reverse {
-		reverseSliceInPlace(list)
+		ReverseSliceInPlace(list)
 	}
 	return 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.  The
-// argument must be a slice, but is not type-safe due to the lack of reflection in Go.
-//
-// Performance of the reflection-based firstUnique is up to 3x slower than a concrete type
-// version such as FirstUniqueStrings.
-func firstUnique(slice interface{}) interface{} {
-	// 4 was chosen based on Benchmark_firstUnique results.
-	if reflect.ValueOf(slice).Len() > 4 {
-		return firstUniqueMap(slice)
-	}
-	return firstUniqueList(slice)
-}
-
-// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for
-// duplicates.
-func firstUniqueList(in interface{}) interface{} {
-	writeIndex := 0
-	slice := reflect.ValueOf(in)
-	length := slice.Len()
-outer:
-	for readIndex := 0; readIndex < length; readIndex++ {
-		readValue := slice.Index(readIndex)
-		for compareIndex := 0; compareIndex < writeIndex; compareIndex++ {
-			compareValue := slice.Index(compareIndex)
-			// These two Interface() calls seem to cause an allocation and significantly
-			// slow down this list-based implementation.  The map implementation below doesn't
-			// have this issue because reflect.Value.MapIndex takes a Value and appears to be
-			// able to do the map lookup without an allocation.
-			if readValue.Interface() == compareValue.Interface() {
-				// The value at readIndex already exists somewhere in the output region
-				// of the slice before writeIndex, skip it.
-				continue outer
-			}
-		}
-		if readIndex != writeIndex {
-			writeValue := slice.Index(writeIndex)
-			writeValue.Set(readValue)
-		}
-		writeIndex++
-	}
-	return slice.Slice(0, writeIndex).Interface()
-}
-
-var trueValue = reflect.ValueOf(true)
-
-// firstUniqueList is an implementation of firstUnique using an O(N) hash set lookup to look for
-// duplicates.
-func firstUniqueMap(in interface{}) interface{} {
-	writeIndex := 0
-	slice := reflect.ValueOf(in)
-	length := slice.Len()
-	seen := reflect.MakeMapWithSize(reflect.MapOf(slice.Type().Elem(), trueValue.Type()), slice.Len())
-	for readIndex := 0; readIndex < length; readIndex++ {
-		readValue := slice.Index(readIndex)
-		if seen.MapIndex(readValue).IsValid() {
-			continue
-		}
-		seen.SetMapIndex(readValue, trueValue)
-		if readIndex != writeIndex {
-			writeValue := slice.Index(writeIndex)
-			writeValue.Set(readValue)
-		}
-		writeIndex++
-	}
-	return slice.Slice(0, writeIndex).Interface()
-}
-
-// reverseSliceInPlace reverses the elements of a slice in place.  The argument must be a slice, but
-// is not type-safe due to the lack of reflection in Go.
-func reverseSliceInPlace(in interface{}) {
-	swapper := reflect.Swapper(in)
-	slice := reflect.ValueOf(in)
-	length := slice.Len()
-	for i, j := 0, length-1; i < j; i, j = i+1, j-1 {
-		swapper(i, j)
-	}
-}
-
-// reverseSlice returns a copy of a slice in reverse order.  The argument must be a slice, but is
-// not type-safe due to the lack of reflection in Go.
-func reverseSlice(in interface{}) interface{} {
-	slice := reflect.ValueOf(in)
-	if !slice.IsValid() || slice.IsNil() {
-		return in
-	}
-	if slice.Kind() != reflect.Slice {
-		panic(fmt.Errorf("%t is not a slice", in))
-	}
-	length := slice.Len()
-	if length == 0 {
-		return in
-	}
-	out := reflect.MakeSlice(slice.Type(), length, length)
-	for i := 0; i < length; i++ {
-		out.Index(i).Set(slice.Index(length - 1 - i))
-	}
-	return out.Interface()
-}
-
-// copySlice returns a copy of a slice.  The argument must be a slice, but is not type-safe due to
-// the lack of reflection in Go.
-func copySlice(in interface{}) interface{} {
-	slice := reflect.ValueOf(in)
-	if !slice.IsValid() || slice.IsNil() {
-		return in
-	}
-	length := slice.Len()
-	if length == 0 {
-		return in
-	}
-	out := reflect.MakeSlice(slice.Type(), length, length)
-	reflect.Copy(out, slice)
-	return out.Interface()
-}
diff --git a/android/depset_paths.go b/android/depset_paths.go
deleted file mode 100644
index ed561ba..0000000
--- a/android/depset_paths.go
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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
-
-// This file implements DepSet, a thin type-safe wrapper around depSet that contains Paths.
-
-// A DepSet efficiently stores Paths from transitive dependencies without copying. It is stored
-// as a DAG of DepSet nodes, each of which has some direct contents and a list of dependency
-// DepSet nodes.
-//
-// A DepSet has an order that will be used to walk the DAG when ToList() is called.  The order
-// can be POSTORDER, PREORDER, or TOPOLOGICAL.  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).
-//
-// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the Paths for direct contents
-// and the *DepSets of dependencies. A DepSet is immutable once created.
-type DepSet struct {
-	depSet
-}
-
-// DepSetBuilder is used to create an immutable DepSet.
-type DepSetBuilder struct {
-	depSetBuilder
-}
-
-// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
-func NewDepSet(order DepSetOrder, direct Paths, transitive []*DepSet) *DepSet {
-	return &DepSet{*newDepSet(order, direct, transitive)}
-}
-
-// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order.
-func NewDepSetBuilder(order DepSetOrder) *DepSetBuilder {
-	return &DepSetBuilder{*newDepSetBuilder(order, Paths(nil))}
-}
-
-// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
-// contents are to the right of any existing direct contents.
-func (b *DepSetBuilder) Direct(direct ...Path) *DepSetBuilder {
-	b.depSetBuilder.DirectSlice(direct)
-	return b
-}
-
-// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
-// transitive contents are to the right of any existing transitive contents.
-func (b *DepSetBuilder) Transitive(transitive ...*DepSet) *DepSetBuilder {
-	b.depSetBuilder.Transitive(transitive)
-	return b
-}
-
-// Returns the DepSet being built by this DepSetBuilder.  The DepSetBuilder retains its contents
-// for creating more DepSets.
-func (b *DepSetBuilder) Build() *DepSet {
-	return &DepSet{*b.depSetBuilder.Build()}
-}
-
-// 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).
-func (d *DepSet) ToList() Paths {
-	if d == nil {
-		return nil
-	}
-	return d.toList(func(paths interface{}) interface{} {
-		return FirstUniquePaths(paths.(Paths))
-	}).(Paths)
-}
-
-// ToSortedList returns the direct and transitive contents of a DepSet in lexically sorted order
-// with duplicates removed.
-func (d *DepSet) ToSortedList() Paths {
-	if d == nil {
-		return nil
-	}
-	paths := d.ToList()
-	return SortedUniquePaths(paths)
-}
diff --git a/android/depset_test.go b/android/depset_test.go
index 955ccb0..376dffa 100644
--- a/android/depset_test.go
+++ b/android/depset_test.go
@@ -17,51 +17,40 @@
 import (
 	"fmt"
 	"reflect"
-	"strconv"
 	"strings"
 	"testing"
 )
 
 func ExampleDepSet_ToList_postordered() {
-	a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+	a := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
 
-	fmt.Println(d.ToList().Strings())
+	fmt.Println(Paths(d.ToList()).Strings())
 	// Output: [a b c d]
 }
 
 func ExampleDepSet_ToList_preordered() {
-	a := NewDepSetBuilder(PREORDER).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder(PREORDER).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder(PREORDER).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder(PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+	a := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
 
-	fmt.Println(d.ToList().Strings())
+	fmt.Println(Paths(d.ToList()).Strings())
 	// Output: [d b a c]
 }
 
 func ExampleDepSet_ToList_topological() {
-	a := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build()
+	a := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build()
 
-	fmt.Println(d.ToList().Strings())
+	fmt.Println(Paths(d.ToList()).Strings())
 	// Output: [d b c a]
 }
 
-func ExampleDepSet_ToSortedList() {
-	a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
-
-	fmt.Println(d.ToSortedList().Strings())
-	// Output: [a b c d]
-}
-
 // Tests based on Bazel's ExpanderTestBase.java to ensure compatibility
 // https://github.com/bazelbuild/bazel/blob/master/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
 func TestDepSet(t *testing.T) {
@@ -74,13 +63,13 @@
 
 	tests := []struct {
 		name                             string
-		depSet                           func(t *testing.T, order DepSetOrder) *DepSet
+		depSet                           func(t *testing.T, order DepSetOrder) *DepSet[Path]
 		postorder, preorder, topological []string
 	}{
 		{
 			name: "simple",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				return NewDepSet(order, Paths{c, a, b}, nil)
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				return NewDepSet[Path](order, Paths{c, a, b}, nil)
 			},
 			postorder:   []string{"c", "a", "b"},
 			preorder:    []string{"c", "a", "b"},
@@ -88,8 +77,8 @@
 		},
 		{
 			name: "simpleNoDuplicates",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				return NewDepSet(order, Paths{c, a, a, a, b}, nil)
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				return NewDepSet[Path](order, Paths{c, a, a, a, b}, nil)
 			},
 			postorder:   []string{"c", "a", "b"},
 			preorder:    []string{"c", "a", "b"},
@@ -97,9 +86,9 @@
 		},
 		{
 			name: "nesting",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				subset := NewDepSet(order, Paths{c, a, e}, nil)
-				return NewDepSet(order, Paths{b, d}, []*DepSet{subset})
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				subset := NewDepSet[Path](order, Paths{c, a, e}, nil)
+				return NewDepSet[Path](order, Paths{b, d}, []*DepSet[Path]{subset})
 			},
 			postorder:   []string{"c", "a", "e", "b", "d"},
 			preorder:    []string{"b", "d", "c", "a", "e"},
@@ -107,14 +96,14 @@
 		},
 		{
 			name: "builderReuse",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
 				assertEquals := func(t *testing.T, w, g Paths) {
 					t.Helper()
 					if !reflect.DeepEqual(w, g) {
 						t.Errorf("want %q, got %q", w, g)
 					}
 				}
-				builder := NewDepSetBuilder(order)
+				builder := NewDepSetBuilder[Path](order)
 				assertEquals(t, nil, builder.Build().ToList())
 
 				builder.Direct(b)
@@ -123,7 +112,7 @@
 				builder.Direct(d)
 				assertEquals(t, Paths{b, d}, builder.Build().ToList())
 
-				child := NewDepSetBuilder(order).Direct(c, a, e).Build()
+				child := NewDepSetBuilder[Path](order).Direct(c, a, e).Build()
 				builder.Transitive(child)
 				return builder.Build()
 			},
@@ -133,9 +122,9 @@
 		},
 		{
 			name: "builderChaining",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				return NewDepSetBuilder(order).Direct(b).Direct(d).
-					Transitive(NewDepSetBuilder(order).Direct(c, a, e).Build()).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				return NewDepSetBuilder[Path](order).Direct(b).Direct(d).
+					Transitive(NewDepSetBuilder[Path](order).Direct(c, a, e).Build()).Build()
 			},
 			postorder:   []string{"c", "a", "e", "b", "d"},
 			preorder:    []string{"b", "d", "c", "a", "e"},
@@ -143,9 +132,9 @@
 		},
 		{
 			name: "transitiveDepsHandledSeparately",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				subset := NewDepSetBuilder(order).Direct(c, a, e).Build()
-				builder := NewDepSetBuilder(order)
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				subset := NewDepSetBuilder[Path](order).Direct(c, a, e).Build()
+				builder := NewDepSetBuilder[Path](order)
 				// The fact that we add the transitive subset between the Direct(b) and Direct(d)
 				// calls should not change the result.
 				builder.Direct(b)
@@ -159,9 +148,9 @@
 		},
 		{
 			name: "nestingNoDuplicates",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				subset := NewDepSetBuilder(order).Direct(c, a, e).Build()
-				return NewDepSetBuilder(order).Direct(b, d, e).Transitive(subset).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				subset := NewDepSetBuilder[Path](order).Direct(c, a, e).Build()
+				return NewDepSetBuilder[Path](order).Direct(b, d, e).Transitive(subset).Build()
 			},
 			postorder:   []string{"c", "a", "e", "b", "d"},
 			preorder:    []string{"b", "d", "e", "c", "a"},
@@ -169,10 +158,10 @@
 		},
 		{
 			name: "chain",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				c := NewDepSetBuilder(order).Direct(c).Build()
-				b := NewDepSetBuilder(order).Direct(b).Transitive(c).Build()
-				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				c := NewDepSetBuilder[Path](order).Direct(c).Build()
+				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(c).Build()
+				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Build()
 
 				return a
 			},
@@ -182,11 +171,11 @@
 		},
 		{
 			name: "diamond",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				d := NewDepSetBuilder(order).Direct(d).Build()
-				c := NewDepSetBuilder(order).Direct(c).Transitive(d).Build()
-				b := NewDepSetBuilder(order).Direct(b).Transitive(d).Build()
-				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				d := NewDepSetBuilder[Path](order).Direct(d).Build()
+				c := NewDepSetBuilder[Path](order).Direct(c).Transitive(d).Build()
+				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Build()
+				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build()
 
 				return a
 			},
@@ -196,12 +185,12 @@
 		},
 		{
 			name: "extendedDiamond",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				d := NewDepSetBuilder(order).Direct(d).Build()
-				e := NewDepSetBuilder(order).Direct(e).Build()
-				b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build()
-				c := NewDepSetBuilder(order).Direct(c).Transitive(e).Transitive(d).Build()
-				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				d := NewDepSetBuilder[Path](order).Direct(d).Build()
+				e := NewDepSetBuilder[Path](order).Direct(e).Build()
+				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Transitive(e).Build()
+				c := NewDepSetBuilder[Path](order).Direct(c).Transitive(e).Transitive(d).Build()
+				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build()
 				return a
 			},
 			postorder:   []string{"d", "e", "b", "c", "a"},
@@ -210,13 +199,13 @@
 		},
 		{
 			name: "extendedDiamondRightArm",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				d := NewDepSetBuilder(order).Direct(d).Build()
-				e := NewDepSetBuilder(order).Direct(e).Build()
-				b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build()
-				c2 := NewDepSetBuilder(order).Direct(c2).Transitive(e).Transitive(d).Build()
-				c := NewDepSetBuilder(order).Direct(c).Transitive(c2).Build()
-				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				d := NewDepSetBuilder[Path](order).Direct(d).Build()
+				e := NewDepSetBuilder[Path](order).Direct(e).Build()
+				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Transitive(e).Build()
+				c2 := NewDepSetBuilder[Path](order).Direct(c2).Transitive(e).Transitive(d).Build()
+				c := NewDepSetBuilder[Path](order).Direct(c).Transitive(c2).Build()
+				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build()
 				return a
 			},
 			postorder:   []string{"d", "e", "b", "c2", "c", "a"},
@@ -225,10 +214,10 @@
 		},
 		{
 			name: "orderConflict",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				child1 := NewDepSetBuilder(order).Direct(a, b).Build()
-				child2 := NewDepSetBuilder(order).Direct(b, a).Build()
-				parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				child1 := NewDepSetBuilder[Path](order).Direct(a, b).Build()
+				child2 := NewDepSetBuilder[Path](order).Direct(b, a).Build()
+				parent := NewDepSetBuilder[Path](order).Transitive(child1).Transitive(child2).Build()
 				return parent
 			},
 			postorder:   []string{"a", "b"},
@@ -237,12 +226,12 @@
 		},
 		{
 			name: "orderConflictNested",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
-				a := NewDepSetBuilder(order).Direct(a).Build()
-				b := NewDepSetBuilder(order).Direct(b).Build()
-				child1 := NewDepSetBuilder(order).Transitive(a).Transitive(b).Build()
-				child2 := NewDepSetBuilder(order).Transitive(b).Transitive(a).Build()
-				parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build()
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
+				a := NewDepSetBuilder[Path](order).Direct(a).Build()
+				b := NewDepSetBuilder[Path](order).Direct(b).Build()
+				child1 := NewDepSetBuilder[Path](order).Transitive(a).Transitive(b).Build()
+				child2 := NewDepSetBuilder[Path](order).Transitive(b).Transitive(a).Build()
+				parent := NewDepSetBuilder[Path](order).Transitive(child1).Transitive(child2).Build()
 				return parent
 			},
 			postorder:   []string{"a", "b"},
@@ -255,19 +244,19 @@
 		t.Run(tt.name, func(t *testing.T) {
 			t.Run("postorder", func(t *testing.T) {
 				depSet := tt.depSet(t, POSTORDER)
-				if g, w := depSet.ToList().Strings(), tt.postorder; !reflect.DeepEqual(g, w) {
+				if g, w := Paths(depSet.ToList()).Strings(), tt.postorder; !reflect.DeepEqual(g, w) {
 					t.Errorf("expected ToList() = %q, got %q", w, g)
 				}
 			})
 			t.Run("preorder", func(t *testing.T) {
 				depSet := tt.depSet(t, PREORDER)
-				if g, w := depSet.ToList().Strings(), tt.preorder; !reflect.DeepEqual(g, w) {
+				if g, w := Paths(depSet.ToList()).Strings(), tt.preorder; !reflect.DeepEqual(g, w) {
 					t.Errorf("expected ToList() = %q, got %q", w, g)
 				}
 			})
 			t.Run("topological", func(t *testing.T) {
 				depSet := tt.depSet(t, TOPOLOGICAL)
-				if g, w := depSet.ToList().Strings(), tt.topological; !reflect.DeepEqual(g, w) {
+				if g, w := Paths(depSet.ToList()).Strings(), tt.topological; !reflect.DeepEqual(g, w) {
 					t.Errorf("expected ToList() = %q, got %q", w, g)
 				}
 			})
@@ -288,7 +277,7 @@
 				}
 			}
 		}()
-		NewDepSet(order1, nil, []*DepSet{NewDepSet(order2, nil, nil)})
+		NewDepSet(order1, nil, []*DepSet[Path]{NewDepSet[Path](order2, nil, nil)})
 		t.Fatal("expected panic")
 	}
 
@@ -304,87 +293,3 @@
 		})
 	}
 }
-
-func Test_firstUnique(t *testing.T) {
-	f := func(t *testing.T, imp func([]string) []string, in, want []string) {
-		t.Helper()
-		out := imp(in)
-		if !reflect.DeepEqual(out, want) {
-			t.Errorf("incorrect output:")
-			t.Errorf("     input: %#v", in)
-			t.Errorf("  expected: %#v", want)
-			t.Errorf("       got: %#v", out)
-		}
-	}
-
-	for _, testCase := range firstUniqueStringsTestCases {
-		t.Run("list", func(t *testing.T) {
-			f(t, func(s []string) []string {
-				return firstUniqueList(s).([]string)
-			}, testCase.in, testCase.out)
-		})
-		t.Run("map", func(t *testing.T) {
-			f(t, func(s []string) []string {
-				return firstUniqueMap(s).([]string)
-			}, testCase.in, testCase.out)
-		})
-	}
-}
-
-func Benchmark_firstUnique(b *testing.B) {
-	implementations := []struct {
-		name string
-		f    func([]string) []string
-	}{
-		{
-			name: "list",
-			f: func(slice []string) []string {
-				return firstUniqueList(slice).([]string)
-			},
-		},
-		{
-			name: "map",
-			f: func(slice []string) []string {
-				return firstUniqueMap(slice).([]string)
-			},
-		},
-		{
-			name: "optimal",
-			f: func(slice []string) []string {
-				return firstUnique(slice).([]string)
-			},
-		},
-	}
-	const maxSize = 1024
-	uniqueStrings := make([]string, maxSize)
-	for i := range uniqueStrings {
-		uniqueStrings[i] = strconv.Itoa(i)
-	}
-	sameString := make([]string, maxSize)
-	for i := range sameString {
-		sameString[i] = uniqueStrings[0]
-	}
-
-	f := func(b *testing.B, imp func([]string) []string, s []string) {
-		for i := 0; i < b.N; i++ {
-			b.ReportAllocs()
-			s = append([]string(nil), s...)
-			imp(s)
-		}
-	}
-
-	for n := 1; n <= maxSize; n <<= 1 {
-		b.Run(strconv.Itoa(n), func(b *testing.B) {
-			for _, implementation := range implementations {
-				b.Run(implementation.name, func(b *testing.B) {
-					b.Run("same", func(b *testing.B) {
-						f(b, implementation.f, sameString[:n])
-					})
-					b.Run("unique", func(b *testing.B) {
-						f(b, implementation.f, uniqueStrings[:n])
-					})
-				})
-			}
-		})
-	}
-}
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 73000a9..8933bd5 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -55,7 +55,7 @@
 	var allDepMetadataFiles Paths
 	var allDepMetadataArgs []string
 	var allDepOutputFiles Paths
-	var allDepMetadataDepSets []*PathsDepSet
+	var allDepMetadataDepSets []*DepSet[Path]
 
 	ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
 		dep, _ := bpdep.(Module)
@@ -127,7 +127,7 @@
 		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n "))
 
 	if isContainer {
-		transitiveDeps := newPathsDepSet(nil, allDepMetadataDepSets).ToList()
+		transitiveDeps := Paths(NewDepSet[Path](TOPOLOGICAL, nil, allDepMetadataDepSets).ToList())
 		args = append(args,
 			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d "))
 		orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...)
@@ -170,7 +170,7 @@
 
 	ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{
 		LicenseMetadataPath:   licenseMetadataFile,
-		LicenseMetadataDepSet: newPathsDepSet(Paths{licenseMetadataFile}, allDepMetadataDepSets),
+		LicenseMetadataDepSet: NewDepSet(TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets),
 	})
 }
 
@@ -198,7 +198,7 @@
 // LicenseMetadataInfo stores the license metadata path for a module.
 type LicenseMetadataInfo struct {
 	LicenseMetadataPath   Path
-	LicenseMetadataDepSet *PathsDepSet
+	LicenseMetadataDepSet *DepSet[Path]
 }
 
 // licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into
diff --git a/android/module.go b/android/module.go
index 726fa85..384776a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1525,10 +1525,10 @@
 
 	noAddressSanitizer   bool
 	installFiles         InstallPaths
-	installFilesDepSet   *installPathsDepSet
+	installFilesDepSet   *DepSet[InstallPath]
 	checkbuildFiles      Paths
 	packagingSpecs       []PackagingSpec
-	packagingSpecsDepSet *packagingSpecsDepSet
+	packagingSpecsDepSet *DepSet[PackagingSpec]
 	// katiInstalls tracks the install rules that were created by Soong but are being exported
 	// to Make to convert to ninja rules so that Make can add additional dependencies.
 	katiInstalls katiInstalls
@@ -2108,9 +2108,9 @@
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
 // tag that is annotated as needing installation via the isInstallDepNeeded method.
-func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
-	var installDeps []*installPathsDepSet
-	var packagingSpecs []*packagingSpecsDepSet
+func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*DepSet[InstallPath], []*DepSet[PackagingSpec]) {
+	var installDeps []*DepSet[InstallPath]
+	var packagingSpecs []*DepSet[PackagingSpec]
 	ctx.VisitDirectDeps(func(dep Module) {
 		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
 			// Installation is still handled by Make, so anything hidden from Make is not
@@ -2405,7 +2405,7 @@
 	// set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies
 	// of installed files of this module.  It will be replaced by a depset including the installed
 	// files of this module at the end for use by modules that depend on this one.
-	m.installFilesDepSet = newInstallPathsDepSet(nil, dependencyInstallFiles)
+	m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles)
 
 	// Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never
 	// reporting missing dependency errors in Blueprint when AllowMissingDependencies == true.
@@ -2512,8 +2512,8 @@
 		}
 	}
 
-	m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
-	m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)
+	m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, m.installFiles, dependencyInstallFiles)
+	m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, m.packagingSpecs, dependencyPackagingSpecs)
 
 	buildLicenseMetadata(ctx, m.licenseMetadataFile)
 
@@ -3394,7 +3394,7 @@
 	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
 
 	if !m.skipInstall() {
-		deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...)
+		deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList()).Paths()...)
 
 		var implicitDeps, orderOnlyDeps Paths
 
@@ -3969,26 +3969,6 @@
 	return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents)
 }
 
-// installPathsDepSet is a thin type-safe wrapper around the generic depSet.  It always uses
-// topological order.
-type installPathsDepSet struct {
-	depSet
-}
-
-// newInstallPathsDepSet returns an immutable packagingSpecsDepSet with the given direct and
-// transitive contents.
-func newInstallPathsDepSet(direct InstallPaths, transitive []*installPathsDepSet) *installPathsDepSet {
-	return &installPathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
-}
-
-// ToList returns the installPathsDepSet flattened to a list in topological order.
-func (d *installPathsDepSet) ToList() InstallPaths {
-	if d == nil {
-		return nil
-	}
-	return d.depSet.ToList().(InstallPaths)
-}
-
 func registerSoongConfigTraceMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("soongconfigtrace", soongConfigTraceMutator).Parallel()
 }
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/packaging.go b/android/packaging.go
index c764a6d..503bb97 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -282,23 +282,3 @@
 	builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
 	return entries
 }
-
-// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet.  It always uses
-// topological order.
-type packagingSpecsDepSet struct {
-	depSet
-}
-
-// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and
-// transitive contents.
-func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet {
-	return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
-}
-
-// ToList returns the packagingSpecsDepSet flattened to a list in topological order.
-func (d *packagingSpecsDepSet) ToList() []PackagingSpec {
-	if d == nil {
-		return nil
-	}
-	return d.depSet.ToList().([]PackagingSpec)
-}
diff --git a/android/paths.go b/android/paths.go
index 94fa89c..fda4d2f 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -2196,23 +2196,3 @@
 	}
 	return false
 }
-
-// PathsDepSet is a thin type-safe wrapper around the generic depSet.  It always uses
-// topological order.
-type PathsDepSet struct {
-	depSet
-}
-
-// newPathsDepSet returns an immutable PathsDepSet with the given direct and
-// transitive contents.
-func newPathsDepSet(direct Paths, transitive []*PathsDepSet) *PathsDepSet {
-	return &PathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
-}
-
-// ToList returns the PathsDepSet flattened to a list in topological order.
-func (d *PathsDepSet) ToList() Paths {
-	if d == nil {
-		return nil
-	}
-	return d.depSet.ToList().(Paths)
-}
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 08a3521..e17d7b2 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,44 +288,87 @@
 }
 
 // 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 firstUniqueStringsMap(list)
-	}
-	return firstUniqueStringsList(list)
+	return firstUnique(list)
 }
 
-func firstUniqueStringsList(list []string) []string {
-	k := 0
+// firstUnique returns all unique elements of a slice, keeping the first copy of each.  It
+// does not modify the input slice.
+func firstUnique[T comparable](slice []T) []T {
+	// 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)
+}
+
+// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for
+// duplicates.
+func firstUniqueList[T any](in []T) []T {
+	writeIndex := 0
 outer:
-	for i := 0; i < len(list); i++ {
-		for j := 0; j < k; j++ {
-			if list[i] == list[j] {
+	for readIndex := 0; readIndex < len(in); readIndex++ {
+		for compareIndex := 0; compareIndex < writeIndex; compareIndex++ {
+			if interface{}(in[readIndex]) == interface{}(in[compareIndex]) {
+				// The value at readIndex already exists somewhere in the output region
+				// of the slice before writeIndex, skip it.
 				continue outer
 			}
 		}
-		list[k] = list[i]
-		k++
+		if readIndex != writeIndex {
+			in[writeIndex] = in[readIndex]
+		}
+		writeIndex++
 	}
-	return list[:k]
+	return in[0:writeIndex]
 }
 
-func firstUniqueStringsMap(list []string) []string {
-	k := 0
-	seen := make(map[string]bool, len(list))
-	for i := 0; i < len(list); i++ {
-		if seen[list[i]] {
+// firstUniqueMap is an implementation of firstUnique using an O(N) hash set lookup to look for
+// duplicates.
+func firstUniqueMap[T comparable](in []T) []T {
+	writeIndex := 0
+	seen := make(map[T]bool, len(in))
+	for readIndex := 0; readIndex < len(in); readIndex++ {
+		if _, exists := seen[in[readIndex]]; exists {
 			continue
 		}
-		seen[list[i]] = true
-		list[k] = list[i]
-		k++
+		seen[in[readIndex]] = true
+		if readIndex != writeIndex {
+			in[writeIndex] = in[readIndex]
+		}
+		writeIndex++
 	}
-	return list[:k]
+	return in[0:writeIndex]
+}
+
+// ReverseSliceInPlace reverses the elements of a slice in place and returns it.
+func ReverseSliceInPlace[T any](in []T) []T {
+	for i, j := 0, len(in)-1; i < j; i, j = i+1, j-1 {
+		in[i], in[j] = in[j], in[i]
+	}
+	return in
+}
+
+// ReverseSlice returns a copy of a slice in reverse order.
+func ReverseSlice[T any](in []T) []T {
+	if in == nil {
+		return in
+	}
+	out := make([]T, len(in))
+	for i := 0; i < len(in); i++ {
+		out[i] = in[len(in)-1-i]
+	}
+	return out
 }
 
 // LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of
diff --git a/android/util_test.go b/android/util_test.go
index a2ef589..20161e5 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -20,6 +20,7 @@
 	"strconv"
 	"strings"
 	"testing"
+	"unsafe"
 )
 
 var firstUniqueStringsTestCases = []struct {
@@ -74,10 +75,10 @@
 
 	for _, testCase := range firstUniqueStringsTestCases {
 		t.Run("list", func(t *testing.T) {
-			f(t, firstUniqueStringsList, testCase.in, testCase.out)
+			f(t, firstUniqueList[string], testCase.in, testCase.out)
 		})
 		t.Run("map", func(t *testing.T) {
-			f(t, firstUniqueStringsMap, testCase.in, testCase.out)
+			f(t, firstUniqueMap[string], testCase.in, testCase.out)
 		})
 	}
 }
@@ -385,7 +386,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)
 }
 
@@ -604,11 +605,11 @@
 	}{
 		{
 			name: "list",
-			f:    firstUniqueStringsList,
+			f:    firstUniqueList[string],
 		},
 		{
 			name: "map",
-			f:    firstUniqueStringsMap,
+			f:    firstUniqueMap[string],
 		},
 		{
 			name: "optimal",
@@ -754,3 +755,65 @@
 		})
 	}
 }
+
+var reverseTestCases = []struct {
+	name     string
+	in       []string
+	expected []string
+}{
+	{
+		name:     "nil",
+		in:       nil,
+		expected: nil,
+	},
+	{
+		name:     "empty",
+		in:       []string{},
+		expected: []string{},
+	},
+	{
+		name:     "one",
+		in:       []string{"one"},
+		expected: []string{"one"},
+	},
+	{
+		name:     "even",
+		in:       []string{"one", "two"},
+		expected: []string{"two", "one"},
+	},
+	{
+		name:     "odd",
+		in:       []string{"one", "two", "three"},
+		expected: []string{"three", "two", "one"},
+	},
+}
+
+func TestReverseSliceInPlace(t *testing.T) {
+	for _, testCase := range reverseTestCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			slice := CopyOf(testCase.in)
+			slice2 := slice
+			ReverseSliceInPlace(slice)
+			if !reflect.DeepEqual(slice, testCase.expected) {
+				t.Errorf("expected %#v, got %#v", testCase.expected, slice)
+			}
+			if unsafe.SliceData(slice) != unsafe.SliceData(slice2) {
+				t.Errorf("expected slices to share backing array")
+			}
+		})
+	}
+}
+
+func TestReverseSlice(t *testing.T) {
+	for _, testCase := range reverseTestCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			slice := ReverseSlice(testCase.in)
+			if !reflect.DeepEqual(slice, testCase.expected) {
+				t.Errorf("expected %#v, got %#v", testCase.expected, slice)
+			}
+			if slice != nil && unsafe.SliceData(testCase.in) == unsafe.SliceData(slice) {
+				t.Errorf("expected slices to have different backing arrays")
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 00315bc..3bec854 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -175,10 +175,6 @@
 			Whole_static_libs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
-		Flatten_apex struct {
-			Enabled *bool
-		}
-
 		Native_coverage struct {
 			Src          *string  `android:"arch_variant"`
 			Srcs         []string `android:"arch_variant"`
@@ -397,7 +393,6 @@
 	Ndk_abis *bool `json:",omitempty"`
 
 	TrimmedApex                  *bool `json:",omitempty"`
-	Flatten_apex                 *bool `json:",omitempty"`
 	ForceApexSymlinkOptimization *bool `json:",omitempty"`
 	CompressedApex               *bool `json:",omitempty"`
 	Aml_abis                     *bool `json:",omitempty"`
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 9623a8b..7212a07 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -166,7 +166,7 @@
 		}
 	} else {
 		llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
-		llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so.1")
+		llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so")
 		for _, strip := range s.properties.Strip_files {
 			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
 			if !ctx.Windows() {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 684833d..f469062 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -85,16 +85,12 @@
 	// conflicts between two apexes with the same apexName.
 
 	moduleNames := []string{}
-	apexType := a.properties.ApexType
 	// To avoid creating duplicate build rules, run this function only when primaryApexType is true
 	// to install symbol files in $(PRODUCT_OUT}/apex.
-	// And if apexType is flattened, run this function to install files in $(PRODUCT_OUT}/system/apex.
-	if !a.primaryApexType && apexType != flattenedApex {
+	if !a.primaryApexType {
 		return moduleNames
 	}
 
-	seenDataOutPaths := make(map[string]bool)
-
 	for _, fi := range a.filesInfo {
 		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
 		moduleName := a.fullModuleName(apexBundleName, linkToSystemLib, &fi)
@@ -131,33 +127,13 @@
 		}
 		// /apex/<apexBundleName>/{lib|framework|...}
 		pathForSymbol := filepath.Join("$(PRODUCT_OUT)", "apex", apexBundleName, fi.installDir)
-		var modulePath string
-		if apexType == flattenedApex {
-			// /system/apex/<apexBundleName>/{lib|framework|...}
-			modulePath = filepath.Join(a.installDir.String(), apexBundleName, fi.installDir)
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
-			if a.primaryApexType {
-				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathForSymbol)
-			}
-			android.AndroidMkEmitAssignList(w, "LOCAL_MODULE_SYMLINKS", fi.symlinks)
-			newDataPaths := []android.DataPath{}
-			for _, path := range fi.dataPaths {
-				dataOutPath := modulePath + ":" + path.SrcPath.Rel()
-				if ok := seenDataOutPaths[dataOutPath]; !ok {
-					newDataPaths = append(newDataPaths, path)
-					seenDataOutPaths[dataOutPath] = true
-				}
-			}
-			android.AndroidMkEmitAssignList(w, "LOCAL_TEST_DATA", android.AndroidMkDataPaths(newDataPaths))
-		} else {
-			modulePath = pathForSymbol
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
+		modulePath := pathForSymbol
+		fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
 
-			// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
-			// We don't need to have notice file for the individual modules in it. Otherwise,
-			// we will have duplicated notice entries.
-			fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
-		}
+		// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
+		// We don't need to have notice file for the individual modules in it. Otherwise,
+		// we will have duplicated notice entries.
+		fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
 		fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem()))
 		fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem()))
 		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
@@ -257,31 +233,6 @@
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk")
 		default:
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
-			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
-				if a.primaryApexType {
-					// To install companion files (init_rc, vintf_fragments)
-					// Copy some common properties of apexBundle to apex_manifest
-					commonProperties := []string{
-						"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
-					}
-					for _, name := range commonProperties {
-						if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok {
-							android.AndroidMkEmitAssignList(w, name, value)
-						}
-					}
-
-					// Make apex_manifest.pb module for this APEX to override all other
-					// modules in the APEXes being overridden by this APEX
-					var patterns []string
-					for _, o := range a.overridableProperties.Overrides {
-						patterns = append(patterns, "%."+o+a.suffix)
-					}
-					android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", patterns)
-				}
-
-				// File_contexts of flattened APEXes should be merged into file_contexts.bin
-				fmt.Fprintln(w, "LOCAL_FILE_CONTEXTS :=", a.fileContexts)
-			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 		}
 
@@ -320,73 +271,62 @@
 				moduleNames = a.androidMkForFiles(w, name, moduleDir, data)
 			}
 
-			if apexType == flattenedApex {
-				// Only image APEXes can be flattened.
-				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle.flat")
-				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
-				data.Entries.WriteLicenseVariables(w)
-				a.writeRequiredModules(w, moduleNames)
-				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
-
-			} else {
-				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle")
-				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
-				data.Entries.WriteLicenseVariables(w)
-				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
-				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
-				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.String())
-				stemSuffix := apexType.suffix()
-				if a.isCompressed {
-					stemSuffix = imageCapexSuffix
-				}
-				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
-				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
-				if a.installable() {
-					fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
-					fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
-				}
-
-				// Because apex writes .mk with Custom(), we need to write manually some common properties
-				// which are available via data.Entries
-				commonProperties := []string{
-					"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
-					"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
-					"LOCAL_MODULE_OWNER",
-				}
-				for _, name := range commonProperties {
-					if value, ok := data.Entries.EntryMap[name]; ok {
-						android.AndroidMkEmitAssignList(w, name, value)
-					}
-				}
-
-				android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
-				a.writeRequiredModules(w, moduleNames)
-
-				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
-
-				if apexType == imageApex {
-					fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
-				}
-				android.AndroidMkEmitAssignList(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS", a.lintReports.Strings())
-
-				if a.installedFilesFile != nil {
-					goal := "checkbuild"
-					distFile := name + "-installed-files.txt"
-					fmt.Fprintln(w, ".PHONY:", goal)
-					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
-						goal, a.installedFilesFile.String(), distFile)
-					fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String())
-				}
-				for _, dist := range data.Entries.GetDistForGoals(a) {
-					fmt.Fprintf(w, dist)
-				}
-
-				distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String())
-				distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String())
-				distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String())
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle")
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+			fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+			data.Entries.WriteLicenseVariables(w)
+			fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
+			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.String())
+			stemSuffix := apexType.suffix()
+			if a.isCompressed {
+				stemSuffix = imageCapexSuffix
 			}
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
+			fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
+			if a.installable() {
+				fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
+				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+			}
+
+			// Because apex writes .mk with Custom(), we need to write manually some common properties
+			// which are available via data.Entries
+			commonProperties := []string{
+				"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
+				"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
+				"LOCAL_MODULE_OWNER",
+			}
+			for _, name := range commonProperties {
+				if value, ok := data.Entries.EntryMap[name]; ok {
+					android.AndroidMkEmitAssignList(w, name, value)
+				}
+			}
+
+			android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
+			a.writeRequiredModules(w, moduleNames)
+
+			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+
+			if apexType == imageApex {
+				fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
+			}
+			android.AndroidMkEmitAssignList(w, "ALL_MODULES.$(my_register_name).LINT_REPORTS", a.lintReports.Strings())
+
+			if a.installedFilesFile != nil {
+				goal := "checkbuild"
+				distFile := name + "-installed-files.txt"
+				fmt.Fprintln(w, ".PHONY:", goal)
+				fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+					goal, a.installedFilesFile.String(), distFile)
+				fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String())
+			}
+			for _, dist := range data.Entries.GetDistForGoals(a) {
+				fmt.Fprintf(w, dist)
+			}
+
+			distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String())
+			distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String())
+			distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String())
 		}}
 }
 
diff --git a/apex/apex.go b/apex/apex.go
index 51c67d0..1d094eb 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -79,7 +79,7 @@
 	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
 	ctx.BottomUp("apex", apexMutator).Parallel()
 	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
-	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+	ctx.BottomUp("apex_packaging", apexPackagingMutator).Parallel()
 	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
 	// Register after apex_info mutator so that it can use ApexVariationName
 	ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
@@ -216,9 +216,7 @@
 
 	HideFromMake bool `blueprint:"mutated"`
 
-	// Internal package method for this APEX. When payload_type is image, this can be either
-	// imageApex or flattenedApex depending on Config.FlattenApex(). When payload_type is zip,
-	// this becomes zipApex.
+	// Internal package method for this APEX.
 	ApexType apexPackaging `blueprint:"mutated"`
 
 	// Name that dependencies can specify in their apex_available properties to refer to this module.
@@ -427,7 +425,7 @@
 	// one gets installed to the device.
 	primaryApexType bool
 
-	// Suffix of module name in Android.mk ".flattened", ".apex", ".zipapex", or ""
+	// Suffix of module name in Android.mk ".apex", ".zipapex", or ""
 	suffix string
 
 	// File system type of apex_payload.img
@@ -535,8 +533,7 @@
 // apexFile represents a file in an APEX bundle. This is created during the first half of
 // GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half
 // of the function, this is used to create commands that copies the files into a staging directory,
-// where they are packaged into the APEX file. This struct is also used for creating Make modules
-// for each of the files in case when the APEX is flattened.
+// where they are packaged into the APEX file.
 type apexFile struct {
 	// buildFile is put in the installDir inside the APEX.
 	builtFile  android.Path
@@ -1367,12 +1364,8 @@
 	// zipApex is a packaging method where contents are directly included in the zip container.
 	// This is used for host-side testing - because the contents are easily accessible by
 	// unzipping the container.
+	// TODO(b/279835185) deprecate zipApex
 	zipApex
-
-	// flattendApex is a packaging method where contents are not included in the APEX file, but
-	// installed to /apex/<apexname> directory on the device. This packaging method is used for
-	// old devices where the filesystem-based APEX file can't be supported.
-	flattenedApex
 )
 
 const (
@@ -1380,12 +1373,10 @@
 	imageApexSuffix  = ".apex"
 	imageCapexSuffix = ".capex"
 	zipApexSuffix    = ".zipapex"
-	flattenedSuffix  = ".flattened"
 
 	// variant names each of which is for a packaging method
-	imageApexType     = "image"
-	zipApexType       = "zip"
-	flattenedApexType = "flattened"
+	imageApexType = "image"
+	zipApexType   = "zip"
 
 	ext4FsType  = "ext4"
 	f2fsFsType  = "f2fs"
@@ -1415,9 +1406,8 @@
 	}
 }
 
-// apexFlattenedMutator creates one or more variations each of which is for a packaging method.
-// TODO(jiyong): give a better name to this mutator
-func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
+// apexPackagingMutator creates one or more variations each of which is for a packaging method.
+func apexPackagingMutator(mctx android.BottomUpMutatorContext) {
 	if !mctx.Module().Enabled() {
 		return
 	}
@@ -1425,19 +1415,11 @@
 		var variants []string
 		switch proptools.StringDefault(ab.properties.Payload_type, "image") {
 		case "image":
-			// This is the normal case. Note that both image and flattend APEXes are
-			// created. The image type is installed to the system partition, while the
-			// flattened APEX is (optionally) installed to the system_ext partition.
-			// This is mostly for GSI which has to support wide range of devices. If GSI
-			// is installed on a newer (APEX-capable) device, the image APEX in the
-			// system will be used. However, if the same GSI is installed on an old
-			// device which can't support image APEX, the flattened APEX in the
-			// system_ext partion (which still is part of GSI) is used instead.
-			variants = append(variants, imageApexType, flattenedApexType)
+			variants = append(variants, imageApexType)
 		case "zip":
 			variants = append(variants, zipApexType)
 		case "both":
-			variants = append(variants, imageApexType, zipApexType, flattenedApexType)
+			variants = append(variants, imageApexType, zipApexType)
 		default:
 			mctx.PropertyErrorf("payload_type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type)
 			return
@@ -1451,18 +1433,12 @@
 				modules[i].(*apexBundle).properties.ApexType = imageApex
 			case zipApexType:
 				modules[i].(*apexBundle).properties.ApexType = zipApex
-			case flattenedApexType:
-				modules[i].(*apexBundle).properties.ApexType = flattenedApex
-				// See the comment above for why system_ext.
-				if !mctx.Config().FlattenApex() && ab.Platform() {
-					modules[i].(*apexBundle).MakeAsSystemExt()
-				}
 			}
 		}
 	} else if _, ok := mctx.Module().(*OverrideApex); ok {
 		// payload_type is forcibly overridden to "image"
 		// TODO(jiyong): is this the right decision?
-		mctx.CreateVariations(imageApexType, flattenedApexType)
+		mctx.CreateVariations(imageApexType)
 	}
 }
 
@@ -1497,9 +1473,6 @@
 var _ multitree.Exportable = (*apexBundle)(nil)
 
 func (a *apexBundle) Exportable() bool {
-	if a.properties.ApexType == flattenedApex {
-		return false
-	}
 	return true
 }
 
@@ -2143,15 +2116,10 @@
 
 func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) {
 	// Set suffix and primaryApexType depending on the ApexType
-	buildFlattenedAsDefault := ctx.Config().FlattenApex()
 	switch a.properties.ApexType {
 	case imageApex:
-		if buildFlattenedAsDefault {
-			a.suffix = imageApexSuffix
-		} else {
-			a.suffix = ""
-			a.primaryApexType = true
-		}
+		a.suffix = ""
+		a.primaryApexType = true
 	case zipApex:
 		if proptools.String(a.properties.Payload_type) == "zip" {
 			a.suffix = ""
@@ -2159,17 +2127,10 @@
 		} else {
 			a.suffix = zipApexSuffix
 		}
-	case flattenedApex:
-		if buildFlattenedAsDefault {
-			a.suffix = ""
-			a.primaryApexType = true
-		} else {
-			a.suffix = flattenedSuffix
-		}
 	}
 }
 
-func (a apexBundle) isCompressable() bool {
+func (a *apexBundle) isCompressable() bool {
 	return proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex
 }
 
@@ -2668,32 +2629,9 @@
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
 	a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
-	if a.properties.ApexType == flattenedApex {
-		a.buildFlattenedApex(ctx)
-	} else {
-		a.buildUnflattenedApex(ctx)
-	}
+	a.buildApex(ctx)
 	a.buildApexDependencyInfo(ctx)
 	a.buildLintReports(ctx)
-
-	// Append meta-files to the filesInfo list so that they are reflected in Android.mk as well.
-	if a.installable() {
-		// For flattened APEX, make sure that APEX manifest and apex_pubkey are also copied
-		// along with other ordinary files. (Note that this is done by apexer for
-		// non-flattened APEXes)
-		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))
-
-		// Place the public key as apex_pubkey. This is also done by apexer for
-		// non-flattened APEXes case.
-		// TODO(jiyong): Why do we need this CP rule?
-		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Input:  a.publicKeyFile,
-			Output: copiedPubkey,
-		})
-		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
-	}
 }
 
 // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 984ca41..f7a4dea 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3206,7 +3206,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex libc.vendor libm.vendor libdl.vendor\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 libc.vendor libm.vendor libdl.vendor\n")
 }
 
 func TestAndroidMkWritesCommonProperties(t *testing.T) {
@@ -4934,17 +4934,17 @@
 
 func TestApexInVariousPartition(t *testing.T) {
 	testcases := []struct {
-		propName, parition, flattenedPartition string
+		propName, partition string
 	}{
-		{"", "system", "system_ext"},
-		{"product_specific: true", "product", "product"},
-		{"soc_specific: true", "vendor", "vendor"},
-		{"proprietary: true", "vendor", "vendor"},
-		{"vendor: true", "vendor", "vendor"},
-		{"system_ext_specific: true", "system_ext", "system_ext"},
+		{"", "system"},
+		{"product_specific: true", "product"},
+		{"soc_specific: true", "vendor"},
+		{"proprietary: true", "vendor"},
+		{"vendor: true", "vendor"},
+		{"system_ext_specific: true", "system_ext"},
 	}
 	for _, tc := range testcases {
-		t.Run(tc.propName+":"+tc.parition, func(t *testing.T) {
+		t.Run(tc.propName+":"+tc.partition, func(t *testing.T) {
 			ctx := testApex(t, `
 				apex {
 					name: "myapex",
@@ -4961,18 +4961,11 @@
 			`)
 
 			apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-			expected := "out/soong/target/product/test_device/" + tc.parition + "/apex"
+			expected := "out/soong/target/product/test_device/" + tc.partition + "/apex"
 			actual := apex.installDir.RelativeToTop().String()
 			if actual != expected {
 				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
 			}
-
-			flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-			expected = "out/soong/target/product/test_device/" + tc.flattenedPartition + "/apex"
-			actual = flattened.installDir.RelativeToTop().String()
-			if actual != expected {
-				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
-			}
 		})
 	}
 }
@@ -5594,6 +5587,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5671,6 +5665,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
 		`)
 
 		myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module()
@@ -5765,6 +5760,28 @@
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
 		bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+			bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
 		prebuilt_apex {
 			name: "myapex",
 			arch: {
@@ -5780,6 +5797,7 @@
 
 		prebuilt_bootclasspath_fragment {
 			name: "my-bootclasspath-fragment",
+			prefer: true,
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
 			hidden_api: {
@@ -5804,6 +5822,7 @@
 			name: "libfoo",
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
+			installable: true,
 		}
 
 		java_sdk_library_import {
@@ -5822,6 +5841,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
+			compile_dex: true,
 		}
 	`
 
@@ -5834,6 +5854,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5842,8 +5863,8 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			java_libs: ["libfoo", "libbar"],
 			updatable: false,
+			bootclasspath_fragments: ["my-bootclasspath-fragment"],
 		}
 
 		apex_key {
@@ -5852,6 +5873,15 @@
 			private_key: "testkey.pem",
 		}
 
+		bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
 		prebuilt_apex {
 			name: "myapex",
 			arch: {
@@ -5890,6 +5920,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 			permitted_packages: ["foo"],
+			installable: true,
 		}
 
 		java_sdk_library_import {
@@ -5907,6 +5938,7 @@
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
 			permitted_packages: ["bar"],
+			compile_dex: true,
 		}
 	`
 
@@ -5917,8 +5949,9 @@
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
-			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+			out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5928,7 +5961,7 @@
 			name: "myapex",
 			enabled: false,
 			key: "myapex.key",
-			java_libs: ["libfoo", "libbar"],
+			bootclasspath_fragments: ["my-bootclasspath-fragment"],
 		}
 
 		apex_key {
@@ -5937,6 +5970,16 @@
 			private_key: "testkey.pem",
 		}
 
+		bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			enabled: false,
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
 		prebuilt_apex {
 			name: "myapex",
 			arch: {
@@ -5966,7 +6009,6 @@
 
 		java_import {
 			name: "libfoo",
-			prefer: true,
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
 			permitted_packages: ["foo"],
@@ -5974,13 +6016,14 @@
 
 		java_library {
 			name: "libfoo",
+			enabled: false,
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
+			installable: true,
 		}
 
 		java_sdk_library_import {
 			name: "libbar",
-			prefer: true,
 			public: {
 				jars: ["libbar.jar"],
 			},
@@ -5991,9 +6034,11 @@
 
 		java_sdk_library {
 			name: "libbar",
+			enabled: false,
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
+			compile_dex: true,
 		}
 	`
 
@@ -6006,6 +6051,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
 		`)
 	})
 }
@@ -6110,16 +6156,7 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
-
-	flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-	data = android.AndroidMkDataForTest(t, ctx, flatBundle)
-	data.Custom(&builder, name, prefix, "", data)
-	flatAndroidMk := builder.String()
-	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n")
-	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :testdata/baz\n")
 }
 
 func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
@@ -7169,13 +7206,11 @@
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
 
@@ -7758,7 +7793,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex a b\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 a b\n")
 	ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES := c d\n")
 	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES := e f\n")
 }
@@ -7966,7 +8001,7 @@
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
 	// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64\n")
 }
 
 func TestApexWithJniLibs(t *testing.T) {
@@ -8181,126 +8216,6 @@
 	android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
 }
 
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) {
-	t.Helper()
-
-	bp := `
-		java_library {
-			name: "some-updatable-apex-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			apex_available: [
-				"some-updatable-apex",
-			],
-			permitted_packages: ["some.updatable.apex.lib"],
-			min_sdk_version: "33",
-		}
-
-		java_library {
-			name: "some-non-updatable-apex-lib",
-			srcs: ["a.java"],
-			apex_available: [
-				"some-non-updatable-apex",
-			],
-			compile_dex: true,
-			permitted_packages: ["some.non.updatable.apex.lib"],
-		}
-
-		bootclasspath_fragment {
-			name: "some-non-updatable-fragment",
-			contents: ["some-non-updatable-apex-lib"],
-			apex_available: [
-				"some-non-updatable-apex",
-			],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-
-		java_library {
-			name: "some-platform-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			installable: true,
-		}
-
-		java_library {
-			name: "some-art-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			apex_available: [
-				"com.android.art.debug",
-			],
-			hostdex: true,
-			compile_dex: true,
-			min_sdk_version: "33",
-		}
-
-		apex {
-			name: "some-updatable-apex",
-			key: "some-updatable-apex.key",
-			java_libs: ["some-updatable-apex-lib"],
-			updatable: true,
-			min_sdk_version: "33",
-		}
-
-		apex {
-			name: "some-non-updatable-apex",
-			key: "some-non-updatable-apex.key",
-			bootclasspath_fragments: ["some-non-updatable-fragment"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "some-updatable-apex.key",
-		}
-
-		apex_key {
-			name: "some-non-updatable-apex.key",
-		}
-
-		apex {
-			name: "com.android.art.debug",
-			key: "com.android.art.debug.key",
-			bootclasspath_fragments: ["art-bootclasspath-fragment"],
-			updatable: true,
-			min_sdk_version: "33",
-		}
-
-		bootclasspath_fragment {
-			name: "art-bootclasspath-fragment",
-			image_name: "art",
-			contents: ["some-art-lib"],
-			apex_available: [
-				"com.android.art.debug",
-			],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-
-		apex_key {
-			name: "com.android.art.debug.key",
-		}
-
-		filegroup {
-			name: "some-updatable-apex-file_contexts",
-			srcs: [
-				"system/sepolicy/apex/some-updatable-apex-file_contexts",
-			],
-		}
-
-		filegroup {
-			name: "some-non-updatable-apex-file_contexts",
-			srcs: [
-				"system/sepolicy/apex/some-non-updatable-apex-file_contexts",
-			],
-		}
-	`
-
-	testDexpreoptWithApexes(t, bp, errmsg, preparer, fragments...)
-}
-
 func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) *android.TestContext {
 	t.Helper()
 
@@ -8324,7 +8239,7 @@
 	result := android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
 		java.PrepareForTestWithHiddenApiBuildComponents,
-		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithDexpreopt,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		PrepareForTestWithApexBuildComponents,
 		preparer,
@@ -8339,12 +8254,16 @@
 					platform_bootclasspath {
 						name: "platform-bootclasspath",
 						fragments: [
+							{apex: "com.android.art", module: "art-bootclasspath-fragment"},
   						%s
 						],
 					}
 				`, insert))
 			}
 		}),
+		// Dexpreopt for boot jars requires the ART boot image profile.
+		java.PrepareApexBootJarModule("com.android.art", "core-oj"),
+		dexpreopt.FixtureSetArtBootJars("com.android.art:core-oj"),
 		dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),
 	).
 		ExtendWithErrorHandler(errorHandler).
@@ -8651,126 +8570,6 @@
 	)
 }
 
-func TestNoUpdatableJarsInBootImage(t *testing.T) {
-	// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
-	// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
-	// modules to be included in the BootJars.
-	prepareSetBootJars := func(bootJars ...string) android.FixturePreparer {
-		return android.GroupFixturePreparers(
-			dexpreopt.FixtureSetBootJars(bootJars...),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
-			}),
-		)
-	}
-
-	// Set the ArtApexJars and BootJars in dexpreopt.GlobalConfig and productVariables all to the
-	// same value. This can result in an invalid configuration as it allows non art apex jars to be
-	// specified in the ArtApexJars configuration.
-	prepareSetArtJars := func(bootJars ...string) android.FixturePreparer {
-		return android.GroupFixturePreparers(
-			dexpreopt.FixtureSetArtBootJars(bootJars...),
-			dexpreopt.FixtureSetBootJars(bootJars...),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
-			}),
-		)
-	}
-
-	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
-		preparer := android.GroupFixturePreparers(
-			java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib"),
-			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
-		)
-		fragments := []java.ApexVariantReference{
-			{
-				Apex:   proptools.StringPtr("com.android.art.debug"),
-				Module: proptools.StringPtr("art-bootclasspath-fragment"),
-			},
-			{
-				Apex:   proptools.StringPtr("some-non-updatable-apex"),
-				Module: proptools.StringPtr("some-non-updatable-fragment"),
-			},
-		}
-		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
-	})
-
-	t.Run("updatable jar from ART apex in the platform bootclasspath => error", func(t *testing.T) {
-		err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the platform bootclasspath`
-		// Update the dexpreopt BootJars directly.
-		preparer := android.GroupFixturePreparers(
-			prepareSetBootJars("com.android.art.debug:some-art-lib"),
-			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
-		)
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err := `ArtApexJars expects this to be in apex "some-updatable-apex" but this is only in apexes.*"com.android.art.debug"`
-		// Update the dexpreopt ArtApexJars directly.
-		preparer := prepareSetArtJars("some-updatable-apex:some-updatable-apex-lib")
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err := `ArtApexJars expects this to be in apex "some-non-updatable-apex" but this is only in apexes.*"com.android.art.debug"`
-		// Update the dexpreopt ArtApexJars directly.
-		preparer := prepareSetArtJars("some-non-updatable-apex:some-non-updatable-apex-lib")
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("updatable jar from some other apex in the platform bootclasspath => error", func(t *testing.T) {
-		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the platform bootclasspath`
-		preparer := android.GroupFixturePreparers(
-			java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib"),
-			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
-		)
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("non-updatable jar from some other apex in the platform bootclasspath => ok", func(t *testing.T) {
-		preparer := java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
-		fragment := java.ApexVariantReference{
-			Apex:   proptools.StringPtr("some-non-updatable-apex"),
-			Module: proptools.StringPtr("some-non-updatable-fragment"),
-		}
-		testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
-	})
-
-	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
-		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
-		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("nonexistent jar in the platform bootclasspath => error", func(t *testing.T) {
-		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
-		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
-		err := `ArtApexJars is invalid as it requests a platform variant of "some-platform-lib"`
-		// Update the dexpreopt ArtApexJars directly.
-		preparer := prepareSetArtJars("platform:some-platform-lib")
-		testNoUpdatableJarsInBootImage(t, err, preparer)
-	})
-
-	t.Run("platform jar in the platform bootclasspath => ok", func(t *testing.T) {
-		preparer := android.GroupFixturePreparers(
-			java.FixtureConfigureBootJars("platform:some-platform-lib"),
-			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
-		)
-		fragments := []java.ApexVariantReference{
-			{
-				Apex:   proptools.StringPtr("some-non-updatable-apex"),
-				Module: proptools.StringPtr("some-non-updatable-fragment"),
-			},
-		}
-		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
-	})
-}
-
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
 	preparer := java.FixtureConfigureApexBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
@@ -9295,7 +9094,7 @@
 
 	apexKeysText := ctx.SingletonForTests("apex_keys_text")
 	content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"]
-	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system_ext" sign_tool="sign_myapex"`)
+	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`)
 }
 
 func TestApexKeysTxtOverrides(t *testing.T) {
@@ -9518,7 +9317,7 @@
 
 	// The make level dependency needs to be on otherlib - prebuilt_otherlib isn't
 	// a thing there.
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex otherlib\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 otherlib\n")
 }
 
 func TestExcludeDependency(t *testing.T) {
@@ -9912,7 +9711,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
 }
 
 func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) {
@@ -9988,7 +9787,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex otherapex")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex otherapex")
 }
 
 func TestAndroidMk_RequiredDeps(t *testing.T) {
@@ -10012,15 +9811,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
-
-	flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-	flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo")
-	flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
-	var flattenedBuilder strings.Builder
-	flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
-	flattenedAndroidMk := flattenedBuilder.String()
-	ensureContains(t, flattenedAndroidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex.flattened apex_pubkey.myapex.flattened foo\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo\n")
 }
 
 func TestApexOutputFileProducer(t *testing.T) {
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 1b52886..7be8477 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -46,78 +46,6 @@
 	dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),
 )
 
-func TestBootclasspathFragments(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForTestWithBootclasspathFragment,
-		// Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"),
-		prepareForTestWithArtApex,
-
-		java.PrepareForTestWithJavaSdkLibraryFiles,
-		java.FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["b.java"],
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java"],
-			installable: true,
-		}
-
-		apex {
-			name: "com.android.art",
-			key: "com.android.art.key",
-			bootclasspath_fragments: ["art-bootclasspath-fragment"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "com.android.art.key",
-			public_key: "com.android.art.avbpubkey",
-			private_key: "com.android.art.pem",
-		}
-
-		java_library {
-			name: "baz",
-			apex_available: [
-				"com.android.art",
-			],
-			srcs: ["b.java"],
-			compile_dex: true,
-		}
-
-		java_library {
-			name: "quuz",
-			apex_available: [
-				"com.android.art",
-			],
-			srcs: ["b.java"],
-			compile_dex: true,
-		}
-
-		bootclasspath_fragment {
-			name: "art-bootclasspath-fragment",
-			image_name: "art",
-			// Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
-			contents: ["baz", "quuz"],
-			apex_available: [
-				"com.android.art",
-			],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-`,
-	)
-
-	// Make sure that the art-bootclasspath-fragment is using the correct configuration.
-	checkBootclasspathFragment(t, result, "art-bootclasspath-fragment", "android_common_apex10000",
-		"com.android.art:baz,com.android.art:quuz")
-}
-
 func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
@@ -248,16 +176,6 @@
 	checkAPIScopeStubs("other", otherInfo, java.CorePlatformHiddenAPIScope)
 }
 
-func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string) {
-	t.Helper()
-
-	bootclasspathFragment := result.ModuleForTests(moduleName, variantName).Module().(*java.BootclasspathFragmentModule)
-
-	bootclasspathFragmentInfo := result.ModuleProvider(bootclasspathFragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
-	modules := bootclasspathFragmentInfo.Modules()
-	android.AssertStringEquals(t, "invalid modules for "+moduleName, expectedConfiguredModules, modules.String())
-}
-
 func TestBootclasspathFragmentInArtApex(t *testing.T) {
 	commonPreparer := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
@@ -268,10 +186,10 @@
 			name: "com.android.art",
 			key: "com.android.art.key",
 			bootclasspath_fragments: [
-				"mybootclasspathfragment",
+				"art-bootclasspath-fragment",
 			],
 			// bar (like foo) should be transitively included in this apex because it is part of the
-			// mybootclasspathfragment bootclasspath_fragment.
+			// art-bootclasspath-fragment bootclasspath_fragment.
 			updatable: false,
 		}
 
@@ -280,42 +198,6 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
-
-		java_library {
-			name: "foo",
-			srcs: ["b.java"],
-			installable: true,
-			apex_available: [
-				"com.android.art",
-			],
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java"],
-			installable: true,
-			apex_available: [
-				"com.android.art",
-			],
-		}
-
-		java_import {
-			name: "foo",
-			jars: ["foo.jar"],
-			apex_available: [
-				"com.android.art",
-			],
-			compile_dex: true,
-		}
-
-		java_import {
-			name: "bar",
-			jars: ["bar.jar"],
-			apex_available: [
-				"com.android.art",
-			],
-			compile_dex: true,
-		}
 	`),
 	)
 
@@ -330,7 +212,7 @@
 	addSource := func(contents ...string) android.FixturePreparer {
 		text := fmt.Sprintf(`
 			bootclasspath_fragment {
-				name: "mybootclasspathfragment",
+				name: "art-bootclasspath-fragment",
 				image_name: "art",
 				%s
 				apex_available: [
@@ -342,6 +224,19 @@
 			}
 		`, contentsInsert(contents))
 
+		for _, content := range contents {
+			text += fmt.Sprintf(`
+				java_library {
+					name: "%[1]s",
+					srcs: ["%[1]s.java"],
+					installable: true,
+					apex_available: [
+						"com.android.art",
+					],
+				}
+			`, content)
+		}
+
 		return android.FixtureAddTextFile("art/build/boot/Android.bp", text)
 	}
 
@@ -357,11 +252,11 @@
 						src: "com.android.art-arm.apex",
 					},
 				},
-				exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+				exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			}
 
 			prebuilt_bootclasspath_fragment {
-				name: "mybootclasspathfragment",
+				name: "art-bootclasspath-fragment",
 				image_name: "art",
 				%s
 				prefer: %t,
@@ -369,14 +264,29 @@
 					"com.android.art",
 				],
 				hidden_api: {
-					annotation_flags: "mybootclasspathfragment/annotation-flags.csv",
-					metadata: "mybootclasspathfragment/metadata.csv",
-					index: "mybootclasspathfragment/index.csv",
-					stub_flags: "mybootclasspathfragment/stub-flags.csv",
-					all_flags: "mybootclasspathfragment/all-flags.csv",
+					annotation_flags: "hiddenapi/annotation-flags.csv",
+					metadata: "hiddenapi/metadata.csv",
+					index: "hiddenapi/index.csv",
+					stub_flags: "hiddenapi/stub-flags.csv",
+					all_flags: "hiddenapi/all-flags.csv",
 				},
 			}
 		`, contentsInsert(contents), prefer)
+
+		for _, content := range contents {
+			text += fmt.Sprintf(`
+				java_import {
+					name: "%[1]s",
+					prefer: %[2]t,
+					jars: ["%[1]s.jar"],
+					apex_available: [
+						"com.android.art",
+					],
+					compile_dex: true,
+				}
+			`, content, prefer)
+		}
+
 		return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
 	}
 
@@ -399,13 +309,13 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
-			`mybootclasspathfragment`,
 		})
 
 		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
-		module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+		module := result.ModuleForTests("art-bootclasspath-fragment", "android_common_apex10000")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
@@ -469,14 +379,14 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
-			`mybootclasspathfragment`,
 			`prebuilt_com.android.art`,
 		})
 
 		// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
-		module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
+		module := result.ModuleForTests("prebuilt_art-bootclasspath-fragment", "android_common_com.android.art")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
@@ -566,7 +476,7 @@
 					src: "com.android.art-arm.apex",
 				},
 			},
-			exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
 
 		java_import {
@@ -586,7 +496,7 @@
 		}
 
 		prebuilt_bootclasspath_fragment {
-			name: "mybootclasspathfragment",
+			name: "art-bootclasspath-fragment",
 			image_name: "art",
 			// Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above.
 			contents: ["foo", "bar"],
@@ -594,11 +504,11 @@
 				"com.android.art",
 			],
 			hidden_api: {
-				annotation_flags: "mybootclasspathfragment/annotation-flags.csv",
-				metadata: "mybootclasspathfragment/metadata.csv",
-				index: "mybootclasspathfragment/index.csv",
-				stub_flags: "mybootclasspathfragment/stub-flags.csv",
-				all_flags: "mybootclasspathfragment/all-flags.csv",
+				annotation_flags: "hiddenapi/annotation-flags.csv",
+				metadata: "hiddenapi/metadata.csv",
+				index: "hiddenapi/index.csv",
+				stub_flags: "hiddenapi/stub-flags.csv",
+				all_flags: "hiddenapi/all-flags.csv",
 			},
 		}
 
@@ -608,7 +518,7 @@
 			apex_name: "com.android.art",
 			%s
 			src: "com.mycompany.android.art.apex",
-			exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
 	`
 
@@ -617,17 +527,17 @@
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
 			`com.android.art.apex.selector`,
-			`prebuilt_mybootclasspathfragment`,
+			`prebuilt_art-bootclasspath-fragment`,
 		})
 
-		java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
 			`com.android.art.deapexer`,
 			`dex2oatd`,
 			`prebuilt_bar`,
 			`prebuilt_foo`,
 		})
 
-		module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
+		module := result.ModuleForTests("art-bootclasspath-fragment", "android_common_com.android.art")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
diff --git a/apex/builder.go b/apex/builder.go
index a2fe2a0..db66a72 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -382,23 +382,6 @@
 			rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(forceLabel).Text(">>").Output(output)
 			rule.Command().Text("echo").Text("/").Text(forceLabel).Text(">>").Output(output)
 		}
-	case flattenedApex:
-		// For flattened apexes, install path should be prepended.
-		// File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS
-		// so that it can be merged into file_contexts.bin
-		apexPath := android.InstallPathToOnDevicePath(ctx, a.installDir.Join(ctx, a.Name()))
-		apexPath = strings.ReplaceAll(apexPath, ".", `\\.`)
-		// remove old file
-		rule.Command().Text("rm").FlagWithOutput("-f ", output)
-		// copy file_contexts
-		rule.Command().Text("awk").Text(`'/object_r/{printf("` + apexPath + `%s\n", $0)}'`).Input(fileContexts).Text(">").Output(output)
-		// new line
-		rule.Command().Text("echo").Text(">>").Output(output)
-		if !useFileContextsAsIs {
-			// force-label /apex_manifest.pb and /
-			rule.Command().Text("echo").Text(apexPath + "/apex_manifest\\\\.pb").Text(forceLabel).Text(">>").Output(output)
-			rule.Command().Text("echo").Text(apexPath + "/").Text(forceLabel).Text(">>").Output(output)
-		}
 	default:
 		panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
 	}
@@ -479,8 +462,8 @@
 	})
 }
 
-// buildUnflattendApex creates build rules to build an APEX using apexer.
-func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
+// buildApex creates build rules to build an APEX using apexer.
+func (a *apexBundle) buildApex(ctx android.ModuleContext) {
 	apexType := a.properties.ApexType
 	suffix := apexType.suffix()
 	apexName := a.BaseModuleName()
@@ -963,49 +946,6 @@
 	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
 }
 
-// buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a
-// single output file. It is a phony target for all the files under /system/apex/<name> directory.
-// This function creates the installation rules for the files.
-func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
-	bundleName := a.Name()
-	installedSymlinks := append(android.InstallPaths(nil), a.compatSymlinks...)
-	if a.installable() {
-		for _, fi := range a.filesInfo {
-			dir := filepath.Join("apex", bundleName, fi.installDir)
-			installDir := android.PathForModuleInstall(ctx, dir)
-			if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
-				pathOnDevice := filepath.Join("/", fi.partition, fi.path())
-				installedSymlinks = append(installedSymlinks,
-					ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice))
-			} else {
-				if fi.class == appSet {
-					as := fi.module.(*java.AndroidAppSet)
-					ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk",
-						as.OutputFile(), as.PackedAdditionalOutputs())
-				} else {
-					target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile)
-					for _, sym := range fi.symlinks {
-						installedSymlinks = append(installedSymlinks,
-							ctx.InstallSymlink(installDir, sym, target))
-					}
-				}
-			}
-		}
-
-		// Create install rules for the files added in GenerateAndroidBuildActions after
-		// buildFlattenedApex is called.  Add the links to system libs (if any) as dependencies
-		// of the apex_manifest.pb file since it is always present.
-		dir := filepath.Join("apex", bundleName)
-		installDir := android.PathForModuleInstall(ctx, dir)
-		ctx.InstallFile(installDir, "apex_manifest.pb", a.manifestPbOut, installedSymlinks.Paths()...)
-		ctx.InstallFile(installDir, "apex_pubkey", a.publicKeyFile)
-	}
-
-	a.fileContexts = a.buildFileContexts(ctx)
-
-	a.outputFile = android.PathForModuleInstall(ctx, "apex", bundleName)
-}
-
 // getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign
 // the zip container of this APEX. See the description of the 'certificate' property for how
 // the cert and the private key are found.
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index bba8bb6..ff8f912 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -155,15 +155,15 @@
 }
 
 func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) {
-	ruleFile := "boot.art"
+	ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
 
 	expectedInputs := []string{
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
-		"out/soong/dexpreopt_arm64/dex_artjars/boot.prof",
-		"out/soong/dexpreopt_arm64/dex_bootjars/boot.prof",
+		"out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art/boot.prof",
+		"out/soong/.intermediates/platform-bootclasspath/android_common/boot/boot.prof",
 	}
 
 	expectedOutputs := []string{
@@ -192,7 +192,7 @@
 // The only difference is that the ART profile should be deapexed from the prebuilt APEX. Other
 // inputs and outputs should be the same as above.
 func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) {
-	ruleFile := "boot.art"
+	ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
 
 	expectedInputs := []string{
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar",
@@ -200,7 +200,7 @@
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
 		"out/soong/.intermediates/com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
-		"out/soong/dexpreopt_arm64/dex_bootjars/boot.prof",
+		"out/soong/.intermediates/platform-bootclasspath/android_common/boot/boot.prof",
 	}
 
 	expectedOutputs := []string{
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 0d83830..3509e6c 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -133,9 +133,7 @@
 }
 
 func (p *prebuiltCommon) checkForceDisable(ctx android.ModuleContext) bool {
-	// If the device is configured to use flattened APEX, force disable the prebuilt because
-	// the prebuilt is a non-flattened one.
-	forceDisable := ctx.Config().FlattenApex()
+	forceDisable := false
 
 	// Force disable the prebuilts when we are doing unbundled build. We do unbundled build
 	// to build the prebuilts themselves.
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 4d39e8f..480158c 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -453,8 +453,32 @@
 	return hashes, nil
 }
 
+// escapes the args received from aquery and creates a command string
+func commandString(actionEntry *analysis_v2_proto.Action) string {
+	switch actionEntry.Mnemonic {
+	case "GoCompilePkg":
+		argsEscaped := []string{}
+		for _, arg := range actionEntry.Arguments {
+			if arg == "" {
+				// If this is an empty string, add ''
+				// And not
+				// 1. (literal empty)
+				// 2. `''\'''\'''` (escaped version of '')
+				//
+				// If we had used (1), then this would appear as a whitespace when we strings.Join
+				argsEscaped = append(argsEscaped, "''")
+			} else {
+				argsEscaped = append(argsEscaped, proptools.ShellEscapeIncludingSpaces(arg))
+			}
+		}
+		return strings.Join(argsEscaped, " ")
+	default:
+		return strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
+	}
+}
+
 func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
+	command := commandString(actionEntry)
 	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
 		return nil, err
@@ -653,7 +677,7 @@
 		if len(actionEntry.Arguments) < 1 {
 			return a.templateExpandActionBuildStatement(actionEntry)
 		}
-	case "FileWrite", "SourceSymlinkManifest":
+	case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest":
 		return a.fileWriteActionBuildStatement(actionEntry)
 	case "SymlinkTree":
 		return a.symlinkTreeActionBuildStatement(actionEntry)
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 782a88c..f889693 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -19,6 +19,7 @@
         "testing.go",
     ],
     deps: [
+        "blueprint-bootstrap",
         "soong-aidl-library",
         "soong-android",
         "soong-android-allowlists",
@@ -37,6 +38,7 @@
         "soong-ui-metrics",
     ],
     testSrcs: [
+        "go_conversion_test.go",
         "aar_conversion_test.go",
         "aidl_library_conversion_test.go",
         "android_app_certificate_conversion_test.go",
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 7f7aa6a..d1b4d40 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -349,7 +349,7 @@
 		}})
 }
 
-func TestAndroidAppMinSdkProvided(t *testing.T) {
+func TestAndroidAppManifestSdkVersionsProvided(t *testing.T) {
 	runAndroidAppTestCase(t, Bp2buildTestCase{
 		Description:                "Android app with value for min_sdk_version",
 		ModuleTypeUnderTest:        "android_app",
@@ -359,7 +359,9 @@
 android_app {
         name: "foo",
         sdk_version: "current",
-				min_sdk_version: "24",
+        min_sdk_version: "24",
+        max_sdk_version: "30",
+        target_sdk_version: "29",
 }
 `,
 		ExpectedBazelTargets: []string{
@@ -367,14 +369,16 @@
 				"manifest":       `"AndroidManifest.xml"`,
 				"resource_files": `[]`,
 				"manifest_values": `{
+        "maxSdkVersion": "30",
         "minSdkVersion": "24",
+        "targetSdkVersion": "29",
     }`,
 				"sdk_version": `"current"`,
 			}),
 		}})
 }
 
-func TestAndroidAppMinSdkDefaultToSdkVersion(t *testing.T) {
+func TestAndroidAppMinAndTargetSdkDefaultToSdkVersion(t *testing.T) {
 	runAndroidAppTestCase(t, Bp2buildTestCase{
 		Description:                "Android app with value for sdk_version",
 		ModuleTypeUnderTest:        "android_app",
@@ -392,6 +396,7 @@
 				"resource_files": `[]`,
 				"manifest_values": `{
         "minSdkVersion": "30",
+        "targetSdkVersion": "30",
     }`,
 				"sdk_version": `"30"`,
 			}),
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 46a5bd8..a817386 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -30,6 +30,7 @@
 	"android/soong/starlark_fmt"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -252,6 +253,235 @@
 	return r.buildFileToTargets
 }
 
+// struct to store state of go bazel targets
+// this implements bp2buildModule interface and is passed to generateBazelTargets
+type goBazelTarget struct {
+	targetName            string
+	targetPackage         string
+	bazelRuleClass        string
+	bazelRuleLoadLocation string
+	bazelAttributes       []interface{}
+}
+
+var _ bp2buildModule = (*goBazelTarget)(nil)
+
+func (g goBazelTarget) TargetName() string {
+	return g.targetName
+}
+
+func (g goBazelTarget) TargetPackage() string {
+	return g.targetPackage
+}
+
+func (g goBazelTarget) BazelRuleClass() string {
+	return g.bazelRuleClass
+}
+
+func (g goBazelTarget) BazelRuleLoadLocation() string {
+	return g.bazelRuleLoadLocation
+}
+
+func (g goBazelTarget) BazelAttributes() []interface{} {
+	return g.bazelAttributes
+}
+
+// Creates a target_compatible_with entry that is *not* compatible with android
+func targetNotCompatibleWithAndroid() bazel.LabelListAttribute {
+	ret := bazel.LabelListAttribute{}
+	ret.SetSelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid,
+		bazel.MakeLabelList(
+			[]bazel.Label{
+				bazel.Label{
+					Label: "@platforms//:incompatible",
+				},
+			},
+		),
+	)
+	return ret
+}
+
+// helper function to return labels for srcs used in bootstrap_go_package and bootstrap_go_binary
+// this function has the following limitations which make it unsuitable for widespread use
+// - wildcard patterns in srcs
+// This is ok for go since build/blueprint does not support it.
+//
+// Prefer to use `BazelLabelForModuleSrc` instead
+func goSrcLabels(cfg android.Config, moduleDir string, srcs []string, linuxSrcs, darwinSrcs []string) bazel.LabelListAttribute {
+	labels := func(srcs []string) bazel.LabelList {
+		ret := []bazel.Label{}
+		for _, src := range srcs {
+			srcLabel := bazel.Label{
+				Label: src,
+			}
+			ret = append(ret, srcLabel)
+		}
+		// Respect package boundaries
+		return android.TransformSubpackagePaths(
+			cfg,
+			moduleDir,
+			bazel.MakeLabelList(ret),
+		)
+	}
+
+	ret := bazel.LabelListAttribute{}
+	// common
+	ret.SetSelectValue(bazel.NoConfigAxis, "", labels(srcs))
+	// linux
+	ret.SetSelectValue(bazel.OsConfigurationAxis, bazel.OsLinux, labels(linuxSrcs))
+	// darwin
+	ret.SetSelectValue(bazel.OsConfigurationAxis, bazel.OsDarwin, labels(darwinSrcs))
+	return ret
+}
+
+func goDepLabels(deps []string, goModulesMap nameToGoLibraryModule) bazel.LabelListAttribute {
+	labels := []bazel.Label{}
+	for _, dep := range deps {
+		moduleDir := goModulesMap[dep].Dir
+		if moduleDir == "." {
+			moduleDir = ""
+		}
+		label := bazel.Label{
+			Label: fmt.Sprintf("//%s:%s", moduleDir, dep),
+		}
+		labels = append(labels, label)
+	}
+	return bazel.MakeLabelListAttribute(bazel.MakeLabelList(labels))
+}
+
+// attributes common to blueprint_go_binary and bootstap_go_package
+type goAttributes struct {
+	Importpath             bazel.StringAttribute
+	Srcs                   bazel.LabelListAttribute
+	Deps                   bazel.LabelListAttribute
+	Target_compatible_with bazel.LabelListAttribute
+}
+
+func generateBazelTargetsGoPackage(ctx *android.Context, g *bootstrap.GoPackage, goModulesMap nameToGoLibraryModule) ([]BazelTarget, []error) {
+	ca := android.CommonAttributes{
+		Name: g.Name(),
+	}
+
+	// For this bootstrap_go_package dep chain,
+	// A --> B --> C ( ---> depends on)
+	// Soong provides the convenience of only listing B as deps of A even if a src file of A imports C
+	// Bazel OTOH
+	// 1. requires C to be listed in `deps` expllicity.
+	// 2. does not require C to be listed if src of A does not import C
+	//
+	// bp2build does not have sufficient info on whether C is a direct dep of A or not, so for now collect all transitive deps and add them to deps
+	transitiveDeps := transitiveGoDeps(g.Deps(), goModulesMap)
+
+	ga := goAttributes{
+		Importpath: bazel.StringAttribute{
+			Value: proptools.StringPtr(g.GoPkgPath()),
+		},
+		Srcs: goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
+		Deps: goDepLabels(
+			android.FirstUniqueStrings(transitiveDeps),
+			goModulesMap,
+		),
+		Target_compatible_with: targetNotCompatibleWithAndroid(),
+	}
+
+	lib := goBazelTarget{
+		targetName:            g.Name(),
+		targetPackage:         ctx.ModuleDir(g),
+		bazelRuleClass:        "go_library",
+		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+		bazelAttributes:       []interface{}{&ca, &ga},
+	}
+	// TODO - b/284483729: Create go_test target from testSrcs
+	libTarget, err := generateBazelTarget(ctx, lib)
+	if err != nil {
+		return []BazelTarget{}, []error{err}
+	}
+	return []BazelTarget{libTarget}, nil
+}
+
+type goLibraryModule struct {
+	Dir  string
+	Deps []string
+}
+
+type nameToGoLibraryModule map[string]goLibraryModule
+
+// Visit each module in the graph
+// If a module is of type `bootstrap_go_package`, return a map containing metadata like its dir and deps
+func createGoLibraryModuleMap(ctx *android.Context) nameToGoLibraryModule {
+	ret := nameToGoLibraryModule{}
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		moduleType := ctx.ModuleType(m)
+		// We do not need to store information about blueprint_go_binary since it does not have any rdeps
+		if moduleType == "bootstrap_go_package" {
+			ret[m.Name()] = goLibraryModule{
+				Dir:  ctx.ModuleDir(m),
+				Deps: m.(*bootstrap.GoPackage).Deps(),
+			}
+		}
+	})
+	return ret
+}
+
+// Returns the deps in the transitive closure of a go target
+func transitiveGoDeps(directDeps []string, goModulesMap nameToGoLibraryModule) []string {
+	allDeps := directDeps
+	i := 0
+	for i < len(allDeps) {
+		curr := allDeps[i]
+		allDeps = append(allDeps, goModulesMap[curr].Deps...)
+		i += 1
+	}
+	allDeps = android.SortedUniqueStrings(allDeps)
+	return allDeps
+}
+
+func generateBazelTargetsGoBinary(ctx *android.Context, g *bootstrap.GoBinary, goModulesMap nameToGoLibraryModule) ([]BazelTarget, []error) {
+	ca := android.CommonAttributes{
+		Name: g.Name(),
+	}
+
+	// For this bootstrap_go_package dep chain,
+	// A --> B --> C ( ---> depends on)
+	// Soong provides the convenience of only listing B as deps of A even if a src file of A imports C
+	// Bazel OTOH
+	// 1. requires C to be listed in `deps` expllicity.
+	// 2. does not require C to be listed if src of A does not import C
+	//
+	// bp2build does not have sufficient info on whether C is a direct dep of A or not, so for now collect all transitive deps and add them to deps
+	transitiveDeps := transitiveGoDeps(g.Deps(), goModulesMap)
+
+	ga := goAttributes{
+		Srcs:                   goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
+		Deps:                   goDepLabels(transitiveDeps, goModulesMap),
+		Target_compatible_with: targetNotCompatibleWithAndroid(),
+	}
+
+	bin := goBazelTarget{
+		targetName:            g.Name(),
+		targetPackage:         ctx.ModuleDir(g),
+		bazelRuleClass:        "go_binary",
+		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+		bazelAttributes:       []interface{}{&ca, &ga},
+	}
+	// TODO - b/284483729: Create go_test target from testSrcs
+	binTarget, err := generateBazelTarget(ctx, bin)
+	if err != nil {
+		return []BazelTarget{}, []error{err}
+	}
+	return []BazelTarget{binTarget}, nil
+}
+
+var (
+	// TODO - b/284483729: Remove this denyilst
+	// Temporary denylist of go binaries that are currently used in mixed builds
+	// This denylist allows us to rollout bp2build converters for go targets without affecting mixed builds
+	goBinaryDenylist = []string{
+		"soong_zip",
+		"zip2zip",
+		"bazel_notice_gen",
+	}
+)
+
 func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
 	buildFileToTargets := make(map[string]BazelTargets)
 
@@ -262,6 +492,10 @@
 
 	var errs []error
 
+	// Visit go libraries in a pre-run and store its state in a map
+	// The time complexity remains O(N), and this does not add significant wall time.
+	nameToGoLibMap := createGoLibraryModuleMap(ctx.Context())
+
 	bpCtx := ctx.Context()
 	bpCtx.VisitAllModules(func(m blueprint.Module) {
 		dir := bpCtx.ModuleDir(m)
@@ -269,6 +503,7 @@
 		dirs[dir] = true
 
 		var targets []BazelTarget
+		var targetErrs []error
 
 		switch ctx.Mode() {
 		case Bp2Build:
@@ -317,7 +552,6 @@
 						return
 					}
 				}
-				var targetErrs []error
 				targets, targetErrs = generateBazelTargets(bpCtx, aModule)
 				errs = append(errs, targetErrs...)
 				for _, t := range targets {
@@ -336,6 +570,14 @@
 					metrics.AddUnconvertedModule(m, moduleType, dir, *reason)
 				}
 				return
+			} else if glib, ok := m.(*bootstrap.GoPackage); ok {
+				targets, targetErrs = generateBazelTargetsGoPackage(bpCtx, glib, nameToGoLibMap)
+				errs = append(errs, targetErrs...)
+				metrics.IncrementRuleClassCount("go_library")
+			} else if gbin, ok := m.(*bootstrap.GoBinary); ok && !android.InList(m.Name(), goBinaryDenylist) {
+				targets, targetErrs = generateBazelTargetsGoBinary(bpCtx, gbin, nameToGoLibMap)
+				errs = append(errs, targetErrs...)
+				metrics.IncrementRuleClassCount("go_binary")
 			} else {
 				metrics.AddUnconvertedModule(m, moduleType, dir, android.UnconvertedReason{
 					ReasonType: int(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED),
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/bp2build/go_conversion_test.go b/bp2build/go_conversion_test.go
new file mode 100644
index 0000000..507fbf0
--- /dev/null
+++ b/bp2build/go_conversion_test.go
@@ -0,0 +1,150 @@
+// 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 bp2build
+
+import (
+	"testing"
+
+	"github.com/google/blueprint/bootstrap"
+
+	"android/soong/android"
+)
+
+func runGoTests(t *testing.T, tc Bp2buildTestCase) {
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		tCtx := ctx.(*android.TestContext)
+		bootstrap.RegisterGoModuleTypes(tCtx.Context.Context) // android.TestContext --> android.Context --> blueprint.Context
+	}, tc)
+}
+
+func TestConvertGoPackage(t *testing.T) {
+	bp := `
+bootstrap_go_package {
+	name: "foo",
+	pkgPath: "android/foo",
+	deps: [
+		"bar",
+	],
+	srcs: [
+		"foo1.go",
+		"foo2.go",
+	],
+	linux: {
+		srcs: [
+			"foo_linux.go",
+		],
+	},
+	darwin: {
+		srcs: [
+			"foo_darwin.go",
+		],
+	},
+	testSrcs: [
+		"foo1_test.go",
+		"foo2_test.go",
+	],
+}
+`
+	depBp := `
+bootstrap_go_package {
+	name: "bar",
+}
+`
+	t.Parallel()
+	runGoTests(t, Bp2buildTestCase{
+		Description:         "Convert bootstrap_go_package to go_library",
+		ModuleTypeUnderTest: "bootrstap_go_package",
+		Blueprint:           bp,
+		Filesystem: map[string]string{
+			"bar/Android.bp": depBp, // Put dep in Android.bp to reduce boilerplate in ExpectedBazelTargets
+		},
+		ExpectedBazelTargets: []string{makeBazelTargetHostOrDevice("go_library", "foo",
+			AttrNameToString{
+				"deps":       `["//bar:bar"]`,
+				"importpath": `"android/foo"`,
+				"srcs": `[
+        "foo1.go",
+        "foo2.go",
+    ] + select({
+        "//build/bazel/platforms/os:darwin": ["foo_darwin.go"],
+        "//build/bazel/platforms/os:linux_glibc": ["foo_linux.go"],
+        "//conditions:default": [],
+    })`,
+			},
+			android.HostSupported,
+		)},
+	})
+}
+
+func TestConvertGoBinaryWithTransitiveDeps(t *testing.T) {
+	bp := `
+blueprint_go_binary {
+	name: "foo",
+	srcs: ["main.go"],
+	deps: ["bar"],
+}
+`
+	depBp := `
+bootstrap_go_package {
+	name: "bar",
+	deps: ["baz"],
+}
+bootstrap_go_package {
+	name: "baz",
+}
+`
+	t.Parallel()
+	runGoTests(t, Bp2buildTestCase{
+		Description: "Convert blueprint_go_binary to go_binary",
+		Blueprint:   bp,
+		Filesystem: map[string]string{
+			"bar/Android.bp": depBp, // Put dep in Android.bp to reduce boilerplate in ExpectedBazelTargets
+		},
+		ExpectedBazelTargets: []string{makeBazelTargetHostOrDevice("go_binary", "foo",
+			AttrNameToString{
+				"deps": `[
+        "//bar:bar",
+        "//bar:baz",
+    ]`,
+				"srcs": `["main.go"]`,
+			},
+			android.HostSupported,
+		)},
+	})
+}
+
+func TestConvertGoBinaryWithSrcInDifferentPackage(t *testing.T) {
+	bp := `
+blueprint_go_binary {
+	name: "foo",
+	srcs: ["subdir/main.go"],
+}
+`
+	t.Parallel()
+	runGoTests(t, Bp2buildTestCase{
+		Description: "Convert blueprint_go_binary with src in different package",
+		Blueprint:   bp,
+		Filesystem: map[string]string{
+			"subdir/Android.bp": "",
+		},
+		ExpectedBazelTargets: []string{makeBazelTargetHostOrDevice("go_binary", "foo",
+			AttrNameToString{
+				"deps": `[]`,
+				"srcs": `["//subdir:main.go"]`,
+			},
+			android.HostSupported,
+		)},
+	})
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index eeef7d4..4c42048 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -35,8 +35,16 @@
 # sufficiently many files were generated.
 declare -r out="${OUT_DIR:-out}"
 
-# Build extraction files for C++ and Java. Build `merge_zips` which we use later.
-build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java xref_rust
+# Build extraction files and `merge_zips` which we use later.
+kzip_targets=(
+  merge_zips
+  xref_cxx
+  xref_java
+  # TODO: b/286390153 - reenable rust
+  # xref_rust
+)
+
+build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k --skip-soong-tests --ninja_weight_source=not_used "${kzip_targets[@]}"
 
 # Build extraction file for Go the files in build/{blueprint,soong} directories.
 declare -r abspath_out=$(realpath "${out}")
@@ -63,10 +71,9 @@
 set +e
 
 declare -r kzip_count=$(find "$out" -name '*.kzip' | wc -l)
-(($kzip_count>100000)) || { printf "Too few kzip files were generated: %d\n" $kzip_count; exit 1; }
+(($kzip_count>100000)) || { >&2 printf "ERROR: Too few kzip files were generated: %d\n" $kzip_count; exit 1; }
 
 # Pack
-# TODO(asmundak): this should be done by soong.
 declare -r allkzip="$KZIP_NAME.kzip"
 "$out/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
 
diff --git a/cc/afdo.go b/cc/afdo.go
index 137ea97..bc7cd52 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -34,7 +34,8 @@
 
 var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
 
-const afdoCFlagsFormat = "-fprofile-sample-use=%s"
+// This flag needs to be in both CFlags and LdFlags to ensure correct symbol ordering
+const afdoFlagsFormat = "-fprofile-sample-use=%s"
 
 func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
 	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
@@ -86,7 +87,7 @@
 	}
 	if path := afdo.Properties.FdoProfilePath; path != nil {
 		// The flags are prepended to allow overriding.
-		profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path)
+		profileUseFlag := fmt.Sprintf(afdoFlagsFormat, *path)
 		flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...)
 		flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...)
 
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 d67f3ad..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"
 
@@ -151,7 +152,7 @@
 	StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
 
 	// Transitive static library dependencies of static libraries for use in ordering.
-	TranstiveStaticLibrariesForOrdering *android.DepSet
+	TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 
 	// Paths to .o files
 	Objs Objects
@@ -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
@@ -1968,12 +1997,10 @@
 func GetApexConfigKey(ctx android.BaseModuleContext) *android.ApexConfigKey {
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
-		if !ctx.Config().BazelContext.IsModuleDclaAllowed(ctx.Module().Name()) {
-			return nil
-		}
 		apexKey := android.ApexConfigKey{
 			WithinApex:     true,
 			ApexSdkVersion: findApexSdkVersion(ctx, apexInfo).String(),
+			ApiDomain:      findApiDomain(apexInfo),
 		}
 		return &apexKey
 	}
@@ -1981,6 +2008,30 @@
 	return nil
 }
 
+// Returns the api domain of a module for an apexInfo group
+// Input:
+// ai.InApexModules: [com.android.foo, test_com.android.foo, com.google.android.foo]
+// Return:
+// com.android.foo
+
+// If a module is included in multiple api domains (collated by min_sdk_version), it will return
+// the first match. The other matches have the same build actions since they share a min_sdk_version, so returning
+// the first match is fine.
+func findApiDomain(ai android.ApexInfo) string {
+	// Remove any test apexes
+	matches, _ := android.FilterList(ai.InApexModules, ai.TestApexes)
+	// Remove any google apexes. Rely on naming convention.
+	pred := func(s string) bool { return !strings.HasPrefix(s, "com.google") }
+	matches = android.FilterListPred(matches, pred)
+	if len(matches) > 0 {
+		// Return the first match
+		return android.SortedUniqueStrings(matches)[0]
+	} else {
+		// No apex in the tree has a dependency on this module
+		return ""
+	}
+}
+
 func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
 	bazelModuleLabel := c.getBazelModuleLabel(ctx)
 	c.bazelHandler.ProcessBazelQueryResponse(ctx, bazelModuleLabel)
@@ -3550,8 +3601,8 @@
 // to match the topological order of the dependency tree, including any static analogues of
 // direct shared libraries.  It returns the ordered static dependencies, and an android.DepSet
 // of the transitive dependencies.
-func orderStaticModuleDeps(staticDeps []StaticLibraryInfo, sharedDeps []SharedLibraryInfo) (ordered android.Paths, transitive *android.DepSet) {
-	transitiveStaticLibsBuilder := android.NewDepSetBuilder(android.TOPOLOGICAL)
+func orderStaticModuleDeps(staticDeps []StaticLibraryInfo, sharedDeps []SharedLibraryInfo) (ordered android.Paths, transitive *android.DepSet[android.Path]) {
+	transitiveStaticLibsBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL)
 	var staticPaths android.Paths
 	for _, staticDep := range staticDeps {
 		staticPaths = append(staticPaths, staticDep.StaticLibrary)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 859059e..fe54463 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2700,8 +2700,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
-		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
+	actual := android.Paths(ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+		TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -2736,8 +2736,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
-		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
+	actual := android.Paths(ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+		TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -4979,6 +4979,7 @@
 
 	conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
 	cppOnly := []string{"-fPIC", "${config.CommonGlobalCppflags}", "${config.DeviceGlobalCppflags}", "${config.ArmCppflags}"}
+	ltoFlags := []string{"-flto=thin", "-fsplit-lto-unit"}
 
 	cflags := []string{"-Werror", "-std=candcpp"}
 	cstd := []string{"-std=gnu17", "-std=conly"}
@@ -5005,17 +5006,17 @@
 		{
 			name:     "c",
 			src:      "foo.c",
-			expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
+			expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, ltoFlags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
 		},
 		{
 			name:     "cc",
 			src:      "foo.cc",
-			expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
+			expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, ltoFlags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
 		},
 		{
 			name:     "assemble",
 			src:      "foo.s",
-			expected: combineSlices(baseExpectedFlags, []string{"${config.CommonGlobalAsflags}"}, expectedIncludes, lastIncludes),
+			expected: combineSlices(baseExpectedFlags, []string{"${config.CommonGlobalAsflags}"}, expectedIncludes, ltoFlags, lastIncludes),
 		},
 	}
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index dec2b45..3397e3d 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -43,9 +43,7 @@
 
 	armNoFixCortexA8LdFlags = []string{"-Wl,--no-fix-cortex-a8"}
 
-	armArmCflags = []string{
-		"-fstrict-aliasing",
-	}
+	armArmCflags = []string{}
 
 	armThumbCflags = []string{
 		"-mthumb",
diff --git a/cc/config/global.go b/cc/config/global.go
index 8ff5f55..266d278 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -48,7 +48,6 @@
 		"-Wno-multichar",
 
 		"-O2",
-		"-g",
 		"-fdebug-default-version=5",
 
 		"-fno-strict-aliasing",
@@ -111,6 +110,9 @@
 
 		// Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
 		"-ffp-contract=off",
+
+		// Using simple template names reduces the size of debug builds.
+		"-gsimple-template-names",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -305,7 +307,7 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r487747c"
+	ClangDefaultVersion      = "clang-r498229"
 	ClangDefaultShortVersion = "17"
 
 	// Directories with warnings from Android.bp files.
@@ -374,6 +376,21 @@
 			flags = append(flags, "-Wno-error=unknown-warning-option")
 		}
 
+		switch ctx.Config().Getenv("CLANG_DEFAULT_DEBUG_LEVEL") {
+		case "debug_level_0":
+			flags = append(flags, "-g0")
+		case "debug_level_1":
+			flags = append(flags, "-g1")
+		case "debug_level_2":
+			flags = append(flags, "-g2")
+		case "debug_level_3":
+			flags = append(flags, "-g3")
+		case "debug_level_g":
+			flags = append(flags, "-g")
+		default:
+			flags = append(flags, "-g")
+		}
+
 		return strings.Join(flags, " ")
 	})
 
@@ -448,11 +465,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/library.go b/cc/library.go
index 47df53e..aec6433 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -892,7 +892,7 @@
 
 		// TODO(b/190524881): Include transitive static libraries in this provider to support
 		// static libraries with deps.
-		TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
+		TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).
 			Direct(outputFilePath).
 			Build(),
 	})
@@ -1649,7 +1649,7 @@
 			Objects:                      library.objects,
 			WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
 
-			TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
+			TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).
 				Direct(outputFile).
 				Transitive(deps.TranstiveStaticLibrariesForOrdering).
 				Build(),
@@ -1794,7 +1794,7 @@
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
 	library.linkSAbiDumpFiles(ctx, objs, fileName, unstrippedOutputFile)
 
-	var transitiveStaticLibrariesForOrdering *android.DepSet
+	var transitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
 		s := ctx.OtherModuleProvider(static[0], StaticLibraryInfoProvider).(StaticLibraryInfo)
 		transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
diff --git a/cc/linkable.go b/cc/linkable.go
index 976a382..19e6501 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -345,7 +345,7 @@
 	TableOfContents android.OptionalPath
 
 	// should be obtained from static analogue
-	TransitiveStaticLibrariesForOrdering *android.DepSet
+	TransitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 }
 
 var SharedLibraryInfoProvider = blueprint.NewProvider(SharedLibraryInfo{})
@@ -387,7 +387,7 @@
 	// This isn't the actual transitive DepSet, shared library dependencies have been
 	// converted into static library analogues.  It is only used to order the static
 	// library dependencies that were specified for the current module.
-	TransitiveStaticLibrariesForOrdering *android.DepSet
+	TransitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 }
 
 var StaticLibraryInfoProvider = blueprint.NewProvider(StaticLibraryInfo{})
diff --git a/cc/lto.go b/cc/lto.go
index 878c21f..ff2bf5a 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -47,6 +47,7 @@
 	} `android:"arch_variant"`
 
 	LtoEnabled bool `blueprint:"mutated"`
+	LtoDefault bool `blueprint:"mutated"`
 
 	// Dep properties indicate that this module needs to be built with LTO
 	// since it is an object dependency of an LTO module.
@@ -66,7 +67,34 @@
 }
 
 func (lto *lto) begin(ctx BaseModuleContext) {
-	lto.Properties.LtoEnabled = lto.LTO(ctx)
+	// First, determine the module indepedent default LTO mode.
+	ltoDefault := GlobalThinLTO(ctx)
+	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
+		ltoDefault = false
+	} else if ctx.Host() {
+		// Performance and binary size are less important for host binaries.
+		ltoDefault = false
+	}
+
+	// Then, determine the actual LTO mode to use. If different from `ltoDefault`, a variant needs
+	// to be created.
+	ltoEnabled := ltoDefault
+	if lto.Never() {
+		ltoEnabled = false
+	} else if lto.ThinLTO() {
+		// Module explicitly requests for LTO.
+		ltoEnabled = true
+	} else if ctx.testBinary() || ctx.testLibrary() {
+		// Do not enable LTO for tests for better debugging.
+		ltoEnabled = false
+	} else if ctx.isVndk() {
+		// FIXME: ThinLTO for VNDK produces different output.
+		// b/169217596
+		ltoEnabled = false
+	}
+
+	lto.Properties.LtoDefault = ltoDefault
+	lto.Properties.LtoEnabled = ltoEnabled
 }
 
 func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
@@ -76,75 +104,48 @@
 		return flags
 	}
 	if lto.Properties.LtoEnabled {
-		var ltoCFlag string
-		var ltoLdFlag string
-		if lto.ThinLTO() {
-			ltoCFlag = "-flto=thin -fsplit-lto-unit"
-		} else {
-			ltoCFlag = "-flto=thin -fsplit-lto-unit"
-			ltoLdFlag = "-Wl,--lto-O0"
+		ltoCFlags := []string{"-flto=thin", "-fsplit-lto-unit"}
+		var ltoLdFlags []string
+
+		// The module did not explicitly turn on LTO. Only leverage LTO's
+		// better dead code elinmination and CFG simplification, but do
+		// not perform costly optimizations for a balance between compile
+		// time, binary size and performance.
+		if !lto.ThinLTO() {
+			ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0")
 		}
 
-		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag)
-		flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlag)
-		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag)
-		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag)
-
 		if Bool(lto.Properties.Whole_program_vtables) {
-			flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables")
+			ltoCFlags = append(ltoCFlags, "-fwhole-program-vtables")
 		}
 
 		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
 			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
-			flags.Local.LdFlags = append(flags.Local.LdFlags, cacheDirFormat+cacheDir)
+			ltoLdFlags = append(ltoLdFlags, cacheDirFormat+cacheDir)
 
 			// Limit the size of the ThinLTO cache to the lesser of 10% of available
 			// disk space and 10GB.
 			cachePolicyFormat := "-Wl,--thinlto-cache-policy="
 			policy := "cache_size=10%:cache_size_bytes=10g"
-			flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy)
+			ltoLdFlags = append(ltoLdFlags, cachePolicyFormat+policy)
 		}
 
 		// 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() {
-			flags.Local.LdFlags = append(flags.Local.LdFlags,
-				"-Wl,-plugin-opt,-import-instr-limit=5")
+		if !ctx.Darwin() && !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
+			ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
 		}
+
+		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlags...)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlags...)
 	}
 	return flags
 }
 
-// Determine which LTO mode to use for the given module.
-func (lto *lto) LTO(ctx BaseModuleContext) bool {
-	if lto.Never() {
-		return false
-	}
-	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
-		return false
-	}
-	// Module explicitly requests for LTO.
-	if lto.ThinLTO() {
-		return true
-	}
-	// LP32 has many subtle issues and less test coverage.
-	if ctx.Arch().ArchType.Multilib == "lib32" {
-		return false
-	}
-	// Performance and binary size are less important for host binaries and tests.
-	if ctx.Host() || ctx.testBinary() || ctx.testLibrary() {
-		return false
-	}
-	// FIXME: ThinLTO for VNDK produces different output.
-	// b/169217596
-	if ctx.isVndk() {
-		return false
-	}
-	return GlobalThinLTO(ctx)
-}
-
 func (lto *lto) ThinLTO() bool {
 	return lto != nil && proptools.Bool(lto.Properties.Lto.Thin)
 }
@@ -154,15 +155,13 @@
 }
 
 func GlobalThinLTO(ctx android.BaseModuleContext) bool {
-	return ctx.Config().IsEnvTrue("GLOBAL_THINLTO")
+	return !ctx.Config().IsEnvFalse("GLOBAL_THINLTO")
 }
 
 // Propagate lto requirements down from binaries
 func ltoDepsMutator(mctx android.TopDownMutatorContext) {
-	defaultLTOMode := GlobalThinLTO(mctx)
-
 	if m, ok := mctx.Module().(*Module); ok {
-		if m.lto == nil || m.lto.Properties.LtoEnabled == defaultLTOMode {
+		if m.lto == nil || m.lto.Properties.LtoEnabled == m.lto.Properties.LtoDefault {
 			return
 		}
 
@@ -237,6 +236,7 @@
 				}
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
+				variation.lto.Properties.LtoDefault = m.lto.Properties.LtoDefault
 				variation.lto.Properties.LtoDep = false
 				variation.lto.Properties.NoLtoDep = false
 			}
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 1d15cf8..d3a0a00 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -113,7 +113,7 @@
 	ndk.libraryDecorator.flagExporter.setProvider(ctx)
 
 	if ndk.static() {
-		depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(lib).Build()
+		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(lib).Build()
 		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
 			StaticLibrary: lib,
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 44cd0d7..a4ca590 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -140,7 +140,7 @@
 		}
 
 		if p.static() {
-			depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
+			depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(in).Build()
 			ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
 				StaticLibrary: in,
 
@@ -508,7 +508,7 @@
 
 	h.module.outputFile = android.OptionalPathForPath(outputPath)
 
-	depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputPath).Build()
+	depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(outputPath).Build()
 	ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
 		StaticLibrary:                        outputPath,
 		TransitiveStaticLibrariesForOrdering: depSet,
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 eab6d25..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
 	}
 
@@ -884,16 +905,11 @@
 
 	if Bool(sanProps.Memtag_stack) {
 		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
-		// TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain.
-		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than")
 		flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
-		// This works around LLD complaining about the stack frame size.
-		// TODO(fmayer): remove once https://reviews.llvm.org/D127917 is in Android toolchain.
-		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
 	}
 
-	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 {
@@ -1016,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:
@@ -1032,6 +1050,7 @@
 		!sanitize.isSanitizerEnabled(scs) &&
 		!sanitize.isSanitizerEnabled(Memtag_heap) &&
 		!sanitize.isSanitizerEnabled(Memtag_stack) &&
+		!sanitize.isSanitizerEnabled(Memtag_globals) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
@@ -1053,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:
@@ -1070,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:
@@ -1557,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/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index bb6e257..a5729df 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -499,7 +499,7 @@
 	}
 
 	if p.static() {
-		depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
+		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(in).Build()
 		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
 			StaticLibrary: in,
 
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/config.go b/dexpreopt/config.go
index e61ebe6..bb83dc8 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -321,7 +321,7 @@
 				missingDepsCtx.AddMissingDependencies([]string{err.Error()})
 			}
 		} else {
-			android.ReportPathErrorf(ctx, "%w", err)
+			android.ReportPathErrorf(ctx, "%s", err)
 		}
 	}
 
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..c6fa030 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",
@@ -57,7 +56,6 @@
 		"RSTest-rscript",
 		"BluetoothGeneratedDumpsysBinarySchema_bfbs",
 		"TracingVMProtoStub_h",
-		"FrontendStub_h",
 		"VehicleServerProtoStub_cc",
 		"AudioFocusControlProtoStub_cc",
 		"AudioFocusControlProtoStub_h",
@@ -95,14 +93,10 @@
 		"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",
 		"BluetoothGeneratedDumpsysDataSchema_h",
-		"FrontendStub_cc",
-		"OpenwrtControlServerProto_cc",
-		"OpenwrtControlServerProto_h",
 		"c2hal_test_genc++",
 		"c2hal_test_genc++_headers",
 		"hidl2aidl_test_gen_aidl",
@@ -119,6 +113,9 @@
 		"nos_app_weaver_service_genc++_headers",
 		"nos_app_weaver_service_genc++_mock",
 		"nos_generator_test_service_genc++",
+		"aidl_camera_build_version",
+		"cronet_aml_base_android_runtime_unchecked_jni_headers",
+		"cronet_aml_base_android_runtime_jni_headers",
 	}
 
 	SandboxingDenyPathList = []string{
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b29e2c9..99c9166 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -435,6 +435,7 @@
 		cmd = g.CmdModifier(ctx, cmd)
 	}
 
+	var extraInputs android.Paths
 	// Generate tasks, either from genrule or gensrcs.
 	for i, task := range g.taskGenerator(ctx, cmd, srcFiles) {
 		if len(task.out) == 0 {
@@ -442,7 +443,6 @@
 			return
 		}
 
-		var extraInputs android.Paths
 		// Only handle extra inputs once as these currently are the same across all tasks
 		if i == 0 {
 			for name, values := range task.extraInputs {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 7c17db1..6301bbf 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os"
 	"regexp"
+	"strconv"
 	"testing"
 
 	"android/soong/android"
@@ -557,10 +558,12 @@
 
 		allowMissingDependencies bool
 
-		err   string
-		cmds  []string
-		deps  []string
-		files []string
+		err    string
+		cmds   []string
+		deps   []string
+		files  []string
+		shards int
+		inputs []string
 	}{
 		{
 			name: "gensrcs",
@@ -627,9 +630,29 @@
 				"out/soong/.intermediates/gen/gen/gensrcs/in2.h",
 				"out/soong/.intermediates/gen/gen/gensrcs/in3.h",
 			},
+			shards: 2,
+			inputs: []string{
+				"baz.txt",
+			},
 		},
 	}
 
+	checkInputs := func(t *testing.T, rule android.TestingBuildParams, inputs []string) {
+		t.Helper()
+		if len(inputs) == 0 {
+			return
+		}
+		inputBaseNames := map[string]bool{}
+		for _, f := range rule.Implicits {
+			inputBaseNames[f.Base()] = true
+		}
+		for _, f := range inputs {
+			if _, ok := inputBaseNames[f]; !ok {
+				t.Errorf("Expected to find input file %q for %q, but did not", f, rule.Description)
+			}
+		}
+	}
+
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
 			bp := "gensrcs {\n"
@@ -647,10 +670,21 @@
 				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
 				RunTestWithBp(t, testGenruleBp()+bp)
 
+			mod := result.ModuleForTests("gen", "")
 			if expectedErrors != nil {
 				return
 			}
 
+			if test.shards > 0 {
+				for i := 0; i < test.shards; i++ {
+					r := mod.Rule("generator" + strconv.Itoa(i))
+					checkInputs(t, r, test.inputs)
+				}
+			} else {
+				r := mod.Rule("generator")
+				checkInputs(t, r, test.inputs)
+			}
+
 			gen := result.Module("gen", "").(*Module)
 			android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
 
diff --git a/go.mod b/go.mod
index 4a511c5..0a11bd2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module android/soong
 
-go 1.19
+go 1.20
 
 require (
 	github.com/google/blueprint v0.0.0
diff --git a/java/aar.go b/java/aar.go
index 29e86e6..ed3c652 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -31,10 +31,9 @@
 type AndroidLibraryDependency interface {
 	LibraryDependency
 	ExportPackage() android.Path
-	ExportedRRODirs() []rroDir
-	ExportedStaticPackages() android.Paths
-	ExportedManifests() android.Paths
-	ExportedAssets() android.OptionalPath
+	ResourcesNodeDepSet() *android.DepSet[resourcesNode]
+	RRODirsDepSet() *android.DepSet[rroDir]
+	ManifestsDepSet() *android.DepSet[android.Path]
 	SetRROEnforcedForDependent(enforce bool)
 	IsRROEnforced(ctx android.BaseModuleContext) bool
 }
@@ -94,30 +93,32 @@
 }
 
 type aapt struct {
-	aaptSrcJar              android.Path
-	exportPackage           android.Path
-	manifestPath            android.Path
-	transitiveManifestPaths android.Paths
-	proguardOptionsFile     android.Path
-	rroDirs                 []rroDir
-	rTxt                    android.Path
-	extraAaptPackagesFile   android.Path
-	mergedManifestFile      android.Path
-	noticeFile              android.OptionalPath
-	assetPackage            android.OptionalPath
-	isLibrary               bool
-	defaultManifestVersion  string
-	useEmbeddedNativeLibs   bool
-	useEmbeddedDex          bool
-	usesNonSdkApis          bool
-	hasNoCode               bool
-	LoggingParent           string
-	resourceFiles           android.Paths
+	aaptSrcJar             android.Path
+	exportPackage          android.Path
+	manifestPath           android.Path
+	proguardOptionsFile    android.Path
+	rTxt                   android.Path
+	extraAaptPackagesFile  android.Path
+	mergedManifestFile     android.Path
+	noticeFile             android.OptionalPath
+	assetPackage           android.OptionalPath
+	isLibrary              bool
+	defaultManifestVersion string
+	useEmbeddedNativeLibs  bool
+	useEmbeddedDex         bool
+	usesNonSdkApis         bool
+	hasNoCode              bool
+	LoggingParent          string
+	resourceFiles          android.Paths
 
 	splitNames []string
 	splits     []split
 
 	aaptProperties aaptProperties
+
+	resourcesNodesDepSet *android.DepSet[resourcesNode]
+	rroDirsDepSet        *android.DepSet[rroDir]
+	manifestsDepSet      *android.DepSet[android.Path]
 }
 
 type split struct {
@@ -141,17 +142,16 @@
 func (a *aapt) ExportPackage() android.Path {
 	return a.exportPackage
 }
-
-func (a *aapt) ExportedRRODirs() []rroDir {
-	return a.rroDirs
+func (a *aapt) ResourcesNodeDepSet() *android.DepSet[resourcesNode] {
+	return a.resourcesNodesDepSet
 }
 
-func (a *aapt) ExportedManifests() android.Paths {
-	return a.transitiveManifestPaths
+func (a *aapt) RRODirsDepSet() *android.DepSet[rroDir] {
+	return a.rroDirsDepSet
 }
 
-func (a *aapt) ExportedAssets() android.OptionalPath {
-	return a.assetPackage
+func (a *aapt) ManifestsDepSet() *android.DepSet[android.Path] {
+	return a.manifestsDepSet
 }
 
 func (a *aapt) SetRROEnforcedForDependent(enforce bool) {
@@ -291,7 +291,7 @@
 	classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string,
 	enforceDefaultTargetSdkVersion bool, extraLinkFlags ...string) {
 
-	transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
+	staticResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedDeps, libFlags :=
 		aaptLibs(ctx, sdkContext, classLoaderContexts)
 
 	// Exclude any libraries from the supplied list.
@@ -314,13 +314,20 @@
 		EnforceDefaultTargetSdkVersion: enforceDefaultTargetSdkVersion,
 	})
 
+	staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList())
+
 	// Add additional manifest files to transitive manifests.
 	additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
-	a.transitiveManifestPaths = append(android.Paths{manifestPath}, additionalManifests...)
-	a.transitiveManifestPaths = append(a.transitiveManifestPaths, transitiveStaticLibManifests...)
+	transitiveManifestPaths := append(android.Paths{manifestPath}, additionalManifests...)
+	// TODO(b/288358614): Soong has historically not merged manifests from dependencies of android_library_import
+	// modules.  Merging manifests from dependencies could remove the need for pom2bp to generate the "-nodeps" copies
+	// of androidx libraries, but doing so triggers errors due to errors introduced by existing dependencies of
+	// android_library_import modules.  If this is fixed, staticManifestsDepSet can be dropped completely in favor of
+	// staticResourcesNodesDepSet.manifests()
+	transitiveManifestPaths = append(transitiveManifestPaths, staticManifestsDepSet.ToList()...)
 
-	if len(a.transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) {
-		a.mergedManifestFile = manifestMerger(ctx, a.transitiveManifestPaths[0], a.transitiveManifestPaths[1:], a.isLibrary)
+	if len(transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) {
+		a.mergedManifestFile = manifestMerger(ctx, transitiveManifestPaths[0], transitiveManifestPaths[1:], a.isLibrary)
 		if !a.isLibrary {
 			// Only use the merged manifest for applications.  For libraries, the transitive closure of manifests
 			// will be propagated to the final application and merged there.  The merged manifest for libraries is
@@ -333,9 +340,9 @@
 
 	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
 
-	rroDirs = append(rroDirs, staticRRODirs...)
 	linkFlags = append(linkFlags, libFlags...)
-	linkDeps = append(linkDeps, libDeps...)
+	linkDeps = append(linkDeps, sharedDeps...)
+	linkDeps = append(linkDeps, staticDeps.resPackages()...)
 	linkFlags = append(linkFlags, extraLinkFlags...)
 	if a.isLibrary {
 		linkFlags = append(linkFlags, "--static-lib")
@@ -363,6 +370,10 @@
 
 	var compiledRes, compiledOverlay android.Paths
 
+	// AAPT2 overlays are in lowest to highest priority order, reverse the topological order
+	// of transitiveStaticLibs.
+	transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages())
+
 	compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
 
 	if len(transitiveStaticLibs) > 0 {
@@ -404,12 +415,18 @@
 		})
 	}
 
+	// No need to specify assets from dependencies to aapt2Link for libraries, all transitive assets will be
+	// provided to the final app aapt2Link step.
+	var transitiveAssets android.Paths
+	if !a.isLibrary {
+		transitiveAssets = android.ReverseSliceInPlace(staticDeps.assets())
+	}
 	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages,
-		linkFlags, linkDeps, compiledRes, compiledOverlay, assetPackages, splitPackages)
+		linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages)
 
 	// Extract assets from the resource package output so that they can be used later in aapt2link
 	// for modules that depend on this one.
-	if android.PrefixInList(linkFlags, "-A ") || len(assetPackages) > 0 {
+	if android.PrefixInList(linkFlags, "-A ") {
 		assets := android.PathForModuleOut(ctx, "assets.zip")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        extractAssetsRule,
@@ -424,17 +441,62 @@
 	a.exportPackage = packageRes
 	a.manifestPath = manifestPath
 	a.proguardOptionsFile = proguardOptionsFile
-	a.rroDirs = rroDirs
 	a.extraAaptPackagesFile = extraPackages
 	a.rTxt = rTxt
 	a.splits = splits
+	a.resourcesNodesDepSet = android.NewDepSetBuilder[resourcesNode](android.TOPOLOGICAL).
+		Direct(resourcesNode{
+			resPackage: a.exportPackage,
+			manifest:   a.manifestPath,
+			assets:     a.assetPackage,
+		}).
+		Transitive(staticResourcesNodesDepSet).Build()
+	a.rroDirsDepSet = android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL).
+		Direct(rroDirs...).
+		Transitive(staticRRODirsDepSet).Build()
+	a.manifestsDepSet = android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).
+		Direct(a.manifestPath).
+		Transitive(staticManifestsDepSet).Build()
+}
+
+type resourcesNode struct {
+	resPackage android.Path
+	manifest   android.Path
+	assets     android.OptionalPath
+}
+
+type transitiveAarDeps []resourcesNode
+
+func (t transitiveAarDeps) resPackages() android.Paths {
+	var paths android.Paths
+	for _, dep := range t {
+		paths = append(paths, dep.resPackage)
+	}
+	return android.FirstUniquePaths(paths)
+}
+
+func (t transitiveAarDeps) manifests() android.Paths {
+	var paths android.Paths
+	for _, dep := range t {
+		paths = append(paths, dep.manifest)
+	}
+	return android.FirstUniquePaths(paths)
+}
+
+func (t transitiveAarDeps) assets() android.Paths {
+	var paths android.Paths
+	for _, dep := range t {
+		if dep.assets.Valid() {
+			paths = append(paths, dep.assets.Path())
+		}
+	}
+	return paths
 }
 
 // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
 func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
-	transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) {
-
-	var sharedLibs android.Paths
+	staticResourcesNodes *android.DepSet[resourcesNode], staticRRODirs *android.DepSet[rroDir],
+	staticManifests *android.DepSet[android.Path], sharedLibs android.Paths, flags []string) {
 
 	if classLoaderContexts == nil {
 		// Not all callers need to compute class loader context, those who don't just pass nil.
@@ -447,6 +509,10 @@
 		sharedLibs = append(sharedLibs, sdkDep.jars...)
 	}
 
+	var resourcesNodeDepSets []*android.DepSet[resourcesNode]
+	rroDirsDepSetBuilder := android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL)
+	manifestsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL)
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		depTag := ctx.OtherModuleDependencyTag(module)
 
@@ -469,32 +535,28 @@
 			}
 		case staticLibTag:
 			if exportPackage != nil {
-				transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
-				transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
-				transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
-				if aarDep.ExportedAssets().Valid() {
-					assets = append(assets, aarDep.ExportedAssets().Path())
-				}
-
-			outer:
-				for _, d := range aarDep.ExportedRRODirs() {
-					for _, e := range staticRRODirs {
-						if d.path == e.path {
-							continue outer
-						}
-					}
-					staticRRODirs = append(staticRRODirs, d)
-				}
+				resourcesNodeDepSets = append(resourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
+				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet())
+				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet())
 			}
 		}
 
 		addCLCFromDep(ctx, module, classLoaderContexts)
 	})
 
-	deps = append(deps, sharedLibs...)
-	deps = append(deps, transitiveStaticLibs...)
+	// AAPT2 overlays are in lowest to highest priority order, the topological order will be reversed later.
+	// Reverse the dependency order now going into the depset so that it comes out in order after the second
+	// reverse later.
+	// NOTE: this is legacy and probably incorrect behavior, for most other cases (e.g. conflicting classes in
+	// dependencies) the highest priority dependency is listed first, but for resources the highest priority
+	// dependency has to be listed last.
+	staticResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
+		android.ReverseSliceInPlace(resourcesNodeDepSets))
 
-	if len(transitiveStaticLibs) > 0 {
+	staticRRODirs = rroDirsDepSetBuilder.Build()
+	staticManifests = manifestsDepSetBuilder.Build()
+
+	if len(staticResourcesNodes.ToList()) > 0 {
 		flags = append(flags, "--auto-add-overlay")
 	}
 
@@ -502,10 +564,7 @@
 		flags = append(flags, "-I "+sharedLib.String())
 	}
 
-	transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
-	transitiveStaticLibManifests = android.FirstUniquePaths(transitiveStaticLibManifests)
-
-	return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assets, deps, flags
+	return staticResourcesNodes, staticRRODirs, staticManifests, sharedLibs, flags
 }
 
 type AndroidLibrary struct {
@@ -516,8 +575,6 @@
 	androidLibraryProperties androidLibraryProperties
 
 	aarFile android.WritablePath
-
-	exportedStaticPackages android.Paths
 }
 
 var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
@@ -532,10 +589,6 @@
 	}
 }
 
-func (a *AndroidLibrary) ExportedStaticPackages() android.Paths {
-	return a.exportedStaticPackages
-}
-
 var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
 
 func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -579,19 +632,15 @@
 
 	a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles,
 		android.PathsForModuleSrc(ctx, a.dexProperties.Optimize.Proguard_flags_files)...)
+
 	ctx.VisitDirectDeps(func(m android.Module) {
 		if ctx.OtherModuleDependencyTag(m) == staticLibTag {
 			if lib, ok := m.(LibraryDependency); ok {
 				a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
 			}
-			if alib, ok := m.(AndroidLibraryDependency); ok {
-				a.exportedStaticPackages = append(a.exportedStaticPackages, alib.ExportPackage())
-				a.exportedStaticPackages = append(a.exportedStaticPackages, alib.ExportedStaticPackages()...)
-			}
 		}
 	})
 	a.exportedProguardFlagFiles = android.FirstUniquePaths(a.exportedProguardFlagFiles)
-	a.exportedStaticPackages = android.FirstUniquePaths(a.exportedStaticPackages)
 
 	prebuiltJniPackages := android.Paths{}
 	ctx.VisitDirectDeps(func(module android.Module) {
@@ -681,7 +730,8 @@
 	manifest              android.WritablePath
 	assetsPackage         android.WritablePath
 
-	exportedStaticPackages android.Paths
+	resourcesNodesDepSet *android.DepSet[resourcesNode]
+	manifestsDepSet      *android.DepSet[android.Path]
 
 	hideApexVariantFromMake bool
 
@@ -738,25 +788,20 @@
 func (a *AARImport) ExportPackage() android.Path {
 	return a.exportPackage
 }
-
 func (a *AARImport) ExportedProguardFlagFiles() android.Paths {
 	return android.Paths{a.proguardFlags}
 }
 
-func (a *AARImport) ExportedRRODirs() []rroDir {
-	return nil
+func (a *AARImport) ResourcesNodeDepSet() *android.DepSet[resourcesNode] {
+	return a.resourcesNodesDepSet
 }
 
-func (a *AARImport) ExportedStaticPackages() android.Paths {
-	return a.exportedStaticPackages
+func (a *AARImport) RRODirsDepSet() *android.DepSet[rroDir] {
+	return android.NewDepSet[rroDir](android.TOPOLOGICAL, nil, nil)
 }
 
-func (a *AARImport) ExportedManifests() android.Paths {
-	return android.Paths{a.manifest}
-}
-
-func (a *AARImport) ExportedAssets() android.OptionalPath {
-	return android.OptionalPathForPath(a.assetsPackage)
+func (a *AARImport) ManifestsDepSet() *android.DepSet[android.Path] {
+	return a.manifestsDepSet
 }
 
 // RRO enforcement is not available on aar_import since its RRO dirs are not
@@ -891,32 +936,44 @@
 	linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
 	linkDeps = append(linkDeps, a.manifest)
 
-	transitiveStaticLibs, staticLibManifests, staticRRODirs, transitiveAssets, libDeps, libFlags :=
+	staticResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedLibs, libFlags :=
 		aaptLibs(ctx, android.SdkContext(a), nil)
 
-	_ = staticLibManifests
-	_ = staticRRODirs
+	_ = staticRRODirsDepSet
+	staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList())
 
-	linkDeps = append(linkDeps, libDeps...)
+	// AAPT2 overlays are in lowest to highest priority order, reverse the topological order
+	// of transitiveStaticLibs.
+	transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages())
+
+	linkDeps = append(linkDeps, sharedLibs...)
+	linkDeps = append(linkDeps, transitiveStaticLibs...)
 	linkFlags = append(linkFlags, libFlags...)
 
 	overlayRes := append(android.Paths{flata}, transitiveStaticLibs...)
 
+	transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets())
 	aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil)
 
-	// Merge this import's assets with its dependencies' assets (if there are any).
-	if len(transitiveAssets) > 0 {
-		mergedAssets := android.PathForModuleOut(ctx, "merged-assets.zip")
-		inputZips := append(android.Paths{a.assetsPackage}, transitiveAssets...)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        mergeAssetsRule,
-			Inputs:      inputZips,
-			Output:      mergedAssets,
-			Description: "merge assets from dependencies and self",
-		})
-		a.assetsPackage = mergedAssets
-	}
+	resourcesNodesDepSetBuilder := android.NewDepSetBuilder[resourcesNode](android.TOPOLOGICAL)
+	resourcesNodesDepSetBuilder.Direct(resourcesNode{
+		resPackage: a.exportPackage,
+		manifest:   a.manifest,
+		assets:     android.OptionalPathForPath(a.assetsPackage),
+	})
+	resourcesNodesDepSetBuilder.Transitive(staticResourcesNodesDepSet)
+	a.resourcesNodesDepSet = resourcesNodesDepSetBuilder.Build()
+
+	manifestDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(a.manifest)
+	// TODO(b/288358614): Soong has historically not merged manifests from dependencies of android_library_import
+	// modules.  Merging manifests from dependencies could remove the need for pom2bp to generate the "-nodeps" copies
+	// of androidx libraries, but doing so triggers errors due to errors introduced by existing dependencies of
+	// android_library_import modules.  If this is fixed, AndroidLibraryDependency.ManifestsDepSet can be dropped
+	// completely in favor of AndroidLibraryDependency.ResourceNodesDepSet.manifest
+	//manifestDepSetBuilder.Transitive(transitiveStaticDeps.manifests)
+	_ = staticManifestsDepSet
+	a.manifestsDepSet = manifestDepSetBuilder.Build()
 
 	a.collectTransitiveHeaderJars(ctx)
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
diff --git a/java/androidmk.go b/java/androidmk.go
index 9c21633..13cc96a 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -368,8 +368,13 @@
 
 				filterRRO := func(filter overlayType) android.Paths {
 					var paths android.Paths
-					for _, d := range app.rroDirs {
+					seen := make(map[android.Path]bool)
+					for _, d := range app.rroDirsDepSet.ToList() {
 						if d.overlayType == filter {
+							if seen[d.path] {
+								continue
+							}
+							seen[d.path] = true
 							paths = append(paths, d.path)
 						}
 					}
diff --git a/java/app.go b/java/app.go
index d9272e4..10010e4 100755
--- a/java/app.go
+++ b/java/app.go
@@ -204,8 +204,8 @@
 	return nil
 }
 
-func (a *AndroidApp) ExportedStaticPackages() android.Paths {
-	return nil
+func (a *AndroidApp) ResourcesNodeDepSet() *android.DepSet[resourcesNode] {
+	return a.aapt.resourcesNodesDepSet
 }
 
 func (a *AndroidApp) OutputFile() android.Path {
@@ -1571,7 +1571,9 @@
 }
 
 type manifestValueAttribute struct {
-	MinSdkVersion *string
+	MinSdkVersion    *string
+	MaxSdkVersion    *string
+	TargetSdkVersion *string
 }
 
 type bazelAndroidAppAttributes struct {
@@ -1601,12 +1603,25 @@
 	// MinSdkVersion(ctx) calls SdkVersion(ctx) if no value for min_sdk_version is set
 	minSdkVersion := a.MinSdkVersion(ctx)
 	if !minSdkVersion.IsPreview() && !minSdkVersion.IsInvalid() {
-		minSdkStr, err := minSdkVersion.EffectiveVersionString(ctx)
-		if err == nil {
+		if minSdkStr, err := minSdkVersion.EffectiveVersionString(ctx); err == nil {
 			manifestValues.MinSdkVersion = &minSdkStr
 		}
 	}
 
+	maxSdkVersion := a.MaxSdkVersion(ctx)
+	if !maxSdkVersion.IsPreview() && !maxSdkVersion.IsInvalid() {
+		if maxSdkStr, err := maxSdkVersion.EffectiveVersionString(ctx); err == nil {
+			manifestValues.MaxSdkVersion = &maxSdkStr
+		}
+	}
+
+	targetSdkVersion := a.TargetSdkVersion(ctx)
+	if !targetSdkVersion.IsPreview() && !targetSdkVersion.IsInvalid() {
+		if targetSdkStr, err := targetSdkVersion.EffectiveVersionString(ctx); err == nil {
+			manifestValues.TargetSdkVersion = &targetSdkStr
+		}
+	}
+
 	appAttrs := &bazelAndroidAppAttributes{
 		// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
 		Custom_package:   a.overridableAppProperties.Package_name,
diff --git a/java/app_import.go b/java/app_import.go
index 9c01960..e25bcd1 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -17,6 +17,7 @@
 // This file contains the module implementations for android_app_import and android_test_import.
 
 import (
+	"fmt"
 	"reflect"
 
 	"github.com/google/blueprint"
@@ -334,11 +335,19 @@
 	// Sign or align the package if package has not been preprocessed
 
 	if proptools.Bool(a.properties.Preprocessed) {
-		output := srcApk
+		var output android.WritablePath
 		if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
-			writableOutput := android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
-			a.validatePreprocessedApk(ctx, srcApk, writableOutput)
-			output = writableOutput
+			output = android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
+			a.validatePreprocessedApk(ctx, srcApk, output)
+		} else {
+			// If using the input APK unmodified, still make a copy of it so that the output filename has the
+			// right basename.
+			output = android.PathForModuleOut(ctx, apkFilename)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  srcApk,
+				Output: output,
+			})
 		}
 		a.outputFile = output
 		a.certificate = PresignedCertificate
@@ -410,6 +419,15 @@
 	return a.outputFile
 }
 
+func (a *AndroidAppImport) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return []android.Path{a.outputFile}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
 	return nil
 }
diff --git a/java/app_test.go b/java/app_test.go
index cf7d174..c438b6c 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -599,7 +599,7 @@
 			android_library {
 				name: "lib3",
 				sdk_version: "current",
-				static_libs: ["lib4"],
+				static_libs: ["lib4", "import"],
 			}
 
 			android_library {
@@ -607,6 +607,12 @@
 				sdk_version: "current",
 				asset_dirs: ["assets_b"],
 			}
+
+			android_library_import {
+				name: "import",
+				sdk_version: "current",
+				aars: ["import.aar"],
+			}
 		`
 
 	testCases := []struct {
@@ -616,11 +622,12 @@
 	}{
 		{
 			name: "foo",
-			// lib1 has its own asset. lib3 doesn't have any, but provides lib4's transitively.
+			// lib1 has its own assets. lib3 doesn't have any, but lib4 and import have assets.
 			assetPackages: []string{
 				"out/soong/.intermediates/foo/android_common/aapt2/package-res.apk",
 				"out/soong/.intermediates/lib1/android_common/assets.zip",
-				"out/soong/.intermediates/lib3/android_common/assets.zip",
+				"out/soong/.intermediates/lib4/android_common/assets.zip",
+				"out/soong/.intermediates/import/android_common/assets.zip",
 			},
 		},
 		{
@@ -632,10 +639,6 @@
 		},
 		{
 			name: "lib3",
-			assetPackages: []string{
-				"out/soong/.intermediates/lib3/android_common/aapt2/package-res.apk",
-				"out/soong/.intermediates/lib4/android_common/assets.zip",
-			},
 		},
 		{
 			name:      "lib4",
@@ -717,7 +720,267 @@
 	}
 }
 
-func TestAndroidResources(t *testing.T) {
+func TestAndroidResourceProcessor(t *testing.T) {
+	testCases := []struct {
+		name string
+
+		dontVerifyApp bool
+		appResources  []string
+		appOverlays   []string
+		appImports    []string
+		appSrcJars    []string
+		appClasspath  []string
+		appCombined   []string
+
+		dontVerifyDirect bool
+		directResources  []string
+		directOverlays   []string
+		directImports    []string
+		directSrcJars    []string
+		directClasspath  []string
+		directCombined   []string
+
+		dontVerifyTransitive bool
+		transitiveResources  []string
+		transitiveOverlays   []string
+		transitiveImports    []string
+		transitiveSrcJars    []string
+		transitiveClasspath  []string
+		transitiveCombined   []string
+
+		dontVerifyDirectImport bool
+		directImportResources  []string
+		directImportOverlays   []string
+		directImportImports    []string
+
+		dontVerifyTransitiveImport bool
+		transitiveImportResources  []string
+		transitiveImportOverlays   []string
+		transitiveImportImports    []string
+	}{
+		{
+			name: "legacy",
+
+			appResources: nil,
+			appOverlays: []string{
+				"out/soong/.intermediates/transitive/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import/android_common/package-res.apk",
+				"out/soong/.intermediates/direct/android_common/package-res.apk",
+				"out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
+				"out/soong/.intermediates/direct_import/android_common/package-res.apk",
+				"out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat",
+			},
+
+			appImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
+			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
+			appClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+			},
+			appCombined: []string{
+				"out/soong/.intermediates/app/android_common/javac/app.jar",
+				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+			},
+
+			directResources: nil,
+			directOverlays: []string{
+				"out/soong/.intermediates/transitive/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import/android_common/package-res.apk",
+				"out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat",
+			},
+
+			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
+			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
+			directClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+			},
+			directCombined: []string{
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+			},
+
+			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
+			transitiveOverlays:  nil,
+			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
+			transitiveSrcJars:   []string{"out/soong/.intermediates/transitive/android_common/gen/android/R.srcjar"},
+			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar"},
+			transitiveCombined:  nil,
+
+			directImportResources: nil,
+			directImportOverlays: []string{
+				"out/soong/.intermediates/direct_import/android_common/flat-res/gen_res.flata",
+				"out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
+			},
+			directImportImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
+
+			transitiveImportResources: nil,
+			transitiveImportOverlays: []string{
+				"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+			},
+			transitiveImportImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			bp := fmt.Sprintf(`
+				android_app {
+					name: "app",
+					sdk_version: "current",
+					srcs: ["app/app.java"],
+					resource_dirs: ["app/res"],
+					manifest: "app/AndroidManifest.xml",
+					static_libs: ["direct", "direct_import"],
+				}
+
+				android_library {
+					name: "direct",
+					sdk_version: "current",
+					srcs: ["direct/direct.java"],
+					resource_dirs: ["direct/res"],
+					manifest: "direct/AndroidManifest.xml",
+					static_libs: ["transitive", "transitive_import"],
+				}
+
+				android_library {
+					name: "transitive",
+					sdk_version: "current",
+					srcs: ["transitive/transitive.java"],
+					resource_dirs: ["transitive/res"],
+					manifest: "transitive/AndroidManifest.xml",
+				}
+
+				android_library_import {
+					name: "direct_import",
+					sdk_version: "current",
+					aars: ["direct_import.aar"],
+					static_libs: ["direct_import_dep"],
+				}
+
+				android_library_import {
+					name: "direct_import_dep",
+					sdk_version: "current",
+					aars: ["direct_import_dep.aar"],
+				}
+
+				android_library_import {
+					name: "transitive_import",
+					sdk_version: "current",
+					aars: ["transitive_import.aar"],
+					static_libs: ["transitive_import_dep"],
+				}
+
+				android_library_import {
+					name: "transitive_import_dep",
+					sdk_version: "current",
+					aars: ["transitive_import_dep.aar"],
+				}
+			`)
+
+			fs := android.MockFS{
+				"app/res/values/strings.xml":        nil,
+				"direct/res/values/strings.xml":     nil,
+				"transitive/res/values/strings.xml": nil,
+			}
+
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithOverlayBuildComponents,
+				fs.AddToFixture(),
+			).RunTestWithBp(t, bp)
+
+			type aaptInfo struct {
+				resources, overlays, imports, srcJars, classpath, combined android.Paths
+			}
+
+			getAaptInfo := func(moduleName string) (aaptInfo aaptInfo) {
+				mod := result.ModuleForTests(moduleName, "android_common")
+				resourceListRule := mod.MaybeOutput("aapt2/res.list")
+				overlayListRule := mod.MaybeOutput("aapt2/overlay.list")
+				aaptRule := mod.Rule("aapt2Link")
+				javacRule := mod.MaybeRule("javac")
+				combinedRule := mod.MaybeOutput("combined/" + moduleName + ".jar")
+
+				aaptInfo.resources = resourceListRule.Inputs
+				aaptInfo.overlays = overlayListRule.Inputs
+
+				aaptFlags := strings.Split(aaptRule.Args["flags"], " ")
+				for i, flag := range aaptFlags {
+					if flag == "-I" && i+1 < len(aaptFlags) {
+						aaptInfo.imports = append(aaptInfo.imports, android.PathForTesting(aaptFlags[i+1]))
+					}
+				}
+
+				if len(javacRule.Args["srcJars"]) > 0 {
+					aaptInfo.srcJars = android.PathsForTesting(strings.Split(javacRule.Args["srcJars"], " ")...)
+				}
+
+				if len(javacRule.Args["classpath"]) > 0 {
+					classpathArg := strings.TrimPrefix(javacRule.Args["classpath"], "-classpath ")
+					aaptInfo.classpath = android.PathsForTesting(strings.Split(classpathArg, ":")...)
+				}
+
+				aaptInfo.combined = combinedRule.Inputs
+				return
+			}
+
+			app := getAaptInfo("app")
+			direct := getAaptInfo("direct")
+			transitive := getAaptInfo("transitive")
+			directImport := getAaptInfo("direct_import")
+			transitiveImport := getAaptInfo("transitive_import")
+
+			if !testCase.dontVerifyApp {
+				android.AssertPathsRelativeToTopEquals(t, "app resources", testCase.appResources, app.resources)
+				android.AssertPathsRelativeToTopEquals(t, "app overlays", testCase.appOverlays, app.overlays)
+				android.AssertPathsRelativeToTopEquals(t, "app imports", testCase.appImports, app.imports)
+				android.AssertPathsRelativeToTopEquals(t, "app srcjars", testCase.appSrcJars, app.srcJars)
+				android.AssertPathsRelativeToTopEquals(t, "app classpath", testCase.appClasspath, app.classpath)
+				android.AssertPathsRelativeToTopEquals(t, "app combined", testCase.appCombined, app.combined)
+			}
+
+			if !testCase.dontVerifyDirect {
+				android.AssertPathsRelativeToTopEquals(t, "direct resources", testCase.directResources, direct.resources)
+				android.AssertPathsRelativeToTopEquals(t, "direct overlays", testCase.directOverlays, direct.overlays)
+				android.AssertPathsRelativeToTopEquals(t, "direct imports", testCase.directImports, direct.imports)
+				android.AssertPathsRelativeToTopEquals(t, "direct srcjars", testCase.directSrcJars, direct.srcJars)
+				android.AssertPathsRelativeToTopEquals(t, "direct classpath", testCase.directClasspath, direct.classpath)
+				android.AssertPathsRelativeToTopEquals(t, "direct combined", testCase.directCombined, direct.combined)
+			}
+
+			if !testCase.dontVerifyTransitive {
+				android.AssertPathsRelativeToTopEquals(t, "transitive resources", testCase.transitiveResources, transitive.resources)
+				android.AssertPathsRelativeToTopEquals(t, "transitive overlays", testCase.transitiveOverlays, transitive.overlays)
+				android.AssertPathsRelativeToTopEquals(t, "transitive imports", testCase.transitiveImports, transitive.imports)
+				android.AssertPathsRelativeToTopEquals(t, "transitive srcjars", testCase.transitiveSrcJars, transitive.srcJars)
+				android.AssertPathsRelativeToTopEquals(t, "transitive classpath", testCase.transitiveClasspath, transitive.classpath)
+				android.AssertPathsRelativeToTopEquals(t, "transitive combined", testCase.transitiveCombined, transitive.combined)
+			}
+
+			if !testCase.dontVerifyDirectImport {
+				android.AssertPathsRelativeToTopEquals(t, "direct_import resources", testCase.directImportResources, directImport.resources)
+				android.AssertPathsRelativeToTopEquals(t, "direct_import overlays", testCase.directImportOverlays, directImport.overlays)
+				android.AssertPathsRelativeToTopEquals(t, "direct_import imports", testCase.directImportImports, directImport.imports)
+			}
+
+			if !testCase.dontVerifyTransitiveImport {
+				android.AssertPathsRelativeToTopEquals(t, "transitive_import resources", testCase.transitiveImportResources, transitiveImport.resources)
+				android.AssertPathsRelativeToTopEquals(t, "transitive_import overlays", testCase.transitiveImportOverlays, transitiveImport.overlays)
+				android.AssertPathsRelativeToTopEquals(t, "transitive_import imports", testCase.transitiveImportImports, transitiveImport.imports)
+			}
+		})
+	}
+}
+
+func TestAndroidResourceOverlays(t *testing.T) {
 	testCases := []struct {
 		name                       string
 		enforceRROTargets          []string
@@ -943,7 +1206,7 @@
 					overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs))
 				}
 
-				for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() {
+				for _, d := range module.Module().(AndroidLibraryDependency).RRODirsDepSet().ToList() {
 					var prefix string
 					if d.overlayType == device {
 						prefix = "device:"
@@ -2697,7 +2960,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 f9c9e1e..cb08ef3 100644
--- a/java/base.go
+++ b/java/base.go
@@ -500,6 +500,8 @@
 	maxSdkVersion android.ApiLevel
 
 	sourceExtensions []string
+
+	annoSrcJars android.Paths
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -1255,8 +1257,9 @@
 			// this module, or else we could have duplicated errorprone messages.
 			errorproneFlags := enableErrorproneFlags(flags)
 			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
+			errorproneAnnoSrcJar := android.PathForModuleOut(ctx, "errorprone", "anno.srcjar")
 
-			transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneFlags, nil,
+			transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneAnnoSrcJar, errorproneFlags, nil,
 				"errorprone", "errorprone")
 
 			extraJarDeps = append(extraJarDeps, errorprone)
@@ -1657,13 +1660,15 @@
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
 
 	kzipName := pathtools.ReplaceExtension(jarName, "kzip")
+	annoSrcJar := android.PathForModuleOut(ctx, "javac", "anno.srcjar")
 	if idx >= 0 {
 		kzipName = strings.TrimSuffix(jarName, filepath.Ext(jarName)) + strconv.Itoa(idx) + ".kzip"
+		annoSrcJar = android.PathForModuleOut(ctx, "javac", "anno-"+strconv.Itoa(idx)+".srcjar")
 		jarName += strconv.Itoa(idx)
 	}
 
 	classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
-	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps)
+	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, annoSrcJar, flags, extraJarDeps)
 
 	if ctx.Config().EmitXrefRules() {
 		extractionFile := android.PathForModuleOut(ctx, kzipName)
@@ -1671,6 +1676,10 @@
 		j.kytheFiles = append(j.kytheFiles, extractionFile)
 	}
 
+	if len(flags.processorPath) > 0 {
+		j.annoSrcJars = append(j.annoSrcJars, annoSrcJar)
+	}
+
 	return classes
 }
 
@@ -1756,24 +1765,24 @@
 
 type providesTransitiveHeaderJars struct {
 	// set of header jars for all transitive libs deps
-	transitiveLibsHeaderJars *android.DepSet
+	transitiveLibsHeaderJars *android.DepSet[android.Path]
 	// set of header jars for all transitive static libs deps
-	transitiveStaticLibsHeaderJars *android.DepSet
+	transitiveStaticLibsHeaderJars *android.DepSet[android.Path]
 }
 
-func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet {
+func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet[android.Path] {
 	return j.transitiveLibsHeaderJars
 }
 
-func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet {
+func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet[android.Path] {
 	return j.transitiveStaticLibsHeaderJars
 }
 
 func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) {
 	directLibs := android.Paths{}
 	directStaticLibs := android.Paths{}
-	transitiveLibs := []*android.DepSet{}
-	transitiveStaticLibs := []*android.DepSet{}
+	transitiveLibs := []*android.DepSet[android.Path]{}
+	transitiveStaticLibs := []*android.DepSet[android.Path]{}
 	ctx.VisitDirectDeps(func(module android.Module) {
 		// don't add deps of the prebuilt version of the same library
 		if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
@@ -1850,6 +1859,7 @@
 	dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
 	dpInfo.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...)
 	dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...)
+	dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 }
 
 func (j *Module) CompilerDeps() []string {
@@ -2202,7 +2212,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.go b/java/bootclasspath.go
index f4cef7f..29eed79 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -77,7 +77,7 @@
 // Use gatherApexModulePairDepsWithTag to retrieve the dependencies.
 func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) {
 	var variations []blueprint.Variation
-	if apex != "platform" && apex != "system_ext" {
+	if !android.IsConfiguredJarForPlatform(apex) {
 		// Pick the correct apex variant.
 		variations = []blueprint.Variation{
 			{Mutator: "apex", Variation: apex},
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 108fdd4..ac45ce1 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -389,10 +389,6 @@
 // BootclasspathFragmentApexContentInfo contains the bootclasspath_fragments contributions to the
 // apex contents.
 type BootclasspathFragmentApexContentInfo struct {
-	// The configured modules, will be empty if this is from a bootclasspath_fragment that does not
-	// set image_name: "art".
-	modules android.ConfiguredJarList
-
 	// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
 	// hidden API encoded dex jar path.
 	contentModuleDexJarPaths bootDexJarByModule
@@ -405,10 +401,6 @@
 	profileInstallPathInApex string
 }
 
-func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList {
-	return i.modules
-}
-
 // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module.
 //
 // The dex boot jar is one which has had hidden API encoding performed on it.
@@ -479,8 +471,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)
 		}
@@ -599,7 +589,6 @@
 	}
 
 	if imageConfig != nil {
-		info.modules = imageConfig.modules
 		global := dexpreopt.GetGlobalConfig(ctx)
 		if !global.DisableGenerateProfile {
 			info.profilePathOnHost = bootImageFiles.profile
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 9bdef74..888caad 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -40,6 +40,12 @@
 				image_name: "unknown",
 				contents: ["foo"],
 			}
+
+			java_library {
+				name: "foo",
+				srcs: ["foo.java"],
+				installable: true,
+			}
 		`)
 }
 
@@ -53,6 +59,11 @@
 				image_name: "unknown",
 				contents: ["foo"],
 			}
+
+			java_import {
+				name: "foo",
+				jars: ["foo.jar"],
+			}
 		`)
 }
 
@@ -72,6 +83,18 @@
 					"apex",
 				],
 			}
+
+			java_library {
+				name: "foo",
+				srcs: ["foo.java"],
+				installable: true,
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["bar.java"],
+				installable: true,
+			}
 		`)
 }
 
@@ -92,6 +115,18 @@
 					"apex2",
 				],
 			}
+
+			java_library {
+				name: "foo",
+				srcs: ["foo.java"],
+				installable: true,
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["bar.java"],
+				installable: true,
+			}
 		`)
 }
 
@@ -432,39 +467,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/builder.go b/java/builder.go
index c4395e9..be4af55 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -42,7 +42,8 @@
 	// TODO(b/143658984): goma can't handle the --system argument to javac.
 	javac, javacRE = pctx.MultiCommandRemoteStaticRules("javac",
 		blueprint.RuleParams{
-			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" "$out" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
+			Command: `rm -rf "$outDir" "$annoDir" "$annoSrcJar" "$srcJarDir" "$out" && ` +
+				`mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
 				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
@@ -50,6 +51,7 @@
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
+				`$annoSrcJarTemplate${config.SoongZipCmd} -jar -o $annoSrcJar -C $annoDir -D $annoDir && ` +
 				`$zipTemplate${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
 				`rm -rf "$srcJarDir"`,
 			CommandDeps: []string{
@@ -73,8 +75,15 @@
 				ExecStrategy: "${config.REJavacExecStrategy}",
 				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 			},
+			"$annoSrcJarTemplate": &remoteexec.REParams{
+				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+				Inputs:       []string{"${config.SoongZipCmd}", "$annoDir"},
+				OutputFiles:  []string{"$annoSrcJar"},
+				ExecStrategy: "${config.REJavacExecStrategy}",
+				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+			},
 		}, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
-			"outDir", "annoDir", "javaVersion"}, nil)
+			"outDir", "annoDir", "annoSrcJar", "javaVersion"}, nil)
 
 	_ = pctx.VariableFunc("kytheCorpus",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
@@ -312,7 +321,7 @@
 }
 
 func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
-	srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) {
+	srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths) {
 
 	// Compile java sources into .class files
 	desc := "javac"
@@ -320,7 +329,7 @@
 		desc += strconv.Itoa(shardIdx)
 	}
 
-	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
+	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, annoSrcJar, flags, deps, "javac", desc)
 }
 
 // Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
@@ -494,7 +503,7 @@
 // suffix will be appended to various intermediate files and directories to avoid collisions when
 // this function is called twice in the same module directory.
 func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
-	shardIdx int, srcFiles, srcJars android.Paths,
+	shardIdx int, srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath,
 	flags javaBuilderFlags, deps android.Paths,
 	intermediatesDir, desc string) {
 
@@ -541,11 +550,12 @@
 		rule = javacRE
 	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        rule,
-		Description: desc,
-		Output:      outputFile,
-		Inputs:      srcFiles,
-		Implicits:   deps,
+		Rule:           rule,
+		Description:    desc,
+		Output:         outputFile,
+		ImplicitOutput: annoSrcJar,
+		Inputs:         srcFiles,
+		Implicits:      deps,
 		Args: map[string]string{
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
@@ -556,6 +566,7 @@
 			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
 			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
 			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
+			"annoSrcJar":    annoSrcJar.String(),
 			"javaVersion":   flags.javaVersion.String(),
 		},
 	})
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_bootjars.go b/java/dexpreopt_bootjars.go
index 35f6097..7b56a19 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -505,8 +505,7 @@
 		// No module has enabled dexpreopting, so we assume there will be no boot image to make.
 		return
 	}
-	archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
-	d.dexpreoptConfigForMake = android.PathForOutput(ctx, toDexpreoptDirName(archType), "dexpreopt.config")
+	d.dexpreoptConfigForMake = android.PathForOutput(ctx, getDexpreoptDirName(ctx), "dexpreopt.config")
 	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
 
 	global := dexpreopt.GetGlobalConfig(ctx)
@@ -789,7 +788,6 @@
 		Flag("--generate-build-id").
 		Flag("--image-format=lz4hc").
 		FlagWithArg("--oat-symbols=", symbolsFile.String()).
-		Flag("--strip").
 		FlagWithArg("--oat-file=", outputPath.String()).
 		FlagWithArg("--oat-location=", oatLocation).
 		FlagWithArg("--image=", imagePath.String()).
@@ -799,6 +797,11 @@
 		Flag("--force-determinism").
 		Flag("--abort-on-hard-verifier-error")
 
+	// We don't strip on host to make perf tools work.
+	if image.target.Os == android.Android {
+		cmd.Flag("--strip")
+	}
+
 	// If the image is profile-guided but the profile is disabled, we omit "--compiler-filter" to
 	// leave the decision to dex2oat to pick the compiler filter.
 	if !(image.isProfileGuided() && global.DisableGenerateProfile) {
@@ -881,11 +884,7 @@
 It is likely that the boot classpath is inconsistent.
 Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
 
-func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
-	if !image.isProfileGuided() {
-		return nil
-	}
-
+func bootImageProfileRuleCommon(ctx android.ModuleContext, name string, dexFiles android.Paths, dexLocations []string) android.WritablePath {
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
@@ -912,28 +911,39 @@
 	if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() {
 		profiles = append(profiles, path.Path())
 	}
-	bootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
+	bootImageProfile := android.PathForModuleOut(ctx, name, "boot-image-profile.txt")
 	rule.Command().Text("cat").Inputs(profiles).Text(">").Output(bootImageProfile)
 
-	profile := image.dir.Join(ctx, "boot.prof")
+	profile := android.PathForModuleOut(ctx, name, "boot.prof")
 
 	rule.Command().
 		Text(`ANDROID_LOG_TAGS="*:e"`).
 		Tool(globalSoong.Profman).
 		Flag("--output-profile-type=boot").
 		FlagWithInput("--create-profile-from=", bootImageProfile).
-		FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
+		FlagForEachInput("--apk=", dexFiles).
+		FlagForEachArg("--dex-location=", dexLocations).
 		FlagWithOutput("--reference-profile-file=", profile)
 
+	rule.Build("bootJarsProfile_"+name, "profile boot jars "+name)
+
+	return profile
+}
+
+func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+	if !image.isProfileGuided() {
+		return nil
+	}
+
+	profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps)
+
 	if image == defaultBootImageConfig(ctx) {
+		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Install(profile, "/system/etc/boot-image.prof")
 		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
 		image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
 	}
 
-	rule.Build("bootJarsProfile", "profile boot jars")
-
 	return profile
 }
 
@@ -972,6 +982,8 @@
 
 func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
 	var allPhonies android.Paths
+	name := image.name
+	global := dexpreopt.GetGlobalConfig(ctx)
 	for _, image := range image.variants {
 		arch := image.target.Arch.ArchType
 		suffix := arch.String()
@@ -980,36 +992,39 @@
 			suffix = "host-" + suffix
 		}
 		// Create a rule to call oatdump.
-		output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt")
+		output := android.PathForOutput(ctx, name+"."+suffix+".oatdump.txt")
 		rule := android.NewRuleBuilder(pctx, ctx)
 		imageLocationsOnHost, _ := image.imageLocations()
-		rule.Command().
+		cmd := rule.Command().
 			BuiltTool("oatdump").
 			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
 			FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()).
 			FlagWithOutput("--output=", output).
 			FlagWithArg("--instruction-set=", arch.String())
-		rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
+		if global.EnableUffdGc && image.target.Os == android.Android {
+			cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
+		}
+		rule.Build("dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String())
 
 		// Create a phony rule that depends on the output file and prints the path.
-		phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix)
+		phony := android.PathForPhony(ctx, "dump-oat-"+name+"-"+suffix)
 		rule = android.NewRuleBuilder(pctx, ctx)
 		rule.Command().
 			Implicit(output).
 			ImplicitOutput(phony).
 			Text("echo").FlagWithArg("Output in ", output.String())
-		rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
+		rule.Build("phony-dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String())
 
 		allPhonies = append(allPhonies, phony)
 	}
 
-	phony := android.PathForPhony(ctx, "dump-oat-boot")
+	phony := android.PathForPhony(ctx, "dump-oat-"+name)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        android.Phony,
 		Output:      phony,
 		Inputs:      allPhonies,
-		Description: "dump-oat-boot",
+		Description: "dump-oat-"+name,
 	})
 }
 
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 9100e87..abbb96a 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -105,8 +105,7 @@
 func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
 	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
 		targets := dexpreoptTargets(ctx)
-		archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
-		deviceDir := android.PathForOutput(ctx, toDexpreoptDirName(archType))
+		deviceDir := android.PathForOutput(ctx, getDexpreoptDirName(ctx))
 
 		configs := genBootImageConfigRaw(ctx)
 
@@ -218,8 +217,7 @@
 func GetApexBootConfig(ctx android.PathContext) apexBootConfig {
 	return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
 		apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
-		archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
-		dir := android.PathForOutput(ctx, toDexpreoptDirName(archType), "apex_bootjars")
+		dir := android.PathForOutput(ctx, getDexpreoptDirName(ctx), "apex_bootjars")
 		dexPaths := apexBootJars.BuildPaths(ctx, dir)
 		dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir)
 
@@ -250,8 +248,6 @@
 
 var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
 
-var copyOf = android.CopyOf
-
 func init() {
 	android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars)
 }
@@ -260,6 +256,11 @@
 	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
 }
 
-func toDexpreoptDirName(arch android.ArchType) string {
-	return "dexpreopt_" + arch.String()
+func getDexpreoptDirName(ctx android.PathContext) string {
+	prefix := "dexpreopt_"
+	targets := ctx.Config().Targets[android.Android]
+	if len(targets) > 0 {
+		return prefix+targets[0].Arch.ArchType.String()
+	}
+	return prefix+"unknown_target"
 }
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 6f3aa2b..56f16e0 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -44,18 +44,18 @@
 
 var PrepareApexBootJarConfigsAndModules = android.GroupFixturePreparers(
 	PrepareApexBootJarConfigs,
-	prepareApexBootJarModule("com.android.foo", "framework-foo"),
-	prepareApexBootJarModule("com.android.bar", "framework-bar"),
+	PrepareApexBootJarModule("com.android.foo", "framework-foo"),
+	PrepareApexBootJarModule("com.android.bar", "framework-bar"),
 )
 
 var ApexBootJarFragmentsForPlatformBootclasspath = fmt.Sprintf(`
 	{
 		apex: "%[1]s",
-		module: "%[1]s-bootclasspathfragment",
+		module: "%[1]s-bootclasspath-fragment",
 	},
 	{
 		apex: "%[2]s",
-		module: "%[2]s-bootclasspathfragment",
+		module: "%[2]s-bootclasspath-fragment",
 	},
 `, "com.android.foo", "com.android.bar")
 
@@ -64,15 +64,22 @@
 	"out/soong/.intermediates/packages/modules/com.android.foo/framework-foo/android_common_apex10000/aligned/framework-foo.jar",
 }
 
-func prepareApexBootJarModule(apexName string, moduleName string) android.FixturePreparer {
+func PrepareApexBootJarModule(apexName string, moduleName string) android.FixturePreparer {
 	moduleSourceDir := fmt.Sprintf("packages/modules/%s", apexName)
+	fragmentName := apexName+"-bootclasspath-fragment"
+	imageNameProp := ""
+	if apexName == "com.android.art" {
+		fragmentName = "art-bootclasspath-fragment"
+		imageNameProp = `image_name: "art",`
+	}
+
 	return android.GroupFixturePreparers(
 		android.FixtureAddTextFile(moduleSourceDir+"/Android.bp", fmt.Sprintf(`
 			apex {
 				name: "%[1]s",
 				key: "%[1]s.key",
 				bootclasspath_fragments: [
-					"%[1]s-bootclasspathfragment",
+					"%[3]s",
 				],
 				updatable: false,
 			}
@@ -84,7 +91,8 @@
 			}
 
 			bootclasspath_fragment {
-				name: "%[1]s-bootclasspathfragment",
+				name: "%[3]s",
+				%[4]s
 				contents: ["%[2]s"],
 				apex_available: ["%[1]s"],
 				hidden_api: {
@@ -100,7 +108,7 @@
 				compile_dex: true,
 				apex_available: ["%[1]s"],
 			}
-		`, apexName, moduleName)),
+		`, apexName, moduleName, fragmentName, imageNameProp)),
 		android.FixtureMergeMockFs(android.MockFS{
 			fmt.Sprintf("%s/apex_manifest.json", moduleSourceDir):          nil,
 			fmt.Sprintf("%s/%s.avbpubkey", moduleSourceDir, apexName):      nil,
@@ -192,7 +200,7 @@
 // getArtImageConfig gets the ART bootImageConfig that was created during the test.
 func getArtImageConfig(result *android.TestResult) *bootImageConfig {
 	pathCtx := &android.TestPathContext{TestResult: result}
-	imageConfig := artBootImageConfig(pathCtx)
+	imageConfig := genBootImageConfigs(pathCtx)["art"]
 	return imageConfig
 }
 
@@ -806,7 +814,7 @@
 		},
 		profileInstalls: []normalizedInstall{
 			{from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"},
-			{from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"},
+			{from: "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/boot/boot.prof", to: "/system/etc/boot-image.prof"},
 		},
 		profileLicenseMetadataFile: expectedLicenseMetadataFile,
 	}
@@ -1136,7 +1144,6 @@
 	android.AssertPathRelativeToTopEquals(t, "dir", expected.dir, imageConfig.dir)
 	android.AssertPathRelativeToTopEquals(t, "symbolsDir", expected.symbolsDir, imageConfig.symbolsDir)
 	android.AssertStringEquals(t, "installDir", expected.installDir, imageConfig.installDir)
-	android.AssertStringEquals(t, "profileInstallPathInApex", expected.profileInstallPathInApex, imageConfig.profileInstallPathInApex)
 	android.AssertDeepEquals(t, "modules", expected.modules, imageConfig.modules)
 	android.AssertPathsRelativeToTopEquals(t, "dexPaths", expected.dexPaths, imageConfig.dexPaths.Paths())
 	android.AssertPathsRelativeToTopEquals(t, "dexPathsDeps", expected.dexPathsDeps, imageConfig.dexPathsDeps.Paths())
@@ -1238,7 +1245,7 @@
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art
 DEXPREOPT_IMAGE_NAMES=art boot mainline
-DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/dexpreopt_arm64/dex_bootjars/boot.prof:/system/etc/boot-image.prof
+DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/boot/boot.prof:/system/etc/boot-image.prof
 DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 151c94a..bb2388f 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -525,8 +525,7 @@
 		cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
 	}
 
-	cmd.Flag("--no-banner").
-		Flag("--color").
+	cmd.Flag("--color").
 		Flag("--quiet").
 		Flag("--format=v2").
 		FlagWithArg("--repeat-errors-max ", "10").
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index da9c997..f31f5d1 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.
@@ -1236,7 +1236,6 @@
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
 		BuiltTool("metalava").
-		Flag("--no-banner").
 		Inputs(removedTxtFiles).
 		FlagWithOutput("--dex-api ", output)
 	rule.Build("modular-hiddenapi-removed-dex-signatures"+suffix, "modular hiddenapi removed dex signatures"+suffix)
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index d4ee4fc..714634f 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -166,7 +166,7 @@
 
 	// Now match the apex part of the boot image configuration.
 	requiredApex := configuredBootJars.Apex(index)
-	if requiredApex == "platform" || requiredApex == "system_ext" {
+	if android.IsConfiguredJarForPlatform(requiredApex) {
 		if len(apexInfo.InApexVariants) != 0 {
 			// A platform variant is required but this is for an apex so ignore it.
 			return false
diff --git a/java/java.go b/java/java.go
index a026610..d3762f6 100644
--- a/java/java.go
+++ b/java/java.go
@@ -231,10 +231,10 @@
 	HeaderJars android.Paths
 
 	// set of header jars for all transitive libs deps
-	TransitiveLibsHeaderJars *android.DepSet
+	TransitiveLibsHeaderJars *android.DepSet[android.Path]
 
 	// set of header jars for all transitive static libs deps
-	TransitiveStaticLibsHeaderJars *android.DepSet
+	TransitiveStaticLibsHeaderJars *android.DepSet[android.Path]
 
 	// ImplementationAndResourceJars is a list of jars that contain the implementations of classes
 	// in the module as well as any resources included in the module.
@@ -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() {
@@ -1624,13 +1624,6 @@
 	})
 }
 
-type JavaApiLibraryDepsInfo struct {
-	JavaInfo
-	StubsSrcJar android.Path
-}
-
-var JavaApiLibraryDepsProvider = blueprint.NewProvider(JavaApiLibraryDepsInfo{})
-
 type ApiLibrary struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -1672,10 +1665,11 @@
 	// merge zipped after metalava invocation
 	Static_libs []string
 
-	// Java Api library to provide the full API surface text files and jar file.
-	// If this property is set, the provided full API surface text files and
-	// jar file are passed to metalava invocation.
-	Dep_api_srcs *string
+	// Java Api library to provide the full API surface stub jar file.
+	// If this property is set, the stub jar of this module is created by
+	// extracting the compiled class files provided by the
+	// full_api_surface_stub module.
+	Full_api_surface_stub *string
 }
 
 func ApiLibraryFactory() android.Module {
@@ -1722,8 +1716,7 @@
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithInputList("--source-files ", srcs, " ")
 
-	cmd.Flag("--no-banner").
-		Flag("--color").
+	cmd.Flag("--color").
 		Flag("--quiet").
 		Flag("--format=v2").
 		Flag("--include-annotations").
@@ -1762,35 +1755,37 @@
 	}
 }
 
-// This method extracts the stub java files from the srcjar file provided from dep_api_srcs module
-// and replaces the java stubs generated by invoking metalava in this module.
+// This method extracts the stub class files from the stub jar file provided
+// from full_api_surface_stub module instead of compiling the srcjar generated from invoking metalava.
 // This method is used because metalava can generate compilable from-text stubs only when
-// the codebase encompasses all classes listed in the input API text file, but a class can extend
+// the codebase encompasses all classes listed in the input API text file, and a class can extend
 // a class that is not within the same API domain.
-func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, depApiSrcsSrcJar android.Path) {
-	generatedStubsList := android.PathForModuleOut(ctx, "metalava", "sources.txt")
+func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, fullApiSurfaceStubJar android.Path) {
+	classFilesList := android.PathForModuleOut(ctx, "metalava", "classes.txt")
 	unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir")
 
 	rule.Command().
 		BuiltTool("list_files").
 		Text(stubsDir.String()).
-		FlagWithOutput("--out ", generatedStubsList).
+		FlagWithOutput("--out ", classFilesList).
 		FlagWithArg("--extensions ", ".java").
-		FlagWithArg("--root ", unzippedSrcJarDir.String())
+		FlagWithArg("--root ", unzippedSrcJarDir.String()).
+		Flag("--classes")
 
 	rule.Command().
 		Text("unzip").
 		Flag("-q").
-		Input(depApiSrcsSrcJar).
+		Input(fullApiSurfaceStubJar).
 		FlagWithArg("-d ", unzippedSrcJarDir.String())
 
 	rule.Command().
 		BuiltTool("soong_zip").
-		Flag("-srcjar").
+		Flag("-jar").
 		Flag("-write_if_changed").
+		Flag("-ignore_missing_files").
 		FlagWithArg("-C ", unzippedSrcJarDir.String()).
-		FlagWithInput("-l ", generatedStubsList).
-		FlagWithOutput("-o ", al.stubsSrcJar)
+		FlagWithInput("-l ", classFilesList).
+		FlagWithOutput("-o ", al.stubsJarWithoutStaticLibs)
 }
 
 func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -1800,8 +1795,8 @@
 	}
 	ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
 	ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...)
-	if al.properties.Dep_api_srcs != nil {
-		ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Dep_api_srcs))
+	if al.properties.Full_api_surface_stub != nil {
+		ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Full_api_surface_stub))
 	}
 }
 
@@ -1823,7 +1818,7 @@
 	var srcFiles android.Paths
 	var classPaths android.Paths
 	var staticLibs android.Paths
-	var depApiSrcsStubsSrcJar android.Path
+	var depApiSrcsStubsJar android.Path
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
@@ -1841,9 +1836,8 @@
 			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
 			staticLibs = append(staticLibs, provider.HeaderJars...)
 		case depApiSrcsTag:
-			provider := ctx.OtherModuleProvider(dep, JavaApiLibraryDepsProvider).(JavaApiLibraryDepsInfo)
-			classPaths = append(classPaths, provider.HeaderJars...)
-			depApiSrcsStubsSrcJar = provider.StubsSrcJar
+			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			depApiSrcsStubsJar = provider.HeaderJars[0]
 		}
 	})
 
@@ -1861,31 +1855,33 @@
 	al.stubsFlags(ctx, cmd, stubsDir)
 
 	al.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
+	al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, "metalava", "stubs.jar")
+	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
 
-	if depApiSrcsStubsSrcJar != nil {
-		al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsSrcJar)
-	} else {
-		rule.Command().
-			BuiltTool("soong_zip").
-			Flag("-write_if_changed").
-			Flag("-jar").
-			FlagWithOutput("-o ", al.stubsSrcJar).
-			FlagWithArg("-C ", stubsDir.String()).
-			FlagWithArg("-D ", stubsDir.String())
+	if depApiSrcsStubsJar != nil {
+		al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsJar)
 	}
+	rule.Command().
+		BuiltTool("soong_zip").
+		Flag("-write_if_changed").
+		Flag("-jar").
+		FlagWithOutput("-o ", al.stubsSrcJar).
+		FlagWithArg("-C ", stubsDir.String()).
+		FlagWithArg("-D ", stubsDir.String())
 
 	rule.Build("metalava", "metalava merged")
 
-	al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, ctx.ModuleName(), "stubs.jar")
-	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
+	if depApiSrcsStubsJar == nil {
+		var flags javaBuilderFlags
+		flags.javaVersion = getStubsJavaVersion()
+		flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
+		flags.classpath = classpath(classPaths)
 
-	var flags javaBuilderFlags
-	flags.javaVersion = getStubsJavaVersion()
-	flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
-	flags.classpath = classpath(classPaths)
+		annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar")
 
-	TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
-		android.Paths{al.stubsSrcJar}, flags, android.Paths{})
+		TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
+			android.Paths{al.stubsSrcJar}, annoSrcJar, flags, android.Paths{})
+	}
 
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().
@@ -1917,13 +1913,6 @@
 		ImplementationJars:             android.PathsIfNonNil(al.stubsJar),
 		AidlIncludeDirs:                android.Paths{},
 	})
-
-	ctx.SetProvider(JavaApiLibraryDepsProvider, JavaApiLibraryDepsInfo{
-		JavaInfo: JavaInfo{
-			HeaderJars: android.PathsIfNonNil(al.stubsJar),
-		},
-		StubsSrcJar: al.stubsSrcJar,
-	})
 }
 
 func (al *ApiLibrary) DexJarBuildPath() OptionalDexJarPath {
diff --git a/java/java_test.go b/java/java_test.go
index 561b187..4738304 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2208,7 +2208,7 @@
 	}
 }
 
-func TestJavaApiLibraryDepApiSrcs(t *testing.T) {
+func TestJavaApiLibraryFullApiSurfaceStub(t *testing.T) {
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2234,7 +2234,7 @@
 			name: "bar1",
 			api_surface: "public",
 			api_contributions: ["foo1"],
-			dep_api_srcs: "lib1",
+			full_api_surface_stub: "lib1",
 		}
 		`,
 		map[string][]byte{
@@ -2247,9 +2247,7 @@
 	manifest := m.Output("metalava.sbox.textproto")
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
-
-	android.AssertStringDoesContain(t, "Command expected to contain module srcjar file", manifestCommand, "bar1-stubs.srcjar")
-	android.AssertStringDoesContain(t, "Command expected to contain output files list text file flag", manifestCommand, "--out __SBOX_SANDBOX_DIR__/out/sources.txt")
+	android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar")
 }
 
 func TestJavaApiLibraryFilegroupInput(t *testing.T) {
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/lint.go b/java/lint.go
index a0f9970..f84f1c0 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -117,18 +117,18 @@
 }
 
 type LintDepSets struct {
-	HTML, Text, XML *android.DepSet
+	HTML, Text, XML *android.DepSet[android.Path]
 }
 
 type LintDepSetsBuilder struct {
-	HTML, Text, XML *android.DepSetBuilder
+	HTML, Text, XML *android.DepSetBuilder[android.Path]
 }
 
 func NewLintDepSetBuilder() LintDepSetsBuilder {
 	return LintDepSetsBuilder{
-		HTML: android.NewDepSetBuilder(android.POSTORDER),
-		Text: android.NewDepSetBuilder(android.POSTORDER),
-		XML:  android.NewDepSetBuilder(android.POSTORDER),
+		HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		Text: android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		XML:  android.NewDepSetBuilder[android.Path](android.POSTORDER),
 	}
 }
 
@@ -553,9 +553,9 @@
 }
 
 func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
-	htmlList := depSets.HTML.ToSortedList()
-	textList := depSets.Text.ToSortedList()
-	xmlList := depSets.XML.ToSortedList()
+	htmlList := android.SortedUniquePaths(depSets.HTML.ToList())
+	textList := android.SortedUniquePaths(depSets.Text.ToList())
+	xmlList := android.SortedUniquePaths(depSets.XML.ToList())
 
 	if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
 		return nil
diff --git a/java/resourceshrinker.go b/java/resourceshrinker.go
index 6d59601..bf1b04d 100644
--- a/java/resourceshrinker.go
+++ b/java/resourceshrinker.go
@@ -22,7 +22,8 @@
 
 var shrinkResources = pctx.AndroidStaticRule("shrinkResources",
 	blueprint.RuleParams{
-		Command:     `${config.ResourceShrinkerCmd} --output $out --input $in --raw_resources $raw_resources`,
+		// Note that we suppress stdout to avoid successful log confirmations.
+		Command:     `${config.ResourceShrinkerCmd} --output $out --input $in --raw_resources $raw_resources >/dev/null`,
 		CommandDeps: []string{"${config.ResourceShrinkerCmd}"},
 	}, "raw_resources")
 
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/java/sdk_library.go b/java/sdk_library.go
index a3d81ce..dbb2f02 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1780,12 +1780,12 @@
 
 func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
-		Name              *string
-		Visibility        []string
-		Api_contributions []string
-		Libs              []string
-		Static_libs       []string
-		Dep_api_srcs      *string
+		Name                  *string
+		Visibility            []string
+		Api_contributions     []string
+		Libs                  []string
+		Static_libs           []string
+		Full_api_surface_stub *string
 	}{}
 
 	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
@@ -1807,12 +1807,12 @@
 	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
 	props.Libs = append(props.Libs, "stub-annotations")
 	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
-	props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + ".from-text")
+	props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + ".from-text")
 
 	// android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n.
 	// Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains.
 	if apiScope.kind == android.SdkModule {
-		props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
+		props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
 	}
 
 	mctx.CreateModule(ApiLibraryFactory, &props)
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 7ba1f6d..c22b980 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -1442,35 +1442,35 @@
 	`)
 
 	testCases := []struct {
-		scope            *apiScope
-		apiContributions []string
-		depApiSrcs       string
+		scope              *apiScope
+		apiContributions   []string
+		fullApiSurfaceStub string
 	}{
 		{
-			scope:            apiScopePublic,
-			apiContributions: []string{"foo.stubs.source.api.contribution"},
-			depApiSrcs:       "android_stubs_current.from-text",
+			scope:              apiScopePublic,
+			apiContributions:   []string{"foo.stubs.source.api.contribution"},
+			fullApiSurfaceStub: "android_stubs_current.from-text",
 		},
 		{
-			scope:            apiScopeSystem,
-			apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			depApiSrcs:       "android_system_stubs_current.from-text",
+			scope:              apiScopeSystem,
+			apiContributions:   []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			fullApiSurfaceStub: "android_system_stubs_current.from-text",
 		},
 		{
-			scope:            apiScopeTest,
-			apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			depApiSrcs:       "android_test_stubs_current.from-text",
+			scope:              apiScopeTest,
+			apiContributions:   []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			fullApiSurfaceStub: "android_test_stubs_current.from-text",
 		},
 		{
-			scope:            apiScopeModuleLib,
-			apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			depApiSrcs:       "android_module_lib_stubs_current_full.from-text",
+			scope:              apiScopeModuleLib,
+			apiContributions:   []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			fullApiSurfaceStub: "android_module_lib_stubs_current_full.from-text",
 		},
 	}
 
 	for _, c := range testCases {
 		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
 		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
-		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.depApiSrcs, *m.properties.Dep_api_srcs)
+		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.fullApiSurfaceStub, *m.properties.Full_api_surface_stub)
 	}
 }
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 96645b0..7dc1b4b 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -247,7 +247,7 @@
 
 	var cmd, cmdDesc string
 	if b.Properties.Custom_bindgen != "" {
-		cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(*Module).HostToolPath().String()
+		cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(android.HostToolProvider).HostToolPath().String()
 		cmdDesc = b.Properties.Custom_bindgen
 	} else {
 		cmd = "$bindgenCmd"
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/rust/config/global.go b/rust/config/global.go
index f838d0e..d844a25 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -41,6 +41,7 @@
 	}
 
 	GlobalRustFlags = []string{
+		"-Z stack-protector=strong",
 		"-Z remap-cwd-prefix=.",
 		"-C codegen-units=1",
 		"-C debuginfo=2",
diff --git a/rust/library.go b/rust/library.go
index ca5ad14..331763a 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -566,7 +566,7 @@
 	}
 
 	if library.static() {
-		depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputFile).Build()
+		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(outputFile).Build()
 		ctx.SetProvider(cc.StaticLibraryInfoProvider, cc.StaticLibraryInfo{
 			StaticLibrary: outputFile,
 
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 894109a..ac23aab 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -86,27 +86,27 @@
 
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(
-			"com.android.art", "mybootclasspathfragment", java.ApexBootJarFragmentsForPlatformBootclasspath),
+			"com.android.art", "art-bootclasspath-fragment", java.ApexBootJarFragmentsForPlatformBootclasspath),
 
 		java.PrepareForBootImageConfigTest,
 		java.PrepareApexBootJarConfigsAndModules,
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
-				bootclasspath_fragments: ["mybootclasspathfragment"],
+				bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			}
 
 			apex {
 				name: "com.android.art",
 				key: "com.android.art.key",
 				bootclasspath_fragments: [
-					"mybootclasspathfragment",
+					"art-bootclasspath-fragment",
 				],
 				updatable: false,
 			}
 
 			bootclasspath_fragment {
-				name: "mybootclasspathfragment",
+				name: "art-bootclasspath-fragment",
 				image_name: "art",
 				contents: ["core1", "core2"],
 				apex_available: ["com.android.art"],
@@ -142,10 +142,10 @@
 	).RunTest(t)
 
 	// A preparer to update the test fixture used when processing an unpackage snapshot.
-	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
+	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "art-bootclasspath-fragment")
 
 	// Check that source on its own configures the bootImageConfig correctly.
-	java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/meta_lic")
+	java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/meta_lic")
 	java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
 
 	CheckSnapshot(t, result, "mysdk", "",
@@ -153,7 +153,7 @@
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
-    name: "mybootclasspathfragment",
+    name: "art-bootclasspath-fragment",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
@@ -189,12 +189,12 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar
 		`),
@@ -213,7 +213,7 @@
 					java.ApexBootJarDexJarPaths...,
 				)...,
 			)
-			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/mybootclasspathfragment/android_common_com.android.art/meta_lic")
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/art-bootclasspath-fragment/android_common_com.android.art/meta_lic")
 			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
 		}),
 
@@ -221,7 +221,7 @@
 
 		// Check the behavior of the snapshot when the source is preferred.
 		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
-			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/meta_lic")
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/meta_lic")
 			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
 		}),
 
@@ -229,7 +229,7 @@
 
 		// Check the behavior of the snapshot when it is preferred.
 		snapshotTestChecker(checkSnapshotPreferredWithSource, func(t *testing.T, result *android.TestResult) {
-			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/prebuilt_mybootclasspathfragment/android_common_com.android.art/meta_lic")
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/prebuilt_art-bootclasspath-fragment/android_common_com.android.art/meta_lic")
 			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
 		}),
 	)
@@ -1194,3 +1194,58 @@
 			expectedSnapshot, expectedCopyRules, expectedStubFlagsInputs, "")
 	})
 }
+
+func TestSnapshotWithEmptyBootClasspathFragment(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("mysdklibrary", "mynewsdklibrary"),
+		java.FixtureConfigureApexBootJars("myapex:mysdklibrary", "myapex:mynewsdklibrary"),
+		prepareForSdkTestWithApex,
+		// Add a platform_bootclasspath that depends on the fragment.
+		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
+		android.FixtureMergeEnv(map[string]string{
+			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
+		}),
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				min_sdk_version: "S",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+			}
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				apex_available: ["myapex"],
+				contents: ["mysdklibrary", "mynewsdklibrary"],
+				hidden_api: {
+					split_packages: [],
+				},
+			}
+			java_sdk_library {
+				name: "mysdklibrary",
+				apex_available: ["myapex"],
+				srcs: ["Test.java"],
+				shared_library: false,
+				public: {enabled: true},
+				min_sdk_version: "Tiramisu",
+			}
+			java_sdk_library {
+				name: "mynewsdklibrary",
+				apex_available: ["myapex"],
+				srcs: ["Test.java"],
+				compile_dex: true,
+				public: {enabled: true},
+				min_sdk_version: "Tiramisu",
+				permitted_packages: ["mynewsdklibrary"],
+			}
+		`),
+	).RunTest(t)
+
+	CheckSnapshot(t, result, "mysdk", "", checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.`))
+}
diff --git a/sdk/bp.go b/sdk/bp.go
index 7ff85a1..57eb2ca 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -311,13 +311,15 @@
 }
 
 func (m *bpModule) deepCopy() *bpModule {
-	return m.transform(deepCopyTransformer)
+	return transformModule(m, deepCopyTransformer)
 }
 
-func (m *bpModule) transform(transformer bpTransformer) *bpModule {
+func transformModule(m *bpModule, transformer bpTransformer) *bpModule {
 	transformedModule := transformer.transformModule(m)
-	// Copy the contents of the returned property set into the module and then transform that.
-	transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
+	if transformedModule != nil {
+		// Copy the contents of the returned property set into the module and then transform that.
+		transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
+	}
 	return transformedModule
 }
 
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 66c44c8..7ccc114 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -86,6 +86,51 @@
 	)
 }
 
+func TestSnapshotWithEmptySystemServerClasspathFragment(t *testing.T) {
+	commonSdk := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			min_sdk_version: "Tiramisu",
+			systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+		}
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspathfragment",
+			apex_available: ["myapex"],
+			contents: ["mysdklibrary"],
+		}
+		java_sdk_library {
+			name: "mysdklibrary",
+			apex_available: ["myapex"],
+			srcs: ["Test.java"],
+			min_sdk_version: "34", // UpsideDownCake
+		}
+		sdk {
+			name: "mysdk",
+			apexes: ["myapex"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("mysdklibrary"),
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:mysdklibrary"),
+		android.FixtureModifyEnv(func(env map[string]string) {
+			// targeting Tiramisu here means that we won't export mysdklibrary
+			env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = "Tiramisu"
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"UpsideDownCake"}
+		}),
+		prepareForSdkTestWithApex,
+		android.FixtureWithRootAndroidBp(commonSdk),
+	).RunTest(t)
+
+	CheckSnapshot(t, result, "mysdk", "", checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.`))
+}
+
 func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
 
 	commonSdk := `
diff --git a/sdk/update.go b/sdk/update.go
index d3c59b0..4c39fae 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -455,11 +455,14 @@
 
 	for _, module := range builder.prebuiltOrder {
 		// Prune any empty property sets.
-		module = module.transform(pruneEmptySetTransformer{})
+		module = transformModule(module, pruneEmptySetTransformer{})
 
 		// Transform the module module to make it suitable for use in the snapshot.
-		module.transform(snapshotTransformer)
-		bpFile.AddModule(module)
+		module = transformModule(module, snapshotTransformer)
+		module = transformModule(module, emptyClasspathContentsTransformation{})
+		if module != nil {
+			bpFile.AddModule(module)
+		}
 	}
 
 	// generate Android.bp
@@ -835,9 +838,11 @@
 }
 
 func (t snapshotTransformation) transformModule(module *bpModule) *bpModule {
-	// If the module is an internal member then use a unique name for it.
-	name := module.Name()
-	module.setProperty("name", t.builder.snapshotSdkMemberName(name, true))
+	if module != nil {
+		// If the module is an internal member then use a unique name for it.
+		name := module.Name()
+		module.setProperty("name", t.builder.snapshotSdkMemberName(name, true))
+	}
 	return module
 }
 
@@ -850,6 +855,25 @@
 	}
 }
 
+type emptyClasspathContentsTransformation struct {
+	identityTransformation
+}
+
+func (t emptyClasspathContentsTransformation) transformModule(module *bpModule) *bpModule {
+	classpathModuleTypes := []string{
+		"prebuilt_bootclasspath_fragment",
+		"prebuilt_systemserverclasspath_fragment",
+	}
+	if module != nil && android.InList(module.moduleType, classpathModuleTypes) {
+		if contents, ok := module.bpPropertySet.properties["contents"].([]string); ok {
+			if len(contents) == 0 {
+				return nil
+			}
+		}
+	}
+	return module
+}
+
 type pruneEmptySetTransformer struct {
 	identityTransformation
 }
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/sbom_test.sh b/tests/sbom_test.sh
index 94fe51d..1241e89 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -23,194 +23,273 @@
   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/libcuttlefish_allocd_utils.so \
+    -I /system/lib64/libcuttlefish_device_config_proto.so \
+    -I /system/lib64/libcuttlefish_device_config.so \
+    -I /system/lib64/libcuttlefish_fs.so \
+    -I /system/lib64/libcuttlefish_kernel_log_monitor_utils.so \
+    -I /system/lib64/libcuttlefish_utils.so \
+    -I /system/lib64/libfruit.so \
+    -I /system/lib64/libgflags.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}"
+}
+
+function test_sbom_unbundled_apex {
+  # Setup
+  out_dir="$(setup)"
+
+  # run_soong to build com.android.adbd.apex
+  run_soong "module_arm64" "${out_dir}" "sbom deapexer" "com.android.adbd"
+
+  deapexer=${out_dir}/host/linux-x86/bin/deapexer
+  debugfs=${out_dir}/host/linux-x86/bin/debugfs_static
+  apex_file=${out_dir}/target/product/module_arm64/system/apex/com.android.adbd.apex
+  echo "============ Diffing files in $apex_file and SBOM"
+  set +e
+  # deapexer prints the list of all files and directories
+  # sed extracts the file/directory names
+  # grep removes directories
+  # sed removes leading ./ in file names
+  diff -I /system/apex/com.android.adbd.apex -I apex_manifest.pb \
+      <($deapexer --debugfs_path=$debugfs list --extents ${apex_file} | sed -E 's#(.*) \[.*\]$#\1#' | grep -v "/$" | sed -E 's#^\./(.*)#\1#' | sort -n) \
+      <(grep '"fileName": ' ${apex_file}.spdx.json | sed -E 's/.*"fileName": "(.*)",/\1/' | sort -n )
+
+  if [ $? != "0" ]; then
+    echo "Diffs found in $apex_file and SBOM"
+    exit 1
+  else
+    echo "No diffs."
+  fi
+  set -e
+
+  # Teardown
+  cleanup "${out_dir}"
+}
+
+function test_sbom_unbundled_apk {
+  # Setup
+  out_dir="$(setup)"
+
+  # run_soong to build Browser2.apk
+  run_soong "module_arm64" "${out_dir}" "sbom" "Browser2"
+
+  sbom_file=${out_dir}/target/product/module_arm64/system/product/app/Browser2/Browser2.apk.spdx.json
+  echo "============ Diffing files in Browser2.apk and SBOM"
+  set +e
+  # There is only one file in SBOM of APKs
+  diff \
+      <(echo "/system/product/app/Browser2/Browser2.apk" ) \
+      <(grep '"fileName": ' ${sbom_file} | sed -E 's/.*"fileName": "(.*)",/\1/' )
+
+  if [ $? != "0" ]; then
+    echo "Diffs found in $sbom_file"
+    exit 1
+  else
+    echo "No diffs."
+  fi
+  set -e
+
+  # Teardown
+  cleanup "${out_dir}"
+}
+
+test_sbom_aosp_cf_x86_64_phone
+test_sbom_unbundled_apex
+test_sbom_unbundled_apk
\ No newline at end of file
diff --git a/third_party/zip/android_test.go b/third_party/zip/android_test.go
index 46588d4..3911dd4 100644
--- a/third_party/zip/android_test.go
+++ b/third_party/zip/android_test.go
@@ -190,7 +190,9 @@
 		t.Errorf("wanted directoryRecords %d, got %d", w, g)
 	}
 
-	if g, w := d.directorySize, uint64(uint32max); g != w {
+	zip64ExtraBuf := 48                                                  // 4x uint16 + 5x uint64
+	expectedDirSize := directoryHeaderLen + zip64ExtraBuf + len("large") // name of header
+	if g, w := d.directorySize, uint64(expectedDirSize); g != w {
 		t.Errorf("wanted directorySize %d, got %d", w, g)
 	}
 
diff --git a/ui/build/config.go b/ui/build/config.go
index 80038f5..fb5f7dd 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1375,6 +1375,17 @@
 }
 
 func (c *configImpl) UseRBE() bool {
+	// These alternate modes of running Soong do not use RBE / reclient.
+	if c.Bp2Build() || c.Queryview() || c.ApiBp2build() || c.JsonModuleGraph() {
+		return false
+	}
+
+	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" {
@@ -1514,7 +1525,7 @@
 	if googleProdCredsExistCache {
 		return googleProdCredsExistCache
 	}
-	if _, err := exec.Command("/usr/bin/gcertstatus").Output(); err != nil {
+	if _, err := exec.Command("/usr/bin/gcertstatus", "-nocheck_ssh").Output(); err != nil {
 		return false
 	}
 	googleProdCredsExistCache = true
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/metrics/bp2build_progress_metrics_proto/bp2build.proto b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
index 4aee88b..5b44002 100644
--- a/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
+++ b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
@@ -38,6 +38,9 @@
 
     // Total number of transitive dependencies.
     int32 num_deps = 5;
+
+    // Unconverted reasons from heuristics
+    repeated string unconverted_reasons_from_heuristics = 6;
   }
 
   // Modules that the transitive dependencies were identified for.
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 6d60316..d68ced8 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -230,7 +230,7 @@
 
 func (m *Metrics) UpdateTotalRealTimeAndNonZeroExit(data []byte, bazelExitCode int32) error {
 	if err := proto.Unmarshal(data, &m.metrics); err != nil {
-		return fmt.Errorf("Failed to unmarshal proto", err)
+		return fmt.Errorf("Failed to unmarshal proto: %w", err)
 	}
 	startTime := *m.metrics.Total.StartTime
 	endTime := uint64(time.Now().UnixNano())
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)
+	}
+}