Merge "Make glob output include product name" into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index ae8276f..6927765 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -20,6 +20,7 @@
         "aconfig_values.go",
         "aconfig_value_set.go",
         "all_aconfig_declarations.go",
+        "cc_aconfig_library.go",
         "init.go",
         "java_aconfig_library.go",
         "testing.go",
@@ -28,6 +29,7 @@
         "aconfig_declarations_test.go",
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
+        "java_aconfig_library_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 007d529..565d185 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -125,12 +125,12 @@
 	intermediatePath := android.PathForModuleOut(ctx, "intermediate.pb")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        aconfigRule,
-		Inputs:      inputFiles,
 		Output:      intermediatePath,
 		Description: "aconfig_declarations",
 		Args: map[string]string{
 			"release_version": ctx.Config().ReleaseVersion(),
 			"package":         module.properties.Package,
+			"declarations":    android.JoinPathsWithPrefix(inputFiles, "--declarations "),
 			"values":          joinAndPrefix(" --values ", module.properties.Values),
 		},
 	})
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index e14ca38..e0d8f7d 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -26,7 +26,10 @@
 		aconfig_declarations {
 			name: "module_name",
 			package: "com.example.package",
-			srcs: ["foo.aconfig"],
+			srcs: [
+				"foo.aconfig",
+				"bar.aconfig",
+			],
 		}
 	`
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
diff --git a/aconfig/cc_aconfig_library.go b/aconfig/cc_aconfig_library.go
new file mode 100644
index 0000000..14090bc
--- /dev/null
+++ b/aconfig/cc_aconfig_library.go
@@ -0,0 +1,128 @@
+// 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"
+	"android/soong/cc"
+	"github.com/google/blueprint"
+
+	"fmt"
+	"strings"
+)
+
+type ccDeclarationsTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var ccDeclarationsTag = ccDeclarationsTagType{}
+
+type CcAconfigLibraryProperties struct {
+	// name of the aconfig_declarations module to generate a library for
+	Aconfig_declarations string
+}
+
+type CcAconfigLibraryCallbacks struct {
+	properties *CcAconfigLibraryProperties
+
+	generatedDir android.WritablePath
+	headerDir    android.WritablePath
+	generatedCpp android.WritablePath
+	generatedH   android.WritablePath
+}
+
+func CcAconfigLibraryFactory() android.Module {
+	callbacks := &CcAconfigLibraryCallbacks{
+		properties: &CcAconfigLibraryProperties{},
+	}
+	return cc.GeneratedCcLibraryModuleFactory("cc_aconfig_library", callbacks)
+}
+
+func (this *CcAconfigLibraryCallbacks) GeneratorInit(ctx cc.BaseModuleContext) {
+}
+
+func (this *CcAconfigLibraryCallbacks) GeneratorProps() []interface{} {
+	return []interface{}{this.properties}
+}
+
+func (this *CcAconfigLibraryCallbacks) GeneratorDeps(ctx cc.DepsContext, deps cc.Deps) cc.Deps {
+	// Add a dependency for the declarations module
+	declarations := this.properties.Aconfig_declarations
+	if len(declarations) == 0 {
+		ctx.PropertyErrorf("aconfig_declarations", "aconfig_declarations property required")
+	} else {
+		ctx.AddDependency(ctx.Module(), ccDeclarationsTag, declarations)
+	}
+
+	// Add a dependency for the aconfig flags base library
+	deps.SharedLibs = append(deps.SharedLibs, "server_configurable_flags")
+	// TODO: It'd be really nice if we could reexport this library and not make everyone do it.
+
+	return deps
+}
+
+func (this *CcAconfigLibraryCallbacks) GeneratorSources(ctx cc.ModuleContext) cc.GeneratedSource {
+	result := cc.GeneratedSource{}
+
+	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
+	declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag)
+	if len(declarationsModules) != 1 {
+		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
+	}
+	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
+
+	// Figure out the generated file paths.  This has to match aconfig's codegen_cpp.rs.
+	this.generatedDir = android.PathForModuleGen(ctx)
+
+	this.headerDir = android.PathForModuleGen(ctx, "include")
+	result.IncludeDirs = []android.Path{this.headerDir}
+	result.ReexportedDirs = []android.Path{this.headerDir}
+
+	basename := strings.ReplaceAll(declarations.Package, ".", "_")
+
+	this.generatedCpp = android.PathForModuleGen(ctx, basename+".cc")
+	result.Sources = []android.Path{this.generatedCpp}
+
+	this.generatedH = android.PathForModuleGen(ctx, "include", basename+".h")
+	result.Headers = []android.Path{this.generatedH}
+
+	return result
+}
+
+func (this *CcAconfigLibraryCallbacks) GeneratorFlags(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) cc.Flags {
+	return flags
+}
+
+func (this *CcAconfigLibraryCallbacks) GeneratorBuildActions(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) {
+	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
+	declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag)
+	if len(declarationsModules) != 1 {
+		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
+	}
+	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:  cppRule,
+		Input: declarations.IntermediatePath,
+		Outputs: []android.WritablePath{
+			this.generatedCpp,
+			this.generatedH,
+		},
+		Description: "cc_aconfig_library",
+		Args: map[string]string{
+			"gendir": this.generatedDir.String(),
+		},
+	})
+}
diff --git a/aconfig/init.go b/aconfig/init.go
index 161fd42..37167aa 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -27,23 +27,24 @@
 		blueprint.RuleParams{
 			Command: `${aconfig} create-cache` +
 				` --package ${package}` +
-				` --declarations ${in}` +
+				` ${declarations}` +
 				` ${values}` +
 				` --cache ${out}.tmp` +
-				` && ( if cmp -s ${out}.tmp ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
 			//				` --build-id ${release_version}` +
 			CommandDeps: []string{
 				"${aconfig}",
 			},
 			Restat: true,
-		}, "release_version", "package", "values")
+		}, "release_version", "package", "declarations", "values")
 
 	// For java_aconfig_library: Generate java file
-	srcJarRule = pctx.AndroidStaticRule("aconfig_srcjar",
+	javaRule = pctx.AndroidStaticRule("java_aconfig_library",
 		blueprint.RuleParams{
 			Command: `rm -rf ${out}.tmp` +
 				` && mkdir -p ${out}.tmp` +
 				` && ${aconfig} create-java-lib` +
+				`    --mode ${mode}` +
 				`    --cache ${in}` +
 				`    --out ${out}.tmp` +
 				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
@@ -53,7 +54,21 @@
 				"$soong_zip",
 			},
 			Restat: true,
-		})
+		}, "mode")
+
+	// For java_aconfig_library: Generate java file
+	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
+		blueprint.RuleParams{
+			Command: `rm -rf ${gendir}` +
+				` && mkdir -p ${gendir}` +
+				` && ${aconfig} create-cpp-lib` +
+				`    --cache ${in}` +
+				`    --out ${gendir}`,
+			CommandDeps: []string{
+				"$aconfig",
+				"$soong_zip",
+			},
+		}, "gendir")
 
 	// For all_aconfig_declarations
 	allDeclarationsRule = pctx.AndroidStaticRule("all_aconfig_declarations_dump",
@@ -75,6 +90,7 @@
 	ctx.RegisterModuleType("aconfig_declarations", DeclarationsFactory)
 	ctx.RegisterModuleType("aconfig_values", ValuesFactory)
 	ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory)
+	ctx.RegisterModuleType("cc_aconfig_library", CcAconfigLibraryFactory)
 	ctx.RegisterModuleType("java_aconfig_library", JavaDeclarationsLibraryFactory)
 	ctx.RegisterParallelSingletonType("all_aconfig_declarations", AllAconfigDeclarationsFactory)
 }
diff --git a/aconfig/java_aconfig_library.go b/aconfig/java_aconfig_library.go
index 0eeb14f..53f8bd1 100644
--- a/aconfig/java_aconfig_library.go
+++ b/aconfig/java_aconfig_library.go
@@ -30,6 +30,9 @@
 type JavaAconfigDeclarationsLibraryProperties struct {
 	// name of the aconfig_declarations module to generate a library for
 	Aconfig_declarations string
+
+	// whether to generate test mode version of the library
+	Test bool
 }
 
 type JavaAconfigDeclarationsLibraryCallbacks struct {
@@ -51,7 +54,7 @@
 	}
 }
 
-func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuildActions(ctx android.ModuleContext) android.Path {
+func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuildActions(module *java.GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path {
 	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
 	declarationsModules := ctx.GetDirectDepsWithTag(declarationsTag)
 	if len(declarationsModules) != 1 {
@@ -59,13 +62,27 @@
 	}
 	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
 
+	// Generate the action to build the srcjar
 	srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
+	var mode string
+	if callbacks.properties.Test {
+		mode = "test"
+	} else {
+		mode = "production"
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        srcJarRule,
+		Rule:        javaRule,
 		Input:       declarations.IntermediatePath,
 		Output:      srcJarPath,
 		Description: "aconfig.srcjar",
+		Args: map[string]string{
+			"mode": mode,
+		},
 	})
 
+	// Tell the java module about the .aconfig files, so they can be propagated up the dependency chain.
+	// TODO: It would be nice to have that propagation code here instead of on java.Module and java.JavaInfo.
+	module.AddAconfigIntermediate(declarations.IntermediatePath)
+
 	return srcJarPath
 }
diff --git a/aconfig/java_aconfig_library_test.go b/aconfig/java_aconfig_library_test.go
new file mode 100644
index 0000000..af50848
--- /dev/null
+++ b/aconfig/java_aconfig_library_test.go
@@ -0,0 +1,191 @@
+// 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 (
+	"fmt"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+// Note: These tests cover the code in the java package. It'd be ideal of that code could
+// be in the aconfig package.
+
+// With the bp parameter that defines a my_module, make sure it has the LOCAL_ACONFIG_FILES entries
+func runJavaAndroidMkTest(t *testing.T, bp string) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, bp+`
+			aconfig_declarations {
+				name: "my_aconfig_declarations",
+				package: "com.example.package",
+				srcs: ["foo.aconfig"],
+			}
+
+			java_aconfig_library {
+				name: "my_java_aconfig_library",
+				aconfig_declarations: "my_aconfig_declarations",
+			}
+		`)
+
+	module := result.ModuleForTests("my_module", "android_common").Module()
+
+	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
+
+	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
+	android.AssertIntEquals(t, "len(LOCAL_ACONFIG_FILES)", 1, len(makeVar))
+	if !strings.HasSuffix(makeVar[0], "intermediate.pb") {
+		t.Errorf("LOCAL_ACONFIG_FILES should end with /intermediates.pb, instead it is: %s", makeVar[0])
+	}
+}
+
+func TestAndroidMkJavaLibrary(t *testing.T) {
+	bp := `
+		java_library {
+			name: "my_module",
+			srcs: [
+				"src/foo.java",
+			],
+			static_libs: [
+				"my_java_aconfig_library",
+			],
+			platform_apis: true,
+		}
+	`
+
+	runJavaAndroidMkTest(t, bp)
+}
+
+func TestAndroidMkAndroidApp(t *testing.T) {
+	bp := `
+		android_app {
+			name: "my_module",
+			srcs: [
+				"src/foo.java",
+			],
+			static_libs: [
+				"my_java_aconfig_library",
+			],
+			platform_apis: true,
+		}
+	`
+
+	runJavaAndroidMkTest(t, bp)
+}
+
+func TestAndroidMkBinary(t *testing.T) {
+	bp := `
+		java_binary {
+			name: "my_module",
+			srcs: [
+				"src/foo.java",
+			],
+			static_libs: [
+				"my_java_aconfig_library",
+			],
+			platform_apis: true,
+			main_class: "foo",
+		}
+	`
+
+	runJavaAndroidMkTest(t, bp)
+}
+
+func TestAndroidMkAndroidLibrary(t *testing.T) {
+	bp := `
+		android_library {
+			name: "my_module",
+			srcs: [
+				"src/foo.java",
+			],
+			static_libs: [
+				"my_java_aconfig_library",
+			],
+			platform_apis: true,
+		}
+	`
+
+	runJavaAndroidMkTest(t, bp)
+}
+
+func TestAndroidMkBinaryThatLinksAgainstAar(t *testing.T) {
+	// Tests AndroidLibrary's propagation of flags through JavaInfo
+	bp := `
+		android_library {
+			name: "some_library",
+			srcs: [
+				"src/foo.java",
+			],
+			static_libs: [
+				"my_java_aconfig_library",
+			],
+			platform_apis: true,
+		}
+		java_binary {
+			name: "my_module",
+			srcs: [
+				"src/bar.java",
+			],
+			static_libs: [
+				"some_library",
+			],
+			platform_apis: true,
+			main_class: "foo",
+		}
+	`
+
+	runJavaAndroidMkTest(t, bp)
+}
+
+func testCodegenMode(t *testing.T, bpMode string, ruleMode string) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, fmt.Sprintf(`
+			aconfig_declarations {
+				name: "my_aconfig_declarations",
+				package: "com.example.package",
+				srcs: ["foo.aconfig"],
+			}
+
+			java_aconfig_library {
+				name: "my_java_aconfig_library",
+				aconfig_declarations: "my_aconfig_declarations",
+				%s
+			}
+		`, bpMode))
+
+	module := result.ModuleForTests("my_java_aconfig_library", "android_common")
+	rule := module.Rule("java_aconfig_library")
+	android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode)
+}
+
+func TestDefaultProdMode(t *testing.T) {
+	testCodegenMode(t, "", "production")
+}
+
+func TestProdMode(t *testing.T) {
+	testCodegenMode(t, "test: false,", "production")
+}
+
+func TestTestMode(t *testing.T) {
+	testCodegenMode(t, "test: true,", "test")
+}
diff --git a/android/Android.bp b/android/Android.bp
index fc5198f..f5bb785 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -107,6 +107,7 @@
         "bazel_test.go",
         "config_test.go",
         "config_bp2build_test.go",
+        "configured_jars_test.go",
         "csuite_config_test.go",
         "defaults_test.go",
         "depset_test.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index d320599..d37dc02 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -121,6 +121,7 @@
 		"development/sdk":                             Bp2BuildDefaultTrueRecursively,
 
 		"external/aac":                           Bp2BuildDefaultTrueRecursively,
+		"external/abseil-cpp":                    Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":        Bp2BuildDefaultTrueRecursively,
 		"external/auto":                          Bp2BuildDefaultTrue,
 		"external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
@@ -141,11 +142,12 @@
 		"external/expat":                         Bp2BuildDefaultTrueRecursively,
 		"external/f2fs-tools":                    Bp2BuildDefaultTrue,
 		"external/flac":                          Bp2BuildDefaultTrueRecursively,
+		"external/flatbuffers":                   Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                        Bp2BuildDefaultTrueRecursively,
 		"external/fsverity-utils":                Bp2BuildDefaultTrueRecursively,
-		"external/guava":                         Bp2BuildDefaultTrueRecursively,
 		"external/google-benchmark":              Bp2BuildDefaultTrueRecursively,
 		"external/googletest":                    Bp2BuildDefaultTrueRecursively,
+		"external/guava":                         Bp2BuildDefaultTrueRecursively,
 		"external/gwp_asan":                      Bp2BuildDefaultTrueRecursively,
 		"external/hamcrest":                      Bp2BuildDefaultTrueRecursively,
 		"external/icu":                           Bp2BuildDefaultTrueRecursively,
@@ -188,7 +190,11 @@
 		"external/ow2-asm":                       Bp2BuildDefaultTrueRecursively,
 		"external/pcre":                          Bp2BuildDefaultTrueRecursively,
 		"external/protobuf":                      Bp2BuildDefaultTrueRecursively,
+		"external/python/pyyaml/lib/yaml":        Bp2BuildDefaultTrueRecursively,
 		"external/python/six":                    Bp2BuildDefaultTrueRecursively,
+		"external/python/jinja/src":              Bp2BuildDefaultTrueRecursively,
+		"external/python/markupsafe/src":         Bp2BuildDefaultTrueRecursively,
+		"external/python/setuptools":             Bp2BuildDefaultTrueRecursively,
 		"external/rappor":                        Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                         Bp2BuildDefaultTrueRecursively,
 		"external/selinux/checkpolicy":           Bp2BuildDefaultTrueRecursively,
@@ -227,6 +233,7 @@
 		"frameworks/hardware/interfaces/stats/aidl":          Bp2BuildDefaultTrue,
 		"frameworks/libs/modules-utils/build":                Bp2BuildDefaultTrueRecursively,
 		"frameworks/libs/net/common/native":                  Bp2BuildDefaultTrueRecursively,
+		"frameworks/native":                                  Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
@@ -429,6 +436,7 @@
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
+		"external/bazelbuild-rules_java":/* recursive = */ true,
 		"external/bazelbuild-rules_license":/* recursive = */ true,
 		"external/bazelbuild-rules_go":/* recursive = */ true,
 		"external/bazelbuild-kotlin-rules":/* recursive = */ true,
@@ -473,6 +481,8 @@
 	Bp2buildModuleAlwaysConvertList = []string{
 		"libidmap2_policies",
 		"libSurfaceFlingerProp",
+		"toolbox_input_labels",
+
 		// cc mainline modules
 
 		// com.android.media.swcodec
@@ -490,7 +500,6 @@
 		"code_coverage.policy.other",
 		"codec2_soft_exports",
 		"compatibility_matrix_schema",
-		"flatbuffer_headers",
 		"framework-connectivity-protos",
 		"gemmlowp_headers",
 		"gl_headers",
@@ -613,10 +622,6 @@
 		"libhidlbase", // needed by cc_hidl_library
 		"libhidl_gtest_helper",
 
-		//frameworks/native
-		"framework_native_aidl_binder",
-		"framework_native_aidl_gui",
-
 		//frameworks/native/libs/input
 		"inputconstants_aidl",
 
@@ -674,6 +679,7 @@
 		"libcodec2_hidl@1.2",
 		"libcodec2_hidl_plugin_stub",
 		"libcodec2_hidl_plugin",
+		"libcodec2_hal_common",
 		"libstagefright_bufferqueue_helper_novndk",
 		"libGLESv2",
 		"libEGL",
@@ -763,6 +769,7 @@
 
 		// Mainline Module Apps
 		"CaptivePortalLogin",
+		"ModuleMetadata",
 
 		"libstagefright_headers",
 
@@ -805,6 +812,14 @@
 		"rs-headers",
 		"rs_script_api",
 		"libRSDispatch",
+
+		// hal_unit_tests and deps
+		"android.hardware.contexthub_interface", // created implicitly by android.hardware.contexthub
+		"chre_flatbuffers",
+		"event_logger",
+		"hal_unit_tests",
+
+		"merge_annotation_zips_test",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -828,6 +843,10 @@
 	// the "prebuilt_" prefix to the name, so that it's differentiable from
 	// the source versions within Soong's module graph.
 	Bp2buildModuleDoNotConvertList = []string{
+		// TODO(b/263326760): Failed already.
+		"minijail_compiler_unittest",
+		"minijail_parser_unittest",
+
 		// Depends on unconverted libandroid, libgui
 		"dvr_buffer_queue-test",
 		"dvr_display-test",
@@ -909,7 +928,10 @@
 		"libart_headers",                                             // depends on unconverted modules: art_libartbase_headers
 		"libartbase-art-gtest",                                       // depends on unconverted modules: libgtest_isolated, libart, libart-compiler, libdexfile, libprofile
 		"libartbased-art-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
+		"libart-runtime",                                             // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
+		"libart-runtime-for-test",                                    // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
 		"libartd",                                                    // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
+		"libartd-runtime",                                            // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
 		"libartd-runtime-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest
 		"libdebuggerd",                                               // depends on unconverted module: libdexfile
 		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
@@ -931,9 +953,6 @@
 		"test_fips",                     // depends on unconverted modules: adb
 		"timezone-host",                 // depends on unconverted modules: art.module.api.annotations
 
-		// '//bionic/libc:libc_bp2build_cc_library_static' is duplicated in the 'deps' attribute of rule
-		"toybox-static",
-
 		// aidl files not created
 		"overlayable_policy_aidl_interface",
 
@@ -1016,6 +1035,7 @@
 		"bionic-unit-tests-static",
 		"boringssl_crypto_test",
 		"boringssl_ssl_test",
+		"boringssl_test_support", //b/244431896
 		"cfi_test_helper",
 		"cfi_test_helper2",
 		"cintltst32",
@@ -1493,8 +1513,6 @@
 		"ICU4CTestRunner",
 		"DeviceLongPollingStubTest",
 
-		"HelloWorldHostTest", // TODO(b/280452825): Convert HelloWorldHostTest to b test
-
 		"libprotobuf-full-test", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory
 		"libprotobuf-lite-test", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory
 
@@ -1509,73 +1527,6 @@
 		"libartd-runtime-gtest",
 	}
 
-	MixedBuildsDisabledList = []string{
-		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
-
-		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
-
-		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
-		"minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
-
-		"cap_names.h",                                  // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libcap",                                       // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
-
-		// Depends on libprotobuf-cpp-*
-		"libadb_pairing_connection",
-		"libadb_pairing_connection_static",
-		"libadb_pairing_server", "libadb_pairing_server_static",
-
-		// java_import[_host] issues
-		// tradefed prebuilts depend on libprotobuf
-		"prebuilt_tradefed",
-		"prebuilt_tradefed-test-framework",
-		// handcrafted BUILD.bazel files in //prebuilts/...
-		"prebuilt_r8lib-prebuilt",
-		"prebuilt_sdk-core-lambda-stubs",
-		"prebuilt_android-support-collections-nodeps",
-		"prebuilt_android-arch-core-common-nodeps",
-		"prebuilt_android-arch-lifecycle-common-java8-nodeps",
-		"prebuilt_android-arch-lifecycle-common-nodeps",
-		"prebuilt_android-support-annotations-nodeps",
-		"prebuilt_android-arch-paging-common-nodeps",
-		"prebuilt_android-arch-room-common-nodeps",
-		// TODO(b/217750501) exclude_dirs property not supported
-		"prebuilt_kotlin-reflect",
-		"prebuilt_kotlin-stdlib",
-		"prebuilt_kotlin-stdlib-jdk7",
-		"prebuilt_kotlin-stdlib-jdk8",
-		"prebuilt_kotlin-test",
-		// TODO(b/217750501) exclude_files property not supported
-		"prebuilt_currysrc_org.eclipse",
-
-		// TODO(b/266459895): re-enable libunwindstack
-		"libunwindstack",
-		"libunwindstack_stdout_log",
-		"libunwindstack_no_dex",
-		"libunwindstack_utils",
-		"unwind_reg_info",
-		"libunwindstack_local",
-		"unwind_for_offline",
-		"unwind",
-		"unwind_info",
-		"unwind_symbols",
-		"libEGL",
-		"libGLESv2",
-		"libc_malloc_debug",
-		"libcodec2_hidl@1.0",
-		"libcodec2_hidl@1.1",
-		"libcodec2_hidl@1.2",
-		"libfdtrack",
-		"libgui",
-		"libgui_bufferqueue_static",
-		"libmedia_codecserviceregistrant",
-		"libstagefright_bufferqueue_helper_novndk",
-		"libutils_test",
-		"libutilscallstack",
-		"mediaswcodec",
-	}
-
 	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
 	// in either prod mode or staging mode.
 	ProdMixedBuildsEnabledList = []string{
@@ -1598,14 +1549,6 @@
 		"test_com.android.neuralnetworks",
 		"libneuralnetworks",
 		"libneuralnetworks_static",
-	}
-
-	// Staging-mode allowlist. Modules in this list are only built
-	// by Bazel with --bazel-mode-staging. This list should contain modules
-	// which will soon be added to the prod allowlist.
-	// It is implicit that all modules in ProdMixedBuildsEnabledList will
-	// also be built - do not add them to this list.
-	StagingMixedBuildsEnabledList = []string{
 		// M13: media.swcodec launch
 		"com.android.media.swcodec",
 		"test_com.android.media.swcodec",
@@ -1613,20 +1556,26 @@
 		"libcodec2_hidl@1.0",
 	}
 
+	// Staging-mode allowlist. Modules in this list are only built
+	// by Bazel with --bazel-mode-staging. This list should contain modules
+	// which will soon be added to the prod allowlist.
+	// It is implicit that all modules in ProdMixedBuildsEnabledList will
+	// also be built - do not add them to this list.
+	StagingMixedBuildsEnabledList = []string{}
+
 	// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
 	ProdDclaMixedBuildsEnabledList = []string{
 		"libbase",
 		"libc++",
 		"libcrypto",
 		"libcutils",
-	}
-
-	// These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList
-	StagingDclaMixedBuildsEnabledList = []string{
 		"libstagefright_flacdec",
 		"libutils",
 	}
 
+	// These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList
+	StagingDclaMixedBuildsEnabledList = []string{}
+
 	// TODO(b/269342245): Enable the rest of the DCLA libs
 	// "libssl",
 
@@ -1638,4 +1587,14 @@
 		"art_":        DEFAULT_PRIORITIZED_WEIGHT,
 		"ndk_library": DEFAULT_PRIORITIZED_WEIGHT,
 	}
+
+	BazelSandwichTargets = []struct {
+		Label string
+		Host  bool
+	}{
+		{
+			Label: "//build/bazel/examples/partitions:system_image",
+			Host:  false,
+		},
+	}
 )
diff --git a/android/api_levels.go b/android/api_levels.go
index 2391e6c..44c8640 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -275,10 +275,6 @@
 // relocations itself.
 var FirstPackedRelocationsVersion = uncheckedFinalApiLevel(23)
 
-// The first API level that does not require NDK code to link
-// libandroid_support.
-var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21)
-
 // LastWithoutModuleLibCoreSystemModules is the last API level where prebuilts/sdk does not contain
 // a core-for-system-modules.jar for the module-lib API scope.
 var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31)
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.go b/android/bazel.go
index 1bfbdec..0d2c777 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -255,7 +255,7 @@
 	if b.ShouldConvertWithBp2build(ctx) {
 		return bp2buildModuleLabel(ctx, module)
 	}
-	panic(fmt.Errorf("requested non-existent label for module ", module.Name()))
+	panic(fmt.Errorf("requested non-existent label for module %s", module.Name()))
 }
 
 type Bp2BuildConversionAllowlist struct {
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index d5ccfca..fd4b5ef 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -16,6 +16,8 @@
 
 import (
 	"bytes"
+	"crypto/sha1"
+	"encoding/hex"
 	"fmt"
 	"os"
 	"path"
@@ -184,6 +186,8 @@
 
 	// Returns the depsets defined in Bazel's aquery response.
 	AqueryDepsets() []bazel.AqueryDepset
+
+	QueueBazelSandwichCqueryRequests(config Config) error
 }
 
 type bazelRunner interface {
@@ -262,6 +266,10 @@
 	m.BazelRequests[key] = true
 }
 
+func (m MockBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	panic("unimplemented")
+}
+
 func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
 	result, ok := m.LabelToOutputFiles[label]
 	if !ok {
@@ -422,6 +430,10 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	panic("unimplemented")
+}
+
 func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
 	panic("unimplemented")
 }
@@ -1040,6 +1052,50 @@
 	allBazelCommands = []bazelCommand{aqueryCmd, cqueryCmd, buildCmd}
 )
 
+func GetBazelSandwichCqueryRequests(config Config) ([]cqueryKey, error) {
+	result := make([]cqueryKey, 0, len(allowlists.BazelSandwichTargets))
+	// Note that bazel "targets" are different from soong "targets", the bazel targets are
+	// synonymous with soong modules, and soong targets are a configuration a module is built in.
+	for _, target := range allowlists.BazelSandwichTargets {
+		var soongTarget Target
+		if target.Host {
+			soongTarget = config.BuildOSTarget
+		} else {
+			soongTarget = config.AndroidCommonTarget
+			if soongTarget.Os.Class != Device {
+				// kernel-build-tools seems to set the AndroidCommonTarget to a linux host
+				// target for some reason, disable device builds in that case.
+				continue
+			}
+		}
+
+		result = append(result, cqueryKey{
+			label:       target.Label,
+			requestType: cquery.GetOutputFiles,
+			configKey: configKey{
+				arch:   soongTarget.Arch.String(),
+				osType: soongTarget.Os,
+			},
+		})
+	}
+	return result, nil
+}
+
+// QueueBazelSandwichCqueryRequests queues cquery requests for all the bazel labels in
+// bazel_sandwich_targets. These will later be given phony targets so that they can be built on the
+// command line.
+func (context *mixedBuildBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	requests, err := GetBazelSandwichCqueryRequests(config)
+	if err != nil {
+		return err
+	}
+	for _, request := range requests {
+		context.QueueBazelRequest(request.label, request.requestType, request.configKey)
+	}
+
+	return nil
+}
+
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
 func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
@@ -1222,7 +1278,11 @@
 		ctx.AddNinjaFileDeps(file)
 	}
 
+	depsetHashToDepset := map[string]bazel.AqueryDepset{}
+
 	for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
+		depsetHashToDepset[depset.ContentHash] = depset
+
 		var outputs []Path
 		var orderOnlies []Path
 		for _, depsetDepHash := range depset.TransitiveDepSetHashes {
@@ -1249,6 +1309,11 @@
 
 	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
 	bazelOutDir := path.Join(executionRoot, "bazel-out")
+	rel, err := filepath.Rel(ctx.Config().OutDir(), executionRoot)
+	if err != nil {
+		ctx.Errorf("%s", err.Error())
+	}
+	dotdotsToOutRoot := strings.Repeat("../", strings.Count(rel, "/")+1)
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
 		// nil build statements are a valid case where we do not create an action because it is
 		// unnecessary or handled by other processing
@@ -1257,7 +1322,31 @@
 		}
 		if len(buildStatement.Command) > 0 {
 			rule := NewRuleBuilder(pctx, ctx)
-			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
+			intermediateDir, intermediateDirHash := intermediatePathForSboxMixedBuildAction(ctx, buildStatement)
+			if buildStatement.ShouldRunInSbox {
+				// Create a rule to build the output inside a sandbox
+				// This will create two changes of working directory
+				// 1. From ANDROID_BUILD_TOP to sbox top
+				// 2. From sbox top to a a synthetic mixed build execution root relative to it
+				// Finally, the outputs will be copied to intermediateDir
+				rule.Sbox(intermediateDir,
+					PathForOutput(ctx, "mixed_build_sbox_intermediates", intermediateDirHash+".textproto")).
+					SandboxInputs().
+					// Since we will cd to mixed build execution root, set sbox's out subdir to empty
+					// Without this, we will try to copy from $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
+					SetSboxOutDirDirAsEmpty()
+
+				// Create another set of rules to copy files from the intermediate dir to mixed build execution root
+				for _, outputPath := range buildStatement.OutputPaths {
+					ctx.Build(pctx, BuildParams{
+						Rule:   CpIfChanged,
+						Input:  intermediateDir.Join(ctx, executionRoot, outputPath),
+						Output: PathForBazelOut(ctx, outputPath),
+					})
+				}
+			}
+			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx, depsetHashToDepset, dotdotsToOutRoot)
+
 			desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
 			rule.Build(fmt.Sprintf("bazel %d", index), desc)
 			continue
@@ -1270,6 +1359,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)
@@ -1296,12 +1391,45 @@
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
 	}
+
+	// Create phony targets for all the bazel sandwich output files
+	requests, err := GetBazelSandwichCqueryRequests(ctx.Config())
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+	for _, request := range requests {
+		files, err := ctx.Config().BazelContext.GetOutputFiles(request.label, request.configKey)
+		if err != nil {
+			ctx.Errorf(err.Error())
+		}
+		filesAsPaths := make([]Path, 0, len(files))
+		for _, file := range files {
+			filesAsPaths = append(filesAsPaths, PathForBazelOut(ctx, file))
+		}
+		ctx.Phony("bazel_sandwich", filesAsPaths...)
+	}
+	ctx.Phony("checkbuild", PathForPhony(ctx, "bazel_sandwich"))
+}
+
+// Returns a out dir path for a sandboxed mixed build action
+func intermediatePathForSboxMixedBuildAction(ctx PathContext, statement *bazel.BuildStatement) (OutputPath, string) {
+	// An artifact can be generated by a single buildstatement.
+	// Use the hash of the first artifact to create a unique path
+	uniqueDir := sha1.New()
+	uniqueDir.Write([]byte(statement.OutputPaths[0]))
+	uniqueDirHashString := hex.EncodeToString(uniqueDir.Sum(nil))
+	return PathForOutput(ctx, "mixed_build_sbox_intermediates", uniqueDirHashString), uniqueDirHashString
 }
 
 // Register bazel-owned build statements (obtained from the aquery invocation).
-func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext) {
+func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext, depsetHashToDepset map[string]bazel.AqueryDepset, dotdotsToOutRoot string) {
 	// executionRoot is the action cwd.
-	cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
+	if buildStatement.ShouldRunInSbox {
+		// mkdir -p ensures that the directory exists when run via sbox
+		cmd.Text(fmt.Sprintf("mkdir -p '%s' && cd '%s' &&", executionRoot, executionRoot))
+	} else {
+		cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
+	}
 
 	// Remove old outputs, as some actions might not rerun if the outputs are detected.
 	if len(buildStatement.OutputPaths) > 0 {
@@ -1317,25 +1445,47 @@
 		cmd.Flag(pair.Key + "=" + pair.Value)
 	}
 
+	command := buildStatement.Command
+	command = strings.ReplaceAll(command, "{DOTDOTS_TO_OUTPUT_ROOT}", dotdotsToOutRoot)
+
 	// The actual Bazel action.
-	if len(buildStatement.Command) > 16*1024 {
+	if len(command) > 16*1024 {
 		commandFile := PathForBazelOut(ctx, buildStatement.OutputPaths[0]+".sh")
-		WriteFileRule(ctx, commandFile, buildStatement.Command)
+		WriteFileRule(ctx, commandFile, command)
 
 		cmd.Text("bash").Text(buildStatement.OutputPaths[0] + ".sh").Implicit(commandFile)
 	} else {
-		cmd.Text(buildStatement.Command)
+		cmd.Text(command)
 	}
 
 	for _, outputPath := range buildStatement.OutputPaths {
-		cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+		if buildStatement.ShouldRunInSbox {
+			// The full path has three components that get joined together
+			// 1. intermediate output dir that `sbox` will place the artifacts at
+			// 2. mixed build execution root
+			// 3. artifact path returned by aquery
+			intermediateDir, _ := intermediatePathForSboxMixedBuildAction(ctx, buildStatement)
+			cmd.ImplicitOutput(intermediateDir.Join(ctx, executionRoot, outputPath))
+		} else {
+			cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+		}
 	}
 	for _, inputPath := range buildStatement.InputPaths {
 		cmd.Implicit(PathForBazelOut(ctx, inputPath))
 	}
 	for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
-		otherDepsetName := bazelDepsetName(inputDepsetHash)
-		cmd.Implicit(PathForPhony(ctx, otherDepsetName))
+		if buildStatement.ShouldRunInSbox {
+			// Bazel depsets are phony targets that are used to group files.
+			// We need to copy the grouped files into the sandbox
+			ds, _ := depsetHashToDepset[inputDepsetHash]
+			cmd.Implicits(PathsForBazelOut(ctx, ds.DirectArtifacts))
+		} else {
+			otherDepsetName := bazelDepsetName(inputDepsetHash)
+			cmd.Implicit(PathForPhony(ctx, otherDepsetName))
+		}
+	}
+	for _, implicitPath := range buildStatement.ImplicitDeps {
+		cmd.Implicit(PathForArbitraryOutput(ctx, implicitPath))
 	}
 
 	if depfile := buildStatement.Depfile; depfile != nil {
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 65cd5a8..9a3c8fc 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -181,13 +181,62 @@
 
 		cmd := RuleBuilderCommand{}
 		ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
-		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx)
+		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{}, "")
 		if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
 			t.Errorf("expected: [%s], actual: [%s]", expected, actual)
 		}
 	}
 }
 
+func TestMixedBuildSandboxedAction(t *testing.T) {
+	input := `{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 },
+   { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+   "target_Id": 1,
+   "action_Key": "x",
+   "mnemonic": "x",
+   "arguments": ["touch", "foo"],
+   "input_dep_set_ids": [1],
+   "output_Ids": [1],
+   "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+   { "id": 1, "direct_artifact_ids": [1, 2] }],
+ "path_fragments": [
+   { "id": 1, "label": "one" },
+   { "id": 2, "label": "two" }]
+}`
+	data, err := JsonToActionGraphContainer(input)
+	if err != nil {
+		t.Error(err)
+	}
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{aqueryCmd: string(data)})
+
+	err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
+	if err != nil {
+		t.Fatalf("TestMixedBuildSandboxedAction did not expect error invoking Bazel, but got %s", err)
+	}
+
+	statement := bazelContext.BuildStatementsToRegister()[0]
+	statement.ShouldRunInSbox = true
+
+	cmd := RuleBuilderCommand{}
+	ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
+	createCommand(&cmd, statement, "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{}, "")
+	// Assert that the output is generated in an intermediate directory
+	// fe05bcdcdc4928012781a5f1a2a77cbb5398e106 is the sha1 checksum of "one"
+	if actual, expected := cmd.outputs[0].String(), "out/soong/mixed_build_sbox_intermediates/fe05bcdcdc4928012781a5f1a2a77cbb5398e106/test/exec_root/one"; expected != actual {
+		t.Errorf("expected: [%s], actual: [%s]", expected, actual)
+	}
+
+	// Assert the actual command remains unchanged inside the sandbox
+	if actual, expected := cmd.buf.String(), "mkdir -p 'test/exec_root' && cd 'test/exec_root' && rm -rf 'one' && touch foo"; expected != actual {
+		t.Errorf("expected: [%s], actual: [%s]", expected, actual)
+	}
+}
+
 func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
 	testConfig.productVariables.ClangCoverage = boolPtr(true)
 
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 872e908..7568543 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -124,6 +124,7 @@
 		labels.Includes = []bazel.Label{}
 		return labels
 	}
+	modules = FirstUniqueStrings(modules)
 	for _, module := range modules {
 		bpText := module
 		if m := SrcIsModule(module); m == "" {
@@ -179,7 +180,7 @@
 // paths, relative to the local module, or Bazel-labels (absolute if in a different package or
 // relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
-// will have already been handled by the path_deps mutator.
+// will have already been handled by the pathdeps mutator.
 func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
 	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
 }
@@ -189,7 +190,7 @@
 // references in paths, minus those in excludes, relative to the local module, or Bazel-labels
 // (absolute if in a different package or relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
-// will have already been handled by the path_deps mutator.
+// will have already been handled by the pathdeps mutator.
 func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
 	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
 	excluded := make([]string, 0, len(excludeLabels.Includes))
@@ -198,7 +199,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 +238,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 +282,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 +296,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 +314,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
 }
@@ -355,7 +356,7 @@
 //
 // Properties passed as the paths or excludes argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on other modules will have already been handled by the
-// path_deps mutator.
+// pathdeps mutator.
 func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
 	if paths == nil {
 		return bazel.LabelList{}
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 fa43962..72ff224 100644
--- a/android/config.go
+++ b/android/config.go
@@ -18,7 +18,6 @@
 // product variables necessary for soong_build's operation.
 
 import (
-	"bytes"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -171,6 +170,19 @@
 	return c.config.TestProductVariables != nil
 }
 
+// DisableHiddenApiChecks returns true if hiddenapi checks have been disabled.
+// For 'eng' target variant hiddenapi checks are disabled by default for performance optimisation,
+// but can be enabled by setting environment variable ENABLE_HIDDENAPI_FLAGS=true.
+// For other target variants hiddenapi check are enabled by default but can be disabled by
+// setting environment variable UNSAFE_DISABLE_HIDDENAPI_FLAGS=true.
+// If both ENABLE_HIDDENAPI_FLAGS=true and UNSAFE_DISABLE_HIDDENAPI_FLAGS=true, then
+// ENABLE_HIDDENAPI_FLAGS=true will be triggered and hiddenapi checks will be considered enabled.
+func (c Config) DisableHiddenApiChecks() bool {
+	return !c.IsEnvTrue("ENABLE_HIDDENAPI_FLAGS") &&
+		(c.IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") ||
+			Bool(c.productVariables.Eng))
+}
+
 // MaxPageSizeSupported returns the max page size supported by the device. This
 // value will define the ELF segment alignment for binaries (executables and
 // shared libraries).
@@ -202,10 +214,10 @@
 // product configuration values are read from Kati-generated soong.variables.
 type config struct {
 	// Options configurable with soong.variables
-	productVariables productVariables
+	productVariables ProductVariables
 
 	// Only available on configs created by TestConfig
-	TestProductVariables *productVariables
+	TestProductVariables *ProductVariables
 
 	// A specialized context object for Bazel/Soong mixed builds and migration
 	// purposes.
@@ -321,7 +333,7 @@
 
 // loadFromConfigFile loads and decodes configuration options from a JSON file
 // in the current working directory.
-func loadFromConfigFile(configurable *productVariables, filename string) error {
+func loadFromConfigFile(configurable *ProductVariables, filename string) error {
 	// Try to open the file
 	configFileReader, err := os.Open(filename)
 	defer configFileReader.Close()
@@ -373,7 +385,7 @@
 
 // atomically writes the config file in case two copies of soong_build are running simultaneously
 // (for example, docs generation and ninja manifest generation)
-func saveToConfigFile(config *productVariables, filename string) error {
+func saveToConfigFile(config *ProductVariables, filename string) error {
 	data, err := json.MarshalIndent(&config, "", "    ")
 	if err != nil {
 		return fmt.Errorf("cannot marshal config data: %s", err.Error())
@@ -402,7 +414,7 @@
 	return nil
 }
 
-func saveToBazelConfigFile(config *productVariables, outDir string) error {
+func saveToBazelConfigFile(config *ProductVariables, outDir string) error {
 	dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config")
 	err := createDirIfNonexistent(dir, os.ModePerm)
 	if err != nil {
@@ -431,32 +443,6 @@
 		return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
 	}
 
-	configJson, err := json.MarshalIndent(&config, "", "    ")
-	if err != nil {
-		return fmt.Errorf("cannot marshal config data: %s", err.Error())
-	}
-	// The backslashes need to be escaped because this text is going to be put
-	// inside a Starlark string literal.
-	configJson = bytes.ReplaceAll(configJson, []byte("\\"), []byte("\\\\"))
-
-	bzl := []string{
-		bazel.GeneratedBazelFileWarning,
-		fmt.Sprintf(`_product_vars = json.decode("""%s""")`, configJson),
-		fmt.Sprintf(`_product_var_constraints = %s`, nonArchVariantProductVariablesJson),
-		fmt.Sprintf(`_arch_variant_product_var_constraints = %s`, archVariantProductVariablesJson),
-		"\n", `
-product_vars = _product_vars
-
-# TODO(b/269577299) Remove these when everything switches over to loading them from product_variable_constants.bzl
-product_var_constraints = _product_var_constraints
-arch_variant_product_var_constraints = _arch_variant_product_var_constraints
-`,
-	}
-	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variables.bzl"),
-		[]byte(strings.Join(bzl, "\n")), 0644)
-	if err != nil {
-		return fmt.Errorf("Could not write .bzl config file %s", err)
-	}
 	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(`
 product_var_constraints = %s
 arch_variant_product_var_constraints = %s
@@ -1355,6 +1341,10 @@
 	return c.bazelForceEnabledModules
 }
 
+func (c *config) IsVndkDeprecated() bool {
+	return !Bool(c.productVariables.KeepVndk)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -1932,6 +1922,10 @@
 	return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
 }
 
+func (c *deviceConfig) Release_aidl_use_unfrozen() bool {
+	return Bool(c.config.productVariables.Release_aidl_use_unfrozen)
+}
+
 func (c *config) SelinuxIgnoreNeverallows() bool {
 	return c.productVariables.SelinuxIgnoreNeverallows
 }
diff --git a/android/config_test.go b/android/config_test.go
index 9df5288..7d327a2 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -75,7 +75,7 @@
 
 // run validateConfigAnnotations against each type that might have json annotations
 func TestProductConfigAnnotations(t *testing.T) {
-	err := validateConfigAnnotations(&productVariables{})
+	err := validateConfigAnnotations(&ProductVariables{})
 	if err != nil {
 		t.Errorf(err.Error())
 	}
@@ -88,7 +88,7 @@
 	}
 }
 
-func verifyProductVariableMarshaling(t *testing.T, v productVariables) {
+func verifyProductVariableMarshaling(t *testing.T, v ProductVariables) {
 	dir := t.TempDir()
 	path := filepath.Join(dir, "test.variables")
 	err := saveToConfigFile(&v, path)
@@ -96,20 +96,20 @@
 		t.Errorf("Couldn't save default product config: %q", err)
 	}
 
-	var v2 productVariables
+	var v2 ProductVariables
 	err = loadFromConfigFile(&v2, path)
 	if err != nil {
 		t.Errorf("Couldn't load default product config: %q", err)
 	}
 }
 func TestDefaultProductVariableMarshaling(t *testing.T) {
-	v := productVariables{}
+	v := ProductVariables{}
 	v.SetDefaultConfig()
 	verifyProductVariableMarshaling(t, v)
 }
 
 func TestBootJarsMarshaling(t *testing.T) {
-	v := productVariables{}
+	v := ProductVariables{}
 	v.SetDefaultConfig()
 	v.BootJars = ConfiguredJarList{
 		apexes: []string{"apex"},
diff --git a/android/configured_jars.go b/android/configured_jars.go
index 53fef05..c7b808f 100644
--- a/android/configured_jars.go
+++ b/android/configured_jars.go
@@ -178,7 +178,7 @@
 func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
 	paths := make(WritablePaths, l.Len())
 	for i, jar := range l.jars {
-		paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
+		paths[i] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), jar)+".jar")
 	}
 	return paths
 }
@@ -187,8 +187,8 @@
 // prefix.
 func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
 	paths := map[string]WritablePath{}
-	for _, jar := range l.jars {
-		paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
+	for i, jar := range l.jars {
+		paths[jar] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), jar)+".jar")
 	}
 	return paths
 }
@@ -228,24 +228,32 @@
 	return json.Marshal(list)
 }
 
-// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
-//
-// TODO(b/139391334): hard coded until we find a good way to query the stem of a
-// module before any other mutators are run.
-func ModuleStem(module string) string {
-	if module == "framework-minus-apex" {
-		return "framework"
+func OverrideConfiguredJarLocationFor(cfg Config, apex string, jar string) (newApex string, newJar string) {
+	for _, entry := range cfg.productVariables.ConfiguredJarLocationOverrides {
+		tuple := strings.Split(entry, ":")
+		if len(tuple) != 4 {
+			panic("malformed configured jar location override '%s', expected format: <old_apex>:<old_jar>:<new_apex>:<new_jar>")
+		}
+		if apex == tuple[0] && jar == tuple[1] {
+			return tuple[2], tuple[3]
+		}
 	}
-	return module
+	return apex, jar
+}
+
+// ModuleStem returns the overridden jar name.
+func ModuleStem(cfg Config, apex string, jar string) string {
+	_, newJar := OverrideConfiguredJarLocationFor(cfg, apex, jar)
+	return newJar
 }
 
 // DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
 // based on the operating system.
 func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
 	paths := make([]string, l.Len())
-	for i, jar := range l.jars {
-		apex := l.apexes[i]
-		name := ModuleStem(jar) + ".jar"
+	for i := 0; i < l.Len(); i++ {
+		apex, jar := OverrideConfiguredJarLocationFor(cfg, l.Apex(i), l.Jar(i))
+		name := jar + ".jar"
 
 		var subdir string
 		if apex == "platform" {
@@ -311,4 +319,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/configured_jars_test.go b/android/configured_jars_test.go
new file mode 100644
index 0000000..4b586e4
--- /dev/null
+++ b/android/configured_jars_test.go
@@ -0,0 +1,46 @@
+// 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 android
+
+import (
+	"testing"
+)
+
+func TestOverrideConfiguredJarLocationFor(t *testing.T) {
+	cfg := NullConfig("", "")
+
+	cfg.productVariables = ProductVariables{
+		ConfiguredJarLocationOverrides: []string{
+			"platform:libfoo-old:com.android.foo:libfoo-new",
+			"com.android.bar:libbar-old:platform:libbar-new",
+		},
+	}
+
+	apex, jar := OverrideConfiguredJarLocationFor(cfg, "platform", "libfoo-old")
+	AssertStringEquals(t, "", "com.android.foo", apex)
+	AssertStringEquals(t, "", "libfoo-new", jar)
+
+	apex, jar = OverrideConfiguredJarLocationFor(cfg, "platform", "libbar-old")
+	AssertStringEquals(t, "", "platform", apex)
+	AssertStringEquals(t, "", "libbar-old", jar)
+
+	apex, jar = OverrideConfiguredJarLocationFor(cfg, "com.android.bar", "libbar-old")
+	AssertStringEquals(t, "", "platform", apex)
+	AssertStringEquals(t, "", "libbar-new", jar)
+
+	apex, jar = OverrideConfiguredJarLocationFor(cfg, "platform", "libbar-old")
+	AssertStringEquals(t, "", "platform", apex)
+	AssertStringEquals(t, "", "libbar-old", jar)
+}
diff --git a/android/depset_generic.go b/android/depset_generic.go
index 4f31b86..45c1937 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -79,8 +79,8 @@
 	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)
+		directCopy = ReverseSlice(direct)
+		transitiveCopy = ReverseSlice(transitive)
 	} else {
 		directCopy = append([]T(nil), direct...)
 		transitiveCopy = append([]*DepSet[T](nil), transitive...)
@@ -184,7 +184,7 @@
 	})
 	list = firstUniqueInPlace(list)
 	if d.reverse {
-		reverseSliceInPlace(list)
+		ReverseSliceInPlace(list)
 	}
 	return list
 }
diff --git a/android/fixture.go b/android/fixture.go
index dbc3bc5..6660afd 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -369,7 +369,7 @@
 
 // Allow access to the product variables when preparing the fixture.
 type FixtureProductVariables struct {
-	*productVariables
+	*ProductVariables
 }
 
 // Modify product variables.
diff --git a/android/module.go b/android/module.go
index 384776a..4c781f6 100644
--- a/android/module.go
+++ b/android/module.go
@@ -4029,43 +4029,26 @@
 	JavaBp2buildTargetName() string
 }
 
-// PartitionXsdSrcs partitions srcs into xsd_config modules and others
-// Since xsd_config are soong modules, we cannot use file extension for partitioning
-func PartitionXsdSrcs(ctx BazelConversionPathContext, srcs []string) ([]string, []string) {
-	//isXsd returns true if src is a soong module of type xsd_config
-	isXsd := func(src string) bool {
-		mod, exists := ctx.ModuleFromName(src)
+// XsdModuleToTargetName is a function that takes an XsdConfigBp2buildTarget
+type XsdModuleToTargetName func(xsd XsdConfigBp2buildTargets) string
+
+// XsdLabelMapper returns a bazel.LabelMapper for partitioning XSD sources/headers given an
+// XsdModuleToTargetName function.
+func XsdLabelMapper(targetName XsdModuleToTargetName) bazel.LabelMapper {
+	return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+		mod, exists := ctx.ModuleFromName(label.OriginalModuleName)
 		if !exists {
-			return false
+			return label.Label, false
 		}
-		_, _isXsd := mod.(XsdConfigBp2buildTargets)
-		return _isXsd
-	}
-	nonXsd := []string{}
-	xsd := []string{}
-
-	for _, src := range srcs {
-		if isXsd(src) {
-			xsd = append(xsd, src)
-		} else {
-			nonXsd = append(nonXsd, src)
+		xsdMod, isXsd := mod.(XsdConfigBp2buildTargets)
+		if !isXsd {
+			return label.Label, false
 		}
-	}
 
-	return nonXsd, xsd
-}
-
-// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-{cpp|java}
-// The new target name is provided by the `targetName` callback function
-func XsdConfigBp2buildTarget(ctx BazelConversionPathContext, mod blueprint.Module, targetName func(xsd XsdConfigBp2buildTargets) string) string {
-	xsd, isXsd := mod.(XsdConfigBp2buildTargets)
-	if !isXsd {
-		ctx.ModuleErrorf("xsdConfigJavaTarget called on %v, which is not an xsd_config", mod)
+		// Remove the base module name
+		ret := strings.TrimSuffix(label.Label, mod.Name())
+		// Append the language specific target name
+		ret += targetName(xsdMod)
+		return ret, true
 	}
-	ret := BazelModuleLabel(ctx, mod)
-	// Remove the base module name
-	ret = strings.TrimSuffix(ret, mod.Name())
-	// Append the language specific target name
-	ret += targetName(xsd)
-	return ret
 }
diff --git a/android/paths.go b/android/paths.go
index fda4d2f..325a953 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -396,7 +396,7 @@
 //
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_deps mutator.
+// pathdeps mutator.
 // If a requested module is not found as a dependency:
 //   - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
 //     missing dependencies
@@ -425,7 +425,7 @@
 // excluding the items (similarly resolved
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_deps mutator.
+// pathdeps mutator.
 // If a requested module is not found as a dependency:
 //   - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
 //     missing dependencies
@@ -560,7 +560,7 @@
 // and a list of the module names of missing module dependencies are returned as the second return.
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_deps mutator.
+// pathdeps mutator.
 func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) {
 	return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{
 		Context:      ctx,
@@ -1029,16 +1029,16 @@
 	return p
 }
 
+func (p basePath) RelativeToTop() Path {
+	ensureTestOnly()
+	return p
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
 }
 
-func (p SourcePath) RelativeToTop() Path {
-	ensureTestOnly()
-	return p
-}
-
 var _ Path = SourcePath{}
 
 func (p SourcePath) withRel(rel string) SourcePath {
@@ -1126,6 +1126,16 @@
 	return path
 }
 
+// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
+// the path is relative to the root of the output folder, not the out/soong folder.
+func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
+	p, err := validatePath(pathComponents...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
+}
+
 // MaybeExistentPathForSource joins the provided path components and validates that the result
 // neither escapes the source dir nor is in the out dir.
 // It does not validate whether the path exists.
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 155fbdf..777c1cf 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -53,6 +53,7 @@
 	remoteable       RemoteRuleSupports
 	rbeParams        *remoteexec.REParams
 	outDir           WritablePath
+	sboxOutSubDir    string
 	sboxTools        bool
 	sboxInputs       bool
 	sboxManifestPath WritablePath
@@ -65,9 +66,18 @@
 		pctx:           pctx,
 		ctx:            ctx,
 		temporariesSet: make(map[WritablePath]bool),
+		sboxOutSubDir:  sboxOutSubDir,
 	}
 }
 
+// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string
+// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds)
+// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
+func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
+	rb.sboxOutSubDir = ""
+	return rb
+}
+
 // RuleBuilderInstall is a tuple of install from and to locations.
 type RuleBuilderInstall struct {
 	From Path
@@ -582,12 +592,10 @@
 
 		// Add copy rules to the manifest to copy each output file from the sbox directory.
 		// to the output directory after running the commands.
-		sboxOutputs := make([]string, len(outputs))
-		for i, output := range outputs {
+		for _, output := range outputs {
 			rel := Rel(r.ctx, r.outDir.String(), output.String())
-			sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
 			command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
-				From: proto.String(filepath.Join(sboxOutSubDir, rel)),
+				From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
 				To:   proto.String(output.String()),
 			})
 		}
diff --git a/android/sdk_version_test.go b/android/sdk_version_test.go
index ea99c4d..30bd002 100644
--- a/android/sdk_version_test.go
+++ b/android/sdk_version_test.go
@@ -75,7 +75,7 @@
 
 	config := NullConfig("", "")
 
-	config.productVariables = productVariables{
+	config.productVariables = ProductVariables{
 		Platform_sdk_version:              intPtr(31),
 		Platform_sdk_codename:             stringPtr("Tiramisu"),
 		Platform_version_active_codenames: []string{"Tiramisu"},
diff --git a/android/test_config.go b/android/test_config.go
index 28d9ec4..2a59d92 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -35,7 +35,7 @@
 	envCopy["PATH"] = os.Getenv("PATH")
 
 	config := &config{
-		productVariables: productVariables{
+		productVariables: ProductVariables{
 			DeviceName:                          stringPtr("test_device"),
 			DeviceProduct:                       stringPtr("test_product"),
 			Platform_sdk_version:                intPtr(30),
diff --git a/android/util.go b/android/util.go
index 50bf5aa..5375373 100644
--- a/android/util.go
+++ b/android/util.go
@@ -137,19 +137,17 @@
 }
 
 // IndexList returns the index of the first occurrence of the given string in the list or -1
-func IndexList(s string, list []string) int {
+func IndexList[T comparable](t T, list []T) int {
 	for i, l := range list {
-		if l == s {
+		if l == t {
 			return i
 		}
 	}
-
 	return -1
 }
 
-// InList checks if the string belongs to the list
-func InList(s string, list []string) bool {
-	return IndexList(s, list) != -1
+func InList[T comparable](t T, list []T) bool {
+	return IndexList(t, list) != -1
 }
 
 func setFromList[T comparable](l []T) map[T]bool {
@@ -351,15 +349,19 @@
 	return in[0:writeIndex]
 }
 
-// reverseSliceInPlace reverses the elements of a slice in place.
-func reverseSliceInPlace[T any](in []T) {
+// 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 {
+// 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]
diff --git a/android/util_test.go b/android/util_test.go
index 0e28b56..20161e5 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -20,6 +20,7 @@
 	"strconv"
 	"strings"
 	"testing"
+	"unsafe"
 )
 
 var firstUniqueStringsTestCases = []struct {
@@ -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 3bec854..7fb81b9 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -95,6 +95,10 @@
 			Cflags []string
 		}
 
+		Device_page_size_agnostic struct {
+			Cflags []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+
 		Override_rs_driver struct {
 			Cflags []string
 		}
@@ -180,12 +184,19 @@
 			Srcs         []string `android:"arch_variant"`
 			Exclude_srcs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
+
+		// release_aidl_use_unfrozen is "true" when a device can
+		// use the unfrozen versions of AIDL interfaces.
+		Release_aidl_use_unfrozen struct {
+			Cflags []string
+			Cmd    *string
+		}
 	} `android:"arch_variant"`
 }
 
 var defaultProductVariables interface{} = variableProperties{}
 
-type productVariables struct {
+type ProductVariables struct {
 	// Suffix to add to generated Makefiles
 	Make_suffix *string `json:",omitempty"`
 
@@ -275,6 +286,7 @@
 	Safestack                    *bool    `json:",omitempty"`
 	HostStaticBinaries           *bool    `json:",omitempty"`
 	Binder32bit                  *bool    `json:",omitempty"`
+	Device_page_size_agnostic    *bool    `json:",omitempty"`
 	UseGoma                      *bool    `json:",omitempty"`
 	UseRBE                       *bool    `json:",omitempty"`
 	UseRBEJAVAC                  *bool    `json:",omitempty"`
@@ -401,9 +413,10 @@
 
 	WithDexpreopt bool `json:",omitempty"`
 
-	ManifestPackageNameOverrides []string `json:",omitempty"`
-	CertificateOverrides         []string `json:",omitempty"`
-	PackageNameOverrides         []string `json:",omitempty"`
+	ManifestPackageNameOverrides   []string `json:",omitempty"`
+	CertificateOverrides           []string `json:",omitempty"`
+	PackageNameOverrides           []string `json:",omitempty"`
+	ConfiguredJarLocationOverrides []string `json:",omitempty"`
 
 	ApexGlobalMinSdkVersionOverride *string `json:",omitempty"`
 
@@ -456,6 +469,8 @@
 
 	SelinuxIgnoreNeverallows bool `json:",omitempty"`
 
+	Release_aidl_use_unfrozen *bool `json:",omitempty"`
+
 	SepolicyFreezeTestExtraDirs         []string `json:",omitempty"`
 	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
 
@@ -474,6 +489,8 @@
 
 	ReleaseVersion          string   `json:",omitempty"`
 	ReleaseAconfigValueSets []string `json:",omitempty"`
+
+	KeepVndk *bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -488,8 +505,8 @@
 	return &v
 }
 
-func (v *productVariables) SetDefaultConfig() {
-	*v = productVariables{
+func (v *ProductVariables) SetDefaultConfig() {
+	*v = ProductVariables{
 		BuildNumberFile: stringPtr("build_number.txt"),
 
 		Platform_version_name:                  stringPtr("S"),
@@ -526,6 +543,7 @@
 		Safestack:                    boolPtr(false),
 		TrimmedApex:                  boolPtr(false),
 		Build_from_text_stub:         boolPtr(false),
+		Device_page_size_agnostic:    boolPtr(false),
 
 		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
 		ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
@@ -725,7 +743,9 @@
 			dst = append(dst, src...)
 			(*p)[propertyName][key] = dst
 		default:
-			panic(fmt.Errorf("TODO: handle merging value %#v", existing))
+			if existing != propertyValue {
+				panic(fmt.Errorf("TODO: handle merging value %#v", existing))
+			}
 		}
 	} else {
 		(*p)[propertyName][key] = propertyValue
@@ -938,7 +958,7 @@
 						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
 					}
 				}
-			} else {
+			} else if !archOrOsSpecificStruct.IsZero() {
 				// One problem with supporting additional fields is that if multiple branches of
 				// "target" overlap, we don't want them to be in the same select statement (aka
 				// configuration axis). "android" and "host" are disjoint, so it's ok that we only
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/apex.go b/apex/apex.go
index 1d094eb..b26d1d2 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -993,7 +993,7 @@
 	// the non-system APEXes because the VNDK libraries won't be included (and duped) in the
 	// APEX, but shared across APEXes via the VNDK APEX.
 	useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
-	excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
+	excludeVndkLibs := useVndk && a.useVndkAsStable(mctx)
 	if proptools.Bool(a.properties.Use_vndk_as_stable) {
 		if !useVndk {
 			mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
@@ -2394,7 +2394,7 @@
 	// tags used below are private (e.g. `cc.sharedDepTag`).
 	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
 		if ch, ok := child.(*cc.Module); ok {
-			if ch.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && ch.IsVndk() {
+			if ch.UseVndk() && a.useVndkAsStable(ctx) && ch.IsVndk() {
 				vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk")
 				return false
 			}
@@ -3277,31 +3277,6 @@
 	// Module separator
 	//
 	m["com.android.runtime"] = []string{
-		"libc_aeabi",
-		"libc_bionic",
-		"libc_bionic_ndk",
-		"libc_bootstrap",
-		"libc_common",
-		"libc_common_shared",
-		"libc_dns",
-		"libc_dynamic_dispatch",
-		"libc_fortify",
-		"libc_freebsd",
-		"libc_freebsd_large_stack",
-		"libc_gdtoa",
-		"libc_init_dynamic",
-		"libc_init_static",
-		"libc_jemalloc_wrapper",
-		"libc_netbsd",
-		"libc_nomalloc",
-		"libc_nopthread",
-		"libc_openbsd",
-		"libc_openbsd_large_stack",
-		"libc_openbsd_ndk",
-		"libc_pthread",
-		"libc_syscalls",
-		"libc_tzcode",
-		"libc_unwind_static",
 		"libdebuggerd",
 		"libdebuggerd_common_headers",
 		"libdebuggerd_handler_core",
@@ -3313,7 +3288,6 @@
 		"libprocinfo",
 		"libpropertyinfoparser",
 		"libscudo",
-		"libstdc++",
 		"libsystemproperties",
 		"libtombstoned_client_static",
 		"libunwindstack",
@@ -3742,3 +3716,12 @@
 func (a *apexBundle) IsTestApex() bool {
 	return a.testApex
 }
+
+func (a *apexBundle) useVndkAsStable(ctx android.BaseModuleContext) bool {
+	// VNDK cannot be linked if it is deprecated
+	if ctx.Config().IsVndkDeprecated() {
+		return false
+	}
+
+	return proptools.Bool(a.properties.Use_vndk_as_stable)
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b67535a..9dba08e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3029,7 +3029,11 @@
 			vendor: true,
 			shared_libs: ["libvndk", "libvendor"],
 		}
-	`)
+	`,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.KeepVndk = proptools.BoolPtr(true)
+		}),
+	)
 
 	vendorVariant := "android_vendor.29_arm64_armv8-a"
 
@@ -5587,6 +5591,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
 		`)
 	})
 
@@ -5664,6 +5669,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()
@@ -5758,6 +5764,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: {
@@ -5773,6 +5801,7 @@
 
 		prebuilt_bootclasspath_fragment {
 			name: "my-bootclasspath-fragment",
+			prefer: true,
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
 			hidden_api: {
@@ -5797,6 +5826,7 @@
 			name: "libfoo",
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
+			installable: true,
 		}
 
 		java_sdk_library_import {
@@ -5815,6 +5845,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
+			compile_dex: true,
 		}
 	`
 
@@ -5827,6 +5858,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
 		`)
 	})
 
@@ -5835,8 +5867,8 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			java_libs: ["libfoo", "libbar"],
 			updatable: false,
+			bootclasspath_fragments: ["my-bootclasspath-fragment"],
 		}
 
 		apex_key {
@@ -5845,6 +5877,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: {
@@ -5883,6 +5924,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 			permitted_packages: ["foo"],
+			installable: true,
 		}
 
 		java_sdk_library_import {
@@ -5900,6 +5942,7 @@
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
 			permitted_packages: ["bar"],
+			compile_dex: true,
 		}
 	`
 
@@ -5910,8 +5953,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
 		`)
 	})
 
@@ -5921,7 +5965,7 @@
 			name: "myapex",
 			enabled: false,
 			key: "myapex.key",
-			java_libs: ["libfoo", "libbar"],
+			bootclasspath_fragments: ["my-bootclasspath-fragment"],
 		}
 
 		apex_key {
@@ -5930,6 +5974,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: {
@@ -5959,7 +6013,6 @@
 
 		java_import {
 			name: "libfoo",
-			prefer: true,
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
 			permitted_packages: ["foo"],
@@ -5967,13 +6020,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"],
 			},
@@ -5984,9 +6038,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,
 		}
 	`
 
@@ -5999,6 +6055,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
 		`)
 	})
 }
@@ -6737,6 +6794,10 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
+		override_apex {
+			name: "myoverrideapex",
+			base: "bar",
+		}
 	`)
 
 	fooManifestRule := result.ModuleForTests("foo", "android_common_foo_image").Rule("apexManifestRule")
@@ -6753,6 +6814,12 @@
 	if barActualDefaultVersion != barExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
 	}
+
+	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_bar_image").Rule("apexManifestRule")
+	overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"]
+	if overrideBarActualDefaultVersion != barExpectedDefaultVersion {
+		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
+	}
 }
 
 func TestApexAvailable_ApexAvailableName(t *testing.T) {
@@ -8163,126 +8230,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()
 
@@ -8306,7 +8253,7 @@
 	result := android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
 		java.PrepareForTestWithHiddenApiBuildComponents,
-		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithDexpreopt,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		PrepareForTestWithApexBuildComponents,
 		preparer,
@@ -8321,12 +8268,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).
@@ -8633,126 +8584,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) {
@@ -10736,6 +10567,7 @@
 			src: "libc.so",
 			min_sdk_version: "29",
 			recovery_available: true,
+			vendor_available: true,
 		}
 		api_imports {
 			name: "api_imports",
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 1b52886..f30f7f6 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)
 	}
 
@@ -387,6 +297,7 @@
 			// Configure some libraries in the art bootclasspath_fragment that match the source
 			// bootclasspath_fragment's contents property.
 			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
 			addSource("foo", "bar"),
 			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
@@ -399,13 +310,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("dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
@@ -454,6 +365,7 @@
 			// Configure some libraries in the art bootclasspath_fragment that match the source
 			// bootclasspath_fragment's contents property.
 			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
 			addSource("foo", "bar"),
 
 			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
@@ -469,14 +381,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("dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
@@ -552,6 +464,7 @@
 
 		// Configure some libraries in the art bootclasspath_fragment.
 		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+		dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
 		java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 	)
 
@@ -566,7 +479,7 @@
 					src: "com.android.art-arm.apex",
 				},
 			},
-			exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
 
 		java_import {
@@ -586,7 +499,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 +507,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 +521,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 +530,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("dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index bba8bb6..2e828ca 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -138,8 +138,8 @@
 		prepareForTestWithArtApex,
 	).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
 
-	platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common")
-	rule := platformBootclasspath.Output(ruleFile)
+	dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
+	rule := dexBootJars.Output(ruleFile)
 
 	inputs := rule.Implicits.Strings()
 	sort.Strings(inputs)
@@ -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-bootclasspath-fragment/boot.prof",
+		"out/soong/.intermediates/default/java/dex_bootjars/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/default/java/dex_bootjars/android_common/boot/boot.prof",
 	}
 
 	expectedOutputs := []string{
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 95e52ae..d77d59a 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -17,15 +17,15 @@
 import (
 	"crypto/sha256"
 	"encoding/base64"
+	"encoding/json"
 	"fmt"
 	"path/filepath"
+	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 	"reflect"
 	"sort"
 	"strings"
 	"sync"
 
-	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
-
 	"github.com/google/blueprint/metrics"
 	"github.com/google/blueprint/proptools"
 	"google.golang.org/protobuf/proto"
@@ -116,6 +116,13 @@
 	InputDepsetHashes []string
 	InputPaths        []string
 	FileContents      string
+	// If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
+	// and run the mixed build action there
+	ShouldRunInSbox bool
+	// A list of files to add as implicit deps to the outputs of this BuildStatement.
+	// Unlike most properties in BuildStatement, these paths must be relative to the root of
+	// the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
+	ImplicitDeps []string
 }
 
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -456,7 +463,7 @@
 // escapes the args received from aquery and creates a command string
 func commandString(actionEntry *analysis_v2_proto.Action) string {
 	switch actionEntry.Mnemonic {
-	case "GoCompilePkg":
+	case "GoCompilePkg", "GoStdlib":
 		argsEscaped := []string{}
 		for _, arg := range actionEntry.Arguments {
 			if arg == "" {
@@ -496,6 +503,12 @@
 		Env:               actionEntry.EnvironmentVariables,
 		Mnemonic:          actionEntry.Mnemonic,
 	}
+	if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
+		// Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
+		// This causes issues for `GOCACHE=$(mktemp -d) go build ...`
+		// To prevent this, sandbox this action in mixed builds as well
+		buildStatement.ShouldRunInSbox = true
+	}
 	return buildStatement, nil
 }
 
@@ -572,6 +585,72 @@
 	}, nil
 }
 
+type bazelSandwichJson struct {
+	Target         string   `json:"target"`
+	DependOnTarget *bool    `json:"depend_on_target,omitempty"`
+	ImplicitDeps   []string `json:"implicit_deps"`
+}
+
+func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+	if err != nil {
+		return nil, err
+	}
+	if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
+		return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
+	}
+	target := actionEntry.UnresolvedSymlinkTarget
+	if target == "" {
+		return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
+	}
+	if filepath.Clean(target) != target {
+		return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
+	}
+	if strings.HasPrefix(target, "/") {
+		return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
+	}
+
+	out := outputPaths[0]
+	outDir := filepath.Dir(out)
+	var implicitDeps []string
+	if strings.HasPrefix(target, "bazel_sandwich:") {
+		j := bazelSandwichJson{}
+		err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
+		if err != nil {
+			return nil, err
+		}
+		if proptools.BoolDefault(j.DependOnTarget, true) {
+			implicitDeps = append(implicitDeps, j.Target)
+		}
+		implicitDeps = append(implicitDeps, j.ImplicitDeps...)
+		dotDotsToReachCwd := ""
+		if outDir != "." {
+			dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
+		}
+		target = proptools.ShellEscapeIncludingSpaces(j.Target)
+		target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
+	} else {
+		target = proptools.ShellEscapeIncludingSpaces(target)
+	}
+
+	outDir = proptools.ShellEscapeIncludingSpaces(outDir)
+	out = proptools.ShellEscapeIncludingSpaces(out)
+	// Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
+	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
+	symlinkPaths := outputPaths[:]
+
+	buildStatement := &BuildStatement{
+		Command:      command,
+		Depfile:      depfile,
+		OutputPaths:  outputPaths,
+		Env:          actionEntry.EnvironmentVariables,
+		Mnemonic:     actionEntry.Mnemonic,
+		SymlinkPaths: symlinkPaths,
+		ImplicitDeps: implicitDeps,
+	}
+	return buildStatement, nil
+}
+
 func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
@@ -677,10 +756,12 @@
 		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)
+	case "UnresolvedSymlink":
+		return a.unresolvedSymlinkActionBuildStatement(actionEntry)
 	}
 
 	if len(actionEntry.Arguments) < 1 {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 19a584f..32c87a0 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -357,9 +357,11 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	if expected := 1; len(actual) != expected {
 		t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
+		return
 	}
 
 	bs := actual[0]
@@ -544,6 +546,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -756,9 +759,11 @@
 	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	if expected := 2; len(actualBuildStatements) != expected {
 		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
+		return
 	}
 
 	expectedDepsetFiles := [][]string{
@@ -859,6 +864,7 @@
 
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -907,6 +913,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -1017,6 +1024,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -1088,6 +1096,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -1126,6 +1135,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -1136,6 +1146,126 @@
 	}, actual)
 }
 
+func TestUnresolvedSymlink(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "symlink/target"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf symlink/target path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+	}}, actual)
+}
+
+func TestUnresolvedSymlinkBazelSandwich(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "bazel_sandwich:{\"target\":\"target/product/emulator_x86_64/system\"}"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+		ImplicitDeps: []string{"target/product/emulator_x86_64/system"},
+	}}, actual)
+}
+
+func TestUnresolvedSymlinkBazelSandwichWithAlternativeDeps(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "bazel_sandwich:{\"depend_on_target\":false,\"implicit_deps\":[\"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp\"],\"target\":\"target/product/emulator_x86_64/system\"}"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+		// Note that the target of the symlink, target/product/emulator_x86_64/system, is not listed here
+		ImplicitDeps: []string{"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp"},
+	}}, actual)
+}
+
 func assertError(t *testing.T, err error, expected string) {
 	t.Helper()
 	if err == nil {
@@ -1201,6 +1331,9 @@
 	if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
 		return "SymlinkPaths"
 	}
+	if !reflect.DeepEqual(sortedStrings(first.ImplicitDeps), sortedStrings(second.ImplicitDeps)) {
+		return "ImplicitDeps"
+	}
 	if first.Depfile != second.Depfile {
 		return "Depfile"
 	}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 8f63ec4..d962a1d 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -74,6 +74,8 @@
 
 	InApex  = "in_apex"
 	NonApex = "non_apex"
+
+	ErrorproneDisabled = "errorprone_disabled"
 )
 
 func PowerSetWithoutEmptySet[T any](items []T) [][]T {
@@ -216,6 +218,11 @@
 		NonApex:                    "//build/bazel/rules/apex:non_apex",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
 	}
+
+	errorProneMap = map[string]string{
+		ErrorproneDisabled:         "//build/bazel/rules/java/errorprone:errorprone_globally_disabled",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
+	}
 )
 
 // basic configuration types
@@ -229,6 +236,7 @@
 	productVariables
 	osAndInApex
 	inApex
+	errorProneDisabled
 )
 
 func osArchString(os string, arch string) string {
@@ -237,13 +245,14 @@
 
 func (ct configurationType) String() string {
 	return map[configurationType]string{
-		noConfig:         "no_config",
-		arch:             "arch",
-		os:               "os",
-		osArch:           "arch_os",
-		productVariables: "product_variables",
-		osAndInApex:      "os_in_apex",
-		inApex:           "in_apex",
+		noConfig:           "no_config",
+		arch:               "arch",
+		os:                 "os",
+		osArch:             "arch_os",
+		productVariables:   "product_variables",
+		osAndInApex:        "os_in_apex",
+		inApex:             "in_apex",
+		errorProneDisabled: "errorprone_disabled",
 	}[ct]
 }
 
@@ -274,6 +283,10 @@
 		if _, ok := inApexMap[config]; !ok {
 			panic(fmt.Errorf("Unknown in_apex config: %s", config))
 		}
+	case errorProneDisabled:
+		if _, ok := errorProneMap[config]; !ok {
+			panic(fmt.Errorf("Unknown errorprone config: %s", config))
+		}
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
 	}
@@ -303,6 +316,8 @@
 		return config
 	case inApex:
 		return inApexMap[config]
+	case errorProneDisabled:
+		return errorProneMap[config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
 	}
@@ -321,6 +336,8 @@
 	OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
 	// An axis for in_apex-specific configurations
 	InApexAxis = ConfigurationAxis{configurationType: inApex}
+
+	ErrorProneAxis = ConfigurationAxis{configurationType: errorProneDisabled}
 )
 
 // ProductVariableConfigurationAxis returns an axis for the given product variable
diff --git a/bazel/properties.go b/bazel/properties.go
index e22f4db..702c31c 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -288,6 +288,41 @@
 	return result
 }
 
+// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for
+// each axis/configuration by keeping the first instance of a Label and omitting all subsequent
+// repetitions.
+func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute {
+	var result LabelListAttribute
+	result.Value = FirstUniqueBazelLabelList(attr.Value)
+	if attr.HasConfigurableValues() {
+		result.ConfigurableValues = make(configurableLabelLists)
+	}
+	for axis, configToLabels := range attr.ConfigurableValues {
+		for c, l := range configToLabels {
+			result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l))
+		}
+	}
+
+	return result
+}
+
+// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each
+// axis/configuration.
+func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
+	var result LabelListAttribute
+	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
+	if haystack.HasConfigurableValues() {
+		result.ConfigurableValues = make(configurableLabelLists)
+	}
+	for axis, configToLabels := range haystack.ConfigurableValues {
+		for haystackConfig, haystackLabels := range configToLabels {
+			result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig)))
+		}
+	}
+
+	return result
+}
+
 type Attribute interface {
 	HasConfigurableValues() bool
 }
@@ -766,7 +801,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		lla.Value = list
-	case arch, os, osArch, productVariables, osAndInApex, inApex:
+	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled:
 		if lla.ConfigurableValues == nil {
 			lla.ConfigurableValues = make(configurableLabelLists)
 		}
@@ -782,7 +817,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		return lla.Value
-	case arch, os, osArch, productVariables, osAndInApex, inApex:
+	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled:
 		return lla.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -1346,7 +1381,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		sla.Value = list
-	case arch, os, osArch, productVariables, osAndInApex:
+	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled:
 		if sla.ConfigurableValues == nil {
 			sla.ConfigurableValues = make(configurableStringLists)
 		}
@@ -1362,7 +1397,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		return sla.Value
-	case arch, os, osArch, productVariables, osAndInApex:
+	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled:
 		return sla.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index c56d11f..c98ae0e 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -125,6 +125,63 @@
 		}
 	}
 }
+
+func TestSubtractBazelLabelListAttribute(t *testing.T) {
+	testCases := []struct {
+		haystack LabelListAttribute
+		needle   LabelListAttribute
+		expected LabelListAttribute
+	}{
+		{
+			haystack: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"a", "b", "a", "c"},
+					[]string{"x", "x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"arm_1", "arm_2"}, []string{}),
+						"x86": makeLabelList([]string{"x86_3", "x86_4", "x86_5"}, []string{"x86_5"}),
+					},
+				},
+			},
+			needle: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"d", "a"},
+					[]string{"x", "y2", "z2"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"arm_1", "arm_3"}, []string{}),
+						"x86": makeLabelList([]string{"x86_3", "x86_4"}, []string{"x86_6"}),
+					},
+				},
+			},
+			expected: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"b", "c"},
+					[]string{"x", "x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"arm_2"}, []string{}),
+						"x86": makeLabelList([]string{"x86_5"}, []string{"x86_5"}),
+					},
+				},
+				ForceSpecifyEmptyList: false,
+				EmitEmptyList:         false,
+				Prepend:               false,
+			},
+		},
+	}
+	for _, tc := range testCases {
+		got := SubtractBazelLabelListAttribute(tc.haystack, tc.needle)
+		if !reflect.DeepEqual(tc.expected, got) {
+			t.Fatalf("Expected\n%v, but got\n%v", tc.expected, got)
+		}
+	}
+}
+
 func TestFirstUniqueBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
@@ -167,6 +224,46 @@
 	}
 }
 
+func TestFirstUniqueBazelLabelListAttribute(t *testing.T) {
+	testCases := []struct {
+		originalLabelList       LabelListAttribute
+		expectedUniqueLabelList LabelListAttribute
+	}{
+		{
+			originalLabelList: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"a", "b", "a", "c"},
+					[]string{"x", "x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"1", "2", "1"}, []string{}),
+						"x86": makeLabelList([]string{"3", "4", "4"}, []string{"5", "5"}),
+					},
+				},
+			},
+			expectedUniqueLabelList: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"a", "b", "c"},
+					[]string{"x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"1", "2"}, []string{}),
+						"x86": makeLabelList([]string{"3", "4"}, []string{"5"}),
+					},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualUniqueLabelList := FirstUniqueBazelLabelListAttribute(tc.originalLabelList)
+		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
+			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
+		}
+	}
+}
+
 func TestUniqueSortedBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 782a88c..4a3786f 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",
@@ -44,6 +46,7 @@
         "apex_conversion_test.go",
         "apex_key_conversion_test.go",
         "build_conversion_test.go",
+        "bp2build_product_config_test.go",
         "bzl_conversion_test.go",
         "cc_binary_conversion_test.go",
         "cc_library_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/bp2build.go b/bp2build/bp2build.go
index b22cb28..cfe52db 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -80,6 +80,12 @@
 		os.Exit(1)
 	}
 	bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
+	injectionFiles, additionalBp2buildFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics)
+	if err != nil {
+		fmt.Printf("%s\n", err.Error())
+		os.Exit(1)
+	}
+	bp2buildFiles = append(bp2buildFiles, additionalBp2buildFiles...)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 	// Delete files under the bp2build root which weren't just written. An
 	// alternative would have been to delete the whole directory and write these
@@ -88,11 +94,6 @@
 	// performance implications.
 	deleteFilesExcept(ctx, bp2buildDir, bp2buildFiles)
 
-	injectionFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics)
-	if err != nil {
-		fmt.Printf("%s\n", err.Error())
-		os.Exit(1)
-	}
 	writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles)
 	starlarkDeps, err := starlark_import.GetNinjaDeps()
 	if err != nil {
@@ -107,20 +108,20 @@
 // This includes
 // 1. config value(s) that are hardcoded in Soong
 // 2. product_config variables
-func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) {
+func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, []BazelFile, error) {
 	var ret []BazelFile
 
-	productConfigFiles, err := CreateProductConfigFiles(ctx)
+	productConfigInjectionFiles, productConfigBp2BuildDirFiles, err := CreateProductConfigFiles(ctx)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-	ret = append(ret, productConfigFiles...)
+	ret = append(ret, productConfigInjectionFiles...)
 	injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-	ret = append(ret, injectionFiles...)
-	return ret, nil
+	ret = append(injectionFiles, ret...)
+	return ret, productConfigBp2BuildDirFiles, nil
 }
 
 // Get the output directory and create it if it doesn't exist.
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 7224496..2513af8 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -1,14 +1,21 @@
 package bp2build
 
 import (
+	"android/soong/android"
+	"android/soong/starlark_import"
+	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
+	"reflect"
 	"strings"
+
+	"github.com/google/blueprint/proptools"
+	"go.starlark.net/starlark"
 )
 
 func CreateProductConfigFiles(
-	ctx *CodegenContext) ([]BazelFile, error) {
+	ctx *CodegenContext) ([]BazelFile, []BazelFile, error) {
 	cfg := &ctx.config
 	targetProduct := "unknown"
 	if cfg.HasDeviceProduct() {
@@ -25,9 +32,14 @@
 	if !strings.HasPrefix(productVariablesFileName, "/") {
 		productVariablesFileName = filepath.Join(ctx.topDir, productVariablesFileName)
 	}
-	bytes, err := os.ReadFile(productVariablesFileName)
+	productVariablesBytes, err := os.ReadFile(productVariablesFileName)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
+	}
+	productVariables := android.ProductVariables{}
+	err = json.Unmarshal(productVariablesBytes, &productVariables)
+	if err != nil {
+		return nil, nil, err
 	}
 
 	// TODO(b/249685973): the name is product_config_platforms because product_config
@@ -39,11 +51,16 @@
 		"{VARIANT}", targetBuildVariant,
 		"{PRODUCT_FOLDER}", currentProductFolder)
 
-	result := []BazelFile{
+	platformMappingContent, err := platformMappingContent(productReplacer.Replace("@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"), &productVariables)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	injectionDirFiles := []BazelFile{
 		newFile(
 			currentProductFolder,
 			"soong.variables.bzl",
-			`variables = json.decode("""`+strings.ReplaceAll(string(bytes), "\\", "\\\\")+`""")`),
+			`variables = json.decode("""`+strings.ReplaceAll(string(productVariablesBytes), "\\", "\\\\")+`""")`),
 		newFile(
 			currentProductFolder,
 			"BUILD",
@@ -80,6 +97,7 @@
 android_product(
     name = "mixed_builds_product-{VARIANT}",
     soong_variables = _soong_variables,
+    extra_constraints = ["@//build/bazel/platforms:mixed_builds"],
 )
 `)),
 		newFile(
@@ -99,6 +117,7 @@
 			"product_config_platforms",
 			"common.bazelrc",
 			productReplacer.Replace(`
+build --platform_mappings=platform_mappings
 build --platforms @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
 
 build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
@@ -120,6 +139,148 @@
 build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
 `)),
 	}
+	bp2buildDirFiles := []BazelFile{
+		newFile(
+			"",
+			"platform_mappings",
+			platformMappingContent),
+	}
+	return injectionDirFiles, bp2buildDirFiles, nil
+}
+
+func platformMappingContent(mainProductLabel string, mainProductVariables *android.ProductVariables) (string, error) {
+	productsForTesting, err := starlark_import.GetStarlarkValue[map[string]map[string]starlark.Value]("products_for_testing")
+	if err != nil {
+		return "", err
+	}
+	var result strings.Builder
+	result.WriteString("platforms:\n")
+	platformMappingSingleProduct(mainProductLabel, mainProductVariables, &result)
+	for product, productVariablesStarlark := range productsForTesting {
+		productVariables, err := starlarkMapToProductVariables(productVariablesStarlark)
+		if err != nil {
+			return "", err
+		}
+		platformMappingSingleProduct("@//build/bazel/tests/products:"+product, &productVariables, &result)
+	}
+	return result.String(), nil
+}
+
+var bazelPlatformSuffixes = []string{
+	"",
+	"_darwin_arm64",
+	"_darwin_x86_64",
+	"_linux_bionic_arm64",
+	"_linux_bionic_x86_64",
+	"_linux_musl_x86",
+	"_linux_musl_x86_64",
+	"_linux_x86",
+	"_linux_x86_64",
+	"_windows_x86",
+	"_windows_x86_64",
+}
+
+func platformMappingSingleProduct(label string, productVariables *android.ProductVariables, result *strings.Builder) {
+	targetBuildVariant := "user"
+	if proptools.Bool(productVariables.Eng) {
+		targetBuildVariant = "eng"
+	} else if proptools.Bool(productVariables.Debuggable) {
+		targetBuildVariant = "userdebug"
+	}
+
+	for _, suffix := range bazelPlatformSuffixes {
+		result.WriteString("  ")
+		result.WriteString(label)
+		result.WriteString(suffix)
+		result.WriteString("\n")
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:always_use_prebuilt_sdks=%t\n", proptools.Bool(productVariables.Always_use_prebuilt_sdks)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:apex_global_min_sdk_version_override=%s\n", proptools.String(productVariables.ApexGlobalMinSdkVersionOverride)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:build_id=%s\n", proptools.String(productVariables.BuildId)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:build_version_tags=%s\n", strings.Join(productVariables.BuildVersionTags, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:certificate_overrides=%s\n", strings.Join(productVariables.CertificateOverrides, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:cfi_exclude_paths=%s\n", strings.Join(productVariables.CFIExcludePaths, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:cfi_include_paths=%s\n", strings.Join(productVariables.CFIIncludePaths, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:compressed_apex=%t\n", proptools.Bool(productVariables.CompressedApex)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:default_app_certificate=%s\n", proptools.String(productVariables.DefaultAppCertificate)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_abi=%s\n", strings.Join(productVariables.DeviceAbi, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_max_page_size_supported=%s\n", proptools.String(productVariables.DeviceMaxPageSizeSupported)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_name=%s\n", proptools.String(productVariables.DeviceName)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_product=%s\n", proptools.String(productVariables.DeviceProduct)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:enable_cfi=%t\n", proptools.BoolDefault(productVariables.EnableCFI, true)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:manifest_package_name_overrides=%s\n", strings.Join(productVariables.ManifestPackageNameOverrides, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:platform_version_name=%s\n", proptools.String(productVariables.Platform_version_name)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:product_brand=%s\n", productVariables.ProductBrand))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:product_manufacturer=%s\n", productVariables.ProductManufacturer))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:target_build_variant=%s\n", targetBuildVariant))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:tidy_checks=%s\n", proptools.String(productVariables.TidyChecks)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:unbundled_build=%t\n", proptools.Bool(productVariables.Unbundled_build)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:unbundled_build_apps=%s\n", strings.Join(productVariables.Unbundled_build_apps, ",")))
+	}
+}
+
+func starlarkMapToProductVariables(in map[string]starlark.Value) (android.ProductVariables, error) {
+	result := android.ProductVariables{}
+	productVarsReflect := reflect.ValueOf(&result).Elem()
+	for i := 0; i < productVarsReflect.NumField(); i++ {
+		field := productVarsReflect.Field(i)
+		fieldType := productVarsReflect.Type().Field(i)
+		name := fieldType.Name
+		if name == "BootJars" || name == "ApexBootJars" || name == "VendorVars" ||
+			name == "VendorSnapshotModules" || name == "RecoverySnapshotModules" {
+			// These variables have more complicated types, and we don't need them right now
+			continue
+		}
+		if _, ok := in[name]; ok {
+			switch field.Type().Kind() {
+			case reflect.Bool:
+				val, err := starlark_import.Unmarshal[bool](in[name])
+				if err != nil {
+					return result, err
+				}
+				field.SetBool(val)
+			case reflect.String:
+				val, err := starlark_import.Unmarshal[string](in[name])
+				if err != nil {
+					return result, err
+				}
+				field.SetString(val)
+			case reflect.Slice:
+				if field.Type().Elem().Kind() != reflect.String {
+					return result, fmt.Errorf("slices of types other than strings are unimplemented")
+				}
+				val, err := starlark_import.UnmarshalReflect(in[name], field.Type())
+				if err != nil {
+					return result, err
+				}
+				field.Set(val)
+			case reflect.Pointer:
+				switch field.Type().Elem().Kind() {
+				case reflect.Bool:
+					val, err := starlark_import.UnmarshalNoneable[bool](in[name])
+					if err != nil {
+						return result, err
+					}
+					field.Set(reflect.ValueOf(val))
+				case reflect.String:
+					val, err := starlark_import.UnmarshalNoneable[string](in[name])
+					if err != nil {
+						return result, err
+					}
+					field.Set(reflect.ValueOf(val))
+				case reflect.Int:
+					val, err := starlark_import.UnmarshalNoneable[int](in[name])
+					if err != nil {
+						return result, err
+					}
+					field.Set(reflect.ValueOf(val))
+				default:
+					return result, fmt.Errorf("pointers of types other than strings/bools are unimplemented: %s", field.Type().Elem().Kind().String())
+				}
+			default:
+				return result, fmt.Errorf("unimplemented type: %s", field.Type().String())
+			}
+		}
+	}
 
 	return result, nil
 }
diff --git a/bp2build/bp2build_product_config_test.go b/bp2build/bp2build_product_config_test.go
new file mode 100644
index 0000000..3dd53ce
--- /dev/null
+++ b/bp2build/bp2build_product_config_test.go
@@ -0,0 +1,88 @@
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/starlark_import"
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+	"go.starlark.net/starlark"
+)
+
+func createStarlarkValue(t *testing.T, code string) starlark.Value {
+	t.Helper()
+	result, err := starlark.ExecFile(&starlark.Thread{}, "main.bzl", "x = "+code, nil)
+	if err != nil {
+		t.Error(err)
+	}
+	return result["x"]
+}
+
+func createStarlarkProductVariablesMap(t *testing.T, code string) map[string]starlark.Value {
+	t.Helper()
+	rawValue := createStarlarkValue(t, code)
+	value, err := starlark_import.Unmarshal[map[string]starlark.Value](rawValue)
+	if err != nil {
+		t.Error(err)
+	}
+	return value
+}
+
+func TestStarlarkMapToProductVariables(t *testing.T) {
+	thirty := 30
+	cases := []struct {
+		starlark string
+		result   android.ProductVariables
+	}{
+		{
+			starlark: `{"CompressedApex": True}`,
+			result:   android.ProductVariables{CompressedApex: proptools.BoolPtr(true)},
+		},
+		{
+			starlark: `{"ApexGlobalMinSdkVersionOverride": "Tiramisu"}`,
+			result:   android.ProductVariables{ApexGlobalMinSdkVersionOverride: proptools.StringPtr("Tiramisu")},
+		},
+		{
+			starlark: `{"ProductManufacturer": "Google"}`,
+			result:   android.ProductVariables{ProductManufacturer: "Google"},
+		},
+		{
+			starlark: `{"Unbundled_build_apps": ["app1", "app2"]}`,
+			result:   android.ProductVariables{Unbundled_build_apps: []string{"app1", "app2"}},
+		},
+		{
+			starlark: `{"Platform_sdk_version": 30}`,
+			result:   android.ProductVariables{Platform_sdk_version: &thirty},
+		},
+		{
+			starlark: `{"HostFakeSnapshotEnabled": True}`,
+			result:   android.ProductVariables{HostFakeSnapshotEnabled: true},
+		},
+	}
+
+	for _, testCase := range cases {
+		productVariables, err := starlarkMapToProductVariables(createStarlarkProductVariablesMap(t,
+			testCase.starlark))
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		if !reflect.DeepEqual(testCase.result, productVariables) {
+			expected, err := json.Marshal(testCase.result)
+			if err != nil {
+				t.Error(err)
+				continue
+			}
+			actual, err := json.Marshal(productVariables)
+			if err != nil {
+				t.Error(err)
+				continue
+			}
+			expectedStr := string(expected)
+			actualStr := string(actual)
+			t.Errorf("expected %q, but got %q", expectedStr, actualStr)
+		}
+	}
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 46a5bd8..09a9d04 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,405 @@
 	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
+	Data                   bazel.LabelListAttribute
+	Target_compatible_with bazel.LabelListAttribute
+
+	// attributes for the dynamically generated go_test target
+	Embed bazel.LabelListAttribute
+}
+
+type goTestProperties struct {
+	name           string
+	dir            string
+	testSrcs       []string
+	linuxTestSrcs  []string
+	darwinTestSrcs []string
+	testData       []string
+	// Name of the target that should be compiled together with the test
+	embedName string
+}
+
+// Creates a go_test target for bootstrap_go_package / blueprint_go_binary
+func generateBazelTargetsGoTest(ctx *android.Context, goModulesMap nameToGoLibraryModule, gp goTestProperties) (BazelTarget, error) {
+	ca := android.CommonAttributes{
+		Name: gp.name,
+	}
+	ga := goAttributes{
+		Srcs: goSrcLabels(ctx.Config(), gp.dir, gp.testSrcs, gp.linuxTestSrcs, gp.darwinTestSrcs),
+		Data: goSrcLabels(ctx.Config(), gp.dir, gp.testData, []string{}, []string{}),
+		Embed: bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{bazel.Label{Label: ":" + gp.embedName}},
+			),
+		),
+		Target_compatible_with: targetNotCompatibleWithAndroid(),
+	}
+
+	libTest := goBazelTarget{
+		targetName:            gp.name,
+		targetPackage:         gp.dir,
+		bazelRuleClass:        "go_test",
+		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+		bazelAttributes:       []interface{}{&ca, &ga},
+	}
+	return generateBazelTarget(ctx, libTest)
+}
+
+// TODO - b/288491147: testSrcs of certain bootstrap_go_package/blueprint_go_binary are not hermetic and depend on
+// testdata checked into the filesystem.
+// Denylist the generation of go_test targets for these Soong modules.
+// The go_library/go_binary will still be generated, since those are hermitic.
+var (
+	goTestsDenylist = []string{
+		"android-archive-zip",
+		"bazel_notice_gen",
+		"blueprint-bootstrap-bpdoc",
+		"blueprint-microfactory",
+		"blueprint-pathtools",
+		"bssl_ar",
+		"compliance_checkmetadata",
+		"compliance_checkshare",
+		"compliance_dumpgraph",
+		"compliance_dumpresolutions",
+		"compliance_listshare",
+		"compliance-module",
+		"compliancenotice_bom",
+		"compliancenotice_shippedlibs",
+		"compliance_rtrace",
+		"compliance_sbom",
+		"golang-protobuf-internal-fuzz-jsonfuzz",
+		"golang-protobuf-internal-fuzz-textfuzz",
+		"golang-protobuf-internal-fuzz-wirefuzz",
+		"htmlnotice",
+		"protoc-gen-go",
+		"rbcrun-module",
+		"spdx-tools-builder",
+		"spdx-tools-builder2v1",
+		"spdx-tools-builder2v2",
+		"spdx-tools-builder2v3",
+		"spdx-tools-idsearcher",
+		"spdx-tools-spdx-json",
+		"spdx-tools-utils",
+		"soong-ui-build",
+		"textnotice",
+		"xmlnotice",
+	}
+)
+
+func testOfGoPackageIsIncompatible(g *bootstrap.GoPackage) bool {
+	return android.InList(g.Name(), goTestsDenylist) ||
+		// Denylist tests of soong_build
+		// Theses tests have a guard that prevent usage outside a test environment
+		// The guard (`ensureTestOnly`) looks for a `-test` in os.Args, which is present in soong's gotestrunner, but missing in `b test`
+		g.IsPluginFor("soong_build") ||
+		// soong-android is a dep of soong_build
+		// This dependency is created by soong_build by listing it in its deps explicitly in Android.bp, and not via `plugin_for` in `soong-android`
+		g.Name() == "soong-android"
+}
+
+func testOfGoBinaryIsIncompatible(g *bootstrap.GoBinary) bool {
+	return android.InList(g.Name(), goTestsDenylist)
+}
+
+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},
+	}
+	retTargets := []BazelTarget{}
+	var retErrs []error
+	if libTarget, err := generateBazelTarget(ctx, lib); err == nil {
+		retTargets = append(retTargets, libTarget)
+	} else {
+		retErrs = []error{err}
+	}
+
+	// If the library contains test srcs, create an additional go_test target
+	if !testOfGoPackageIsIncompatible(g) && (len(g.TestSrcs()) > 0 || len(g.LinuxTestSrcs()) > 0 || len(g.DarwinTestSrcs()) > 0) {
+		gp := goTestProperties{
+			name:           g.Name() + "-test",
+			dir:            ctx.ModuleDir(g),
+			testSrcs:       g.TestSrcs(),
+			linuxTestSrcs:  g.LinuxTestSrcs(),
+			darwinTestSrcs: g.DarwinTestSrcs(),
+			testData:       g.TestData(),
+			embedName:      g.Name(), // embed the source go_library in the test so that its .go files are included in the compilation unit
+		}
+		if libTestTarget, err := generateBazelTargetsGoTest(ctx, goModulesMap, gp); err == nil {
+			retTargets = append(retTargets, libTestTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+	}
+
+	return retTargets, retErrs
+}
+
+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(),
+	}
+
+	retTargets := []BazelTarget{}
+	var retErrs []error
+
+	// 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)
+
+	goSource := ""
+	// If the library contains test srcs, create an additional go_test target
+	// The go_test target will embed a go_source containining the source .go files it tests
+	if !testOfGoBinaryIsIncompatible(g) && (len(g.TestSrcs()) > 0 || len(g.LinuxTestSrcs()) > 0 || len(g.DarwinTestSrcs()) > 0) {
+		// Create a go_source containing the source .go files of go_library
+		// This target will be an `embed` of the go_binary and go_test
+		goSource = g.Name() + "-source"
+		ca := android.CommonAttributes{
+			Name: goSource,
+		}
+		ga := goAttributes{
+			Srcs:                   goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
+			Deps:                   goDepLabels(transitiveDeps, goModulesMap),
+			Target_compatible_with: targetNotCompatibleWithAndroid(),
+		}
+		libTestSource := goBazelTarget{
+			targetName:            goSource,
+			targetPackage:         ctx.ModuleDir(g),
+			bazelRuleClass:        "go_source",
+			bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+			bazelAttributes:       []interface{}{&ca, &ga},
+		}
+		if libSourceTarget, err := generateBazelTarget(ctx, libTestSource); err == nil {
+			retTargets = append(retTargets, libSourceTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+
+		// Create a go_test target
+		gp := goTestProperties{
+			name:           g.Name() + "-test",
+			dir:            ctx.ModuleDir(g),
+			testSrcs:       g.TestSrcs(),
+			linuxTestSrcs:  g.LinuxTestSrcs(),
+			darwinTestSrcs: g.DarwinTestSrcs(),
+			testData:       g.TestData(),
+			// embed the go_source in the test
+			embedName: g.Name() + "-source",
+		}
+		if libTestTarget, err := generateBazelTargetsGoTest(ctx, goModulesMap, gp); err == nil {
+			retTargets = append(retTargets, libTestTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+
+	}
+
+	// Create a go_binary target
+	ga := goAttributes{
+		Deps:                   goDepLabels(transitiveDeps, goModulesMap),
+		Target_compatible_with: targetNotCompatibleWithAndroid(),
+	}
+
+	// If the binary has testSrcs, embed the common `go_source`
+	if goSource != "" {
+		ga.Embed = bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{bazel.Label{Label: ":" + goSource}},
+			),
+		)
+	} else {
+		ga.Srcs = goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs())
+	}
+
+	bin := goBazelTarget{
+		targetName:            g.Name(),
+		targetPackage:         ctx.ModuleDir(g),
+		bazelRuleClass:        "go_binary",
+		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+		bazelAttributes:       []interface{}{&ca, &ga},
+	}
+
+	if binTarget, err := generateBazelTarget(ctx, bin); err == nil {
+		retTargets = append(retTargets, binTarget)
+	} else {
+		retErrs = []error{err}
+	}
+
+	return retTargets, retErrs
+}
+
 func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
 	buildFileToTargets := make(map[string]BazelTargets)
 
@@ -262,6 +662,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 +673,7 @@
 		dirs[dir] = true
 
 		var targets []BazelTarget
+		var targetErrs []error
 
 		switch ctx.Mode() {
 		case Bp2Build:
@@ -317,7 +722,6 @@
 						return
 					}
 				}
-				var targetErrs []error
 				targets, targetErrs = generateBazelTargets(bpCtx, aModule)
 				errs = append(errs, targetErrs...)
 				for _, t := range targets {
@@ -336,6 +740,16 @@
 					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")
+				metrics.AddConvertedModule(glib, "go_library", dir)
+			} else if gbin, ok := m.(*bootstrap.GoBinary); ok {
+				targets, targetErrs = generateBazelTargetsGoBinary(bpCtx, gbin, nameToGoLibMap)
+				errs = append(errs, targetErrs...)
+				metrics.IncrementRuleClassCount("go_binary")
+				metrics.AddConvertedModule(gbin, "go_binary", dir)
 			} 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 18cb9e1..d9a7860 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -881,7 +881,7 @@
 		targets: []testBazelTarget{
 			{"cc_binary", "foo", AttrNameToString{
 				"local_includes": `["."]`,
-				"features":       `["ubsan_blocklist_foo_blocklist_txt"]`,
+				"features":       `["sanitizer_blocklist_foo_blocklist_txt"]`,
 			}},
 		},
 	})
@@ -1214,3 +1214,82 @@
 		},
 	})
 }
+
+func TestCcBinaryStatic_SystemSharedLibUsedAsDep(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_library_static system_shared_lib empty for linux_bionic variant",
+		blueprint: soongCcLibraryStaticPreamble +
+			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+
+cc_library {
+    name: "libm",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_binary {
+    name: "used_in_bionic_oses",
+    target: {
+        android: {
+            static_libs: ["libc"],
+        },
+        linux_bionic: {
+            static_libs: ["libc"],
+        },
+    },
+    include_build_directory: false,
+    static_executable: true,
+}
+
+cc_binary {
+    name: "all",
+    static_libs: ["libc"],
+    include_build_directory: false,
+    static_executable: true,
+}
+
+cc_binary {
+    name: "keep_for_empty_system_shared_libs",
+    static_libs: ["libc"],
+    system_shared_libs: [],
+    include_build_directory: false,
+    static_executable: true,
+}
+
+cc_binary {
+    name: "used_with_stubs",
+    static_libs: ["libm"],
+    include_build_directory: false,
+    static_executable: true,
+}
+
+cc_binary {
+    name: "keep_with_stubs",
+    static_libs: ["libm"],
+    system_shared_libs: [],
+    include_build_directory: false,
+    static_executable: true,
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "all", AttrNameToString{
+				"linkshared": "False",
+			}},
+			{"cc_binary", "keep_for_empty_system_shared_libs", AttrNameToString{
+				"deps":        `[":libc_bp2build_cc_library_static"]`,
+				"system_deps": `[]`,
+				"linkshared":  "False",
+			}},
+			{"cc_binary", "keep_with_stubs", AttrNameToString{
+				"linkshared":  "False",
+				"deps":        `[":libm_bp2build_cc_library_static"]`,
+				"system_deps": `[]`,
+			}},
+			{"cc_binary", "used_in_bionic_oses", AttrNameToString{
+				"linkshared": "False",
+			}},
+			{"cc_binary", "used_with_stubs", AttrNameToString{
+				"linkshared": "False",
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 1e3d72e..490cd91 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -4194,11 +4194,11 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
-				"features":       `["ubsan_blocklist_foo_blocklist_txt"]`,
+				"features":       `["sanitizer_blocklist_foo_blocklist_txt"]`,
 				"local_includes": `["."]`,
 			}),
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"features":       `["ubsan_blocklist_foo_blocklist_txt"]`,
+				"features":       `["sanitizer_blocklist_foo_blocklist_txt"]`,
 				"local_includes": `["."]`,
 			}),
 		},
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 2d61d53..ccb426f 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -1225,7 +1225,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"features":       `["ubsan_blocklist_foo_blocklist_txt"]`,
+				"features":       `["sanitizer_blocklist_foo_blocklist_txt"]`,
 				"local_includes": `["."]`,
 			}),
 		},
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 18225df..03e9cd0 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1003,6 +1003,38 @@
 	})
 }
 
+func TestCcLibraryStaticGeneratedHeadersMultipleExports(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Blueprint: soongCcLibraryStaticPreamble + `
+genrule {
+    name: "generated_hdr",
+    cmd: "nothing to see here",
+    export_include_dirs: ["foo", "bar"],
+    bazel_module: { bp2build_available: false },
+}
+
+genrule {
+    name: "export_generated_hdr",
+    cmd: "nothing to see here",
+    export_include_dirs: ["a", "b"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "foo_static",
+    generated_headers: ["generated_hdr", "export_generated_hdr"],
+    export_generated_headers: ["export_generated_hdr"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+				"deps":                `[":export_generated_hdr__header_library"]`,
+				"implementation_deps": `[":generated_hdr__header_library"]`,
+			}),
+		},
+	})
+}
+
 // generated_headers has "variant_prepend" tag. In bp2build output,
 // variant info(select) should go before general info.
 func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) {
@@ -1918,7 +1950,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
-				"features":       `["ubsan_blocklist_foo_blocklist_txt"]`,
+				"features":       `["sanitizer_blocklist_foo_blocklist_txt"]`,
 				"local_includes": `["."]`,
 			}),
 		},
diff --git a/bp2build/cc_test_conversion_test.go b/bp2build/cc_test_conversion_test.go
index 4df4d4d..3c037b4 100644
--- a/bp2build/cc_test_conversion_test.go
+++ b/bp2build/cc_test_conversion_test.go
@@ -94,7 +94,9 @@
 			simpleModuleDoNotConvertBp2build("genrule", "data_mod") +
 			simpleModuleDoNotConvertBp2build("cc_binary", "cc_bin") +
 			simpleModuleDoNotConvertBp2build("cc_library", "cc_lib") +
-			simpleModuleDoNotConvertBp2build("cc_test_library", "cc_test_lib2"),
+			simpleModuleDoNotConvertBp2build("cc_test_library", "cc_test_lib2") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_library_shared", "cc_test_lib1", AttrNameToString{}},
 			{"cc_library_static", "cc_test_lib1_bp2build_cc_library_static", AttrNameToString{}},
@@ -106,7 +108,11 @@
         ":cc_bin",
         ":cc_lib",
     ]`,
-				"deps": `[":cc_test_lib1_bp2build_cc_library_static"] + select({
+				"deps": `[
+        ":cc_test_lib1_bp2build_cc_library_static",
+        ":libgtest_main",
+        ":libgtest",
+    ] + select({
         "//build/bazel/platforms/os:darwin": [":hostlib"],
         "//build/bazel/platforms/os:linux_bionic": [":hostlib"],
         "//build/bazel/platforms/os:linux_glibc": [":hostlib"],
@@ -114,8 +120,6 @@
         "//build/bazel/platforms/os:windows": [":hostlib"],
         "//conditions:default": [],
     })`,
-				"gtest":          "True",
-				"isolated":       "True",
 				"local_includes": `["."]`,
 				"dynamic_deps": `[":cc_test_lib2"] + select({
         "//build/bazel/platforms/os:android": [":foolib"],
@@ -152,7 +156,6 @@
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
 				"gtest":          "False",
-				"isolated":       "False",
 				"local_includes": `["."]`,
 				"srcs":           `["test.cpp"]`,
 			},
@@ -171,14 +174,17 @@
     srcs: ["test.cpp"],
     test_options: { tags: ["no-remote"] },
 }
-`,
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
 				"tags":           `["no-remote"]`,
 				"local_includes": `["."]`,
 				"srcs":           `["test.cpp"]`,
-				"gtest":          "True",
-				"isolated":       "True",
+				"deps": `[
+        ":libgtest_main",
+        ":libgtest",
+    ]`,
 			},
 			},
 		},
@@ -197,15 +203,18 @@
 	srcs: ["test.cpp"],
 	test_config: "test_config.xml",
 }
-`,
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
-				"gtest":                  "True",
-				"isolated":               "True",
 				"local_includes":         `["."]`,
 				"srcs":                   `["test.cpp"]`,
 				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 				"test_config":            `"test_config.xml"`,
+				"deps": `[
+        ":libgtest_main",
+        ":libgtest",
+    ]`,
 			},
 			},
 		},
@@ -223,15 +232,18 @@
 	name: "mytest",
 	srcs: ["test.cpp"],
 }
-`,
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
-				"gtest":                  "True",
-				"isolated":               "True",
 				"local_includes":         `["."]`,
 				"srcs":                   `["test.cpp"]`,
 				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 				"test_config":            `"AndroidTest.xml"`,
+				"deps": `[
+        ":libgtest_main",
+        ":libgtest",
+    ]`,
 			},
 			},
 		},
@@ -250,13 +262,13 @@
 	srcs: ["test.cpp"],
 	test_config_template: "test_config_template.xml",
 	auto_gen_config: true,
+	isolated: true,
 }
-`,
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_isolated_main") +
+			simpleModuleDoNotConvertBp2build("cc_library", "liblog"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
 				"auto_generate_test_config": "True",
-				"gtest":                     "True",
-				"isolated":                  "True",
 				"local_includes":            `["."]`,
 				"srcs":                      `["test.cpp"]`,
 				"target_compatible_with":    `["//build/bazel/platforms/os:android"]`,
@@ -266,6 +278,95 @@
     ]`,
 				"template_install_base": `"/data/local/tmp"`,
 				"template_test_config":  `"test_config_template.xml"`,
+				"deps":                  `[":libgtest_isolated_main"]`,
+				"dynamic_deps":          `[":liblog"]`,
+			},
+			},
+		},
+	})
+}
+
+func TestCcTest_WithExplicitGTestDepInAndroidBp(t *testing.T) {
+	runCcTestTestCase(t, ccTestBp2buildTestCase{
+		description: "cc test that lists libgtest in Android.bp should not have dups of libgtest in BUILD file",
+		blueprint: `
+cc_test {
+	name: "mytest",
+	srcs: ["test.cpp"],
+	static_libs: ["libgtest"],
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
+		targets: []testBazelTarget{
+			{"cc_test", "mytest", AttrNameToString{
+				"local_includes":         `["."]`,
+				"srcs":                   `["test.cpp"]`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+				"deps": `[
+        ":libgtest",
+        ":libgtest_main",
+    ]`,
+			},
+			},
+		},
+	})
+
+}
+
+func TestCcTest_WithIsolatedTurnedOn(t *testing.T) {
+	runCcTestTestCase(t, ccTestBp2buildTestCase{
+		description: "cc test that sets `isolated: true` should run with ligtest_isolated_main instead of libgtest_main",
+		blueprint: `
+cc_test {
+	name: "mytest",
+	srcs: ["test.cpp"],
+	isolated: true,
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_isolated_main") +
+			simpleModuleDoNotConvertBp2build("cc_library", "liblog"),
+		targets: []testBazelTarget{
+			{"cc_test", "mytest", AttrNameToString{
+				"local_includes":         `["."]`,
+				"srcs":                   `["test.cpp"]`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+				"deps":                   `[":libgtest_isolated_main"]`,
+				"dynamic_deps":           `[":liblog"]`,
+			},
+			},
+		},
+	})
+
+}
+
+func TestCcTest_GtestExplicitlySpecifiedInAndroidBp(t *testing.T) {
+	runCcTestTestCase(t, ccTestBp2buildTestCase{
+		description: "If `gtest` is explicit in Android.bp, it should be explicit in BUILD files as well",
+		blueprint: `
+cc_test {
+	name: "mytest_with_gtest",
+	gtest: true,
+}
+cc_test {
+	name: "mytest_with_no_gtest",
+	gtest: false,
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
+		targets: []testBazelTarget{
+			{"cc_test", "mytest_with_gtest", AttrNameToString{
+				"local_includes": `["."]`,
+				"deps": `[
+        ":libgtest_main",
+        ":libgtest",
+    ]`,
+				"gtest":                  "True",
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			},
+			},
+			{"cc_test", "mytest_with_no_gtest", AttrNameToString{
+				"local_includes":         `["."]`,
+				"gtest":                  "False",
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 			},
 			},
 		},
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 5cf4fb2..5a73969 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -695,3 +696,79 @@
 			})
 	})
 }
+
+func TestGenruleWithExportIncludeDirs(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
+		},
+		{
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
+		},
+	}
+
+	dir := "baz"
+
+	bp := `%s {
+    name: "foo",
+    out: ["foo.out.h"],
+    srcs: ["foo.in"],
+    cmd: "cp $(in) $(out)",
+    export_include_dirs: ["foo", "bar", "."],
+    bazel_module: { bp2build_available: true },
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := AttrNameToString{
+			"cmd":  `"cp $(SRCS) $(OUTS)"`,
+			"outs": `["foo.out.h"]`,
+			"srcs": `["foo.in"]`,
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+			makeBazelTargetHostOrDevice("cc_library_headers", "foo__header_library", AttrNameToString{
+				"hdrs": `[":foo"]`,
+				"export_includes": `[
+        "foo",
+        "baz/foo",
+        "bar",
+        "baz/bar",
+        ".",
+        "baz",
+    ]`,
+			},
+				tc.hod),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				Bp2buildTestCase{
+					ModuleTypeUnderTest:        tc.moduleType,
+					ModuleTypeUnderTestFactory: tc.factory,
+					Filesystem: map[string]string{
+						filepath.Join(dir, "Android.bp"): fmt.Sprintf(bp, tc.moduleType),
+					},
+					Dir:                  dir,
+					ExpectedBazelTargets: expectedBazelTargets,
+				})
+		})
+	}
+}
diff --git a/bp2build/go_conversion_test.go b/bp2build/go_conversion_test.go
new file mode 100644
index 0000000..2387641
--- /dev/null
+++ b/bp2build/go_conversion_test.go
@@ -0,0 +1,208 @@
+// 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",
+		],
+		testSrcs: [
+			"foo_linux_test.go",
+		],
+	},
+	darwin: {
+		srcs: [
+			"foo_darwin.go",
+		],
+		testSrcs: [
+			"foo_darwin_test.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,
+		),
+			makeBazelTargetHostOrDevice("go_test", "foo-test",
+				AttrNameToString{
+					"embed": `[":foo"]`,
+					"srcs": `[
+        "foo1_test.go",
+        "foo2_test.go",
+    ] + select({
+        "//build/bazel/platforms/os:darwin": ["foo_darwin_test.go"],
+        "//build/bazel/platforms/os:linux_glibc": ["foo_linux_test.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 TestConvertGoBinaryWithTestSrcs(t *testing.T) {
+	bp := `
+blueprint_go_binary {
+	name: "foo",
+	srcs: ["main.go"],
+	testSrcs: ["main_test.go"],
+}
+`
+	t.Parallel()
+	runGoTests(t, Bp2buildTestCase{
+		Description: "Convert blueprint_go_binary with testSrcs",
+		Blueprint:   bp,
+		ExpectedBazelTargets: []string{
+			makeBazelTargetHostOrDevice("go_binary", "foo",
+				AttrNameToString{
+					"deps":  `[]`,
+					"embed": `[":foo-source"]`,
+				},
+				android.HostSupported,
+			),
+			makeBazelTargetHostOrDevice("go_source", "foo-source",
+				AttrNameToString{
+					"deps": `[]`,
+					"srcs": `["main.go"]`,
+				},
+				android.HostSupported,
+			),
+			makeBazelTargetHostOrDevice("go_test", "foo-test",
+				AttrNameToString{
+					"embed": `[":foo-source"]`,
+					"srcs":  `["main_test.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/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index fd92e95..c501a7b 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -183,8 +183,8 @@
 	})
 }
 
-func TestJavaLibraryErrorproneJavacflagsEnabledManually(t *testing.T) {
-	runJavaLibraryTestCase(t, Bp2buildTestCase{
+func TestJavaLibraryErrorproneEnabledManually(t *testing.T) {
+	runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
 		Blueprint: `java_library {
     name: "java-lib-1",
     srcs: ["a.java"],
@@ -192,7 +192,13 @@
     errorprone: {
         enabled: true,
         javacflags: ["-Xep:SpeedLimit:OFF"],
+        extra_check_modules: ["plugin2"],
     },
+}
+java_plugin {
+    name: "plugin2",
+    srcs: ["a.java"],
+    bazel_module: { bp2build_available: false },
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
@@ -200,10 +206,14 @@
         "-Xsuper-fast",
         "-Xep:SpeedLimit:OFF",
     ]`,
-				"srcs": `["a.java"]`,
+				"plugins":                 `[":plugin2"]`,
+				"srcs":                    `["a.java"]`,
+				"errorprone_force_enable": `True`,
 			}),
 			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
 		},
+	}, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("java_plugin", java.PluginFactory)
 	})
 }
 
@@ -227,21 +237,23 @@
 	})
 }
 
-func TestJavaLibraryErrorproneJavacflagsErrorproneDisabledManually(t *testing.T) {
+func TestJavaLibraryErrorproneDisabledManually(t *testing.T) {
 	runJavaLibraryTestCase(t, Bp2buildTestCase{
 		Blueprint: `java_library {
     name: "java-lib-1",
     srcs: ["a.java"],
     javacflags: ["-Xsuper-fast"],
     errorprone: {
-		enabled: false,
-        javacflags: ["-Xep:SpeedLimit:OFF"],
+    enabled: false,
     },
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
-				"javacopts": `["-Xsuper-fast"]`,
-				"srcs":      `["a.java"]`,
+				"javacopts": `[
+        "-Xsuper-fast",
+        "-XepDisableAllChecks",
+    ]`,
+				"srcs": `["a.java"]`,
 			}),
 			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
 		},
diff --git a/bp2build/java_test_host_conversion_test.go b/bp2build/java_test_host_conversion_test.go
index f411ffb..f41345e 100644
--- a/bp2build/java_test_host_conversion_test.go
+++ b/bp2build/java_test_host_conversion_test.go
@@ -71,6 +71,11 @@
 			}),
 			MakeBazelTarget("java_test", "java_test_host-1", AttrNameToString{
 				"runtime_deps": `[":java_test_host-1_lib"]`,
+				"deps": `[
+        ":lib_a-neverlink",
+        ":static_libs_a",
+    ]`,
+				"srcs": `["a.java"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
@@ -128,6 +133,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_test", "java_test_host-1", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
 				"runtime_deps": `[":java_test_host-1_lib"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index 813773d..143597d 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -1406,3 +1406,111 @@
     target_compatible_with = ["//build/bazel/platforms/os:android"],
 )`}})
 }
+
+// If we have
+// A. a soong_config_module_type with target.android_<arch>.* in properties
+// B. a module that uses this module type but does not set target.android_<arch>.* via soong config vars
+// Then we should not panic
+func TestPanicsIfSoongConfigModuleTypeHasArchSpecificProperties(t *testing.T) {
+	commonBp := `
+soong_config_bool_variable {
+	name: "my_bool_variable",
+}
+soong_config_module_type {
+	name: "special_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "my_namespace",
+	bool_variables: ["my_bool_variable"],
+	properties: [
+		"cflags",
+		"target.android_arm64.shared_libs",
+	],
+}
+cc_binary {
+	name: "my_binary",
+	defaults: ["my_special_cc_defaults"],
+}
+`
+	testCases := []struct {
+		desc            string
+		additionalBp    string
+		isPanicExpected bool
+	}{
+		{
+			desc: "target.android_arm64 is not set, bp2build should not panic",
+			additionalBp: `
+special_cc_defaults {
+	name: "my_special_cc_defaults",
+	soong_config_variables: {
+		my_bool_variable: {
+			cflags: ["-DFOO"],
+			conditions_default: {
+				cflags: ["-DBAR"],
+			}
+		}
+	},
+}
+			`,
+			isPanicExpected: false,
+		},
+		{
+			desc: "target.android_arm64 is set using the bool soong config var, bp2build should panic",
+			additionalBp: `
+special_cc_defaults {
+	name: "my_special_cc_defaults",
+	soong_config_variables: {
+		my_bool_variable: {
+			cflags: ["-DFOO"],
+			target: {
+				android_arm64: {
+					shared_libs: ["liblog"],
+				},
+			},
+			conditions_default: {
+				cflags: ["-DBAR"],
+			}
+		}
+	},
+}
+			`,
+			isPanicExpected: true,
+		},
+		{
+			desc: "target.android_arm64 is set using conditions_default for the bool soong config var, bp2build should panic",
+			additionalBp: `
+special_cc_defaults {
+	name: "my_special_cc_defaults",
+	soong_config_variables: {
+		my_bool_variable: {
+			cflags: ["-DFOO"],
+			conditions_default: {
+				cflags: ["-DBAR"],
+				target: {
+					android_arm64: {
+						shared_libs: ["liblog"],
+					},
+				},
+			}
+		}
+	},
+}
+			`,
+			isPanicExpected: true,
+		},
+	}
+	for _, tc := range testCases {
+		bp2buildTestCase := Bp2buildTestCase{
+			Description:                tc.desc,
+			ModuleTypeUnderTest:        "cc_binary",
+			ModuleTypeUnderTestFactory: cc.BinaryFactory,
+			Blueprint:                  commonBp + tc.additionalBp,
+			// Check in `foo` dir so that we can check whether it panics or not and not trip over an empty `ExpectedBazelTargets`
+			Dir:                  "foo",
+			ExpectedBazelTargets: []string{},
+		}
+		if tc.isPanicExpected {
+			bp2buildTestCase.ExpectedErr = fmt.Errorf("TODO: support other target types in soong config variable structs: Android_arm64")
+		}
+		runSoongConfigModuleTypeTest(t, bp2buildTestCase)
+	}
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index dddcd3f..4c42048 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -44,7 +44,7 @@
   # xref_rust
 )
 
-build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k "${kzip_targets[@]}"
+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}")
@@ -71,7 +71,7 @@
 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
 declare -r allkzip="$KZIP_NAME.kzip"
diff --git a/cc/Android.bp b/cc/Android.bp
index f49dc1a..e88ea03 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -32,6 +32,7 @@
         "check.go",
         "coverage.go",
         "gen.go",
+        "generated_cc_library.go",
         "image.go",
         "linkable.go",
         "lto.go",
diff --git a/cc/afdo.go b/cc/afdo.go
index bc7cd52..23d196d 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -131,6 +131,10 @@
 		return
 	}
 
+	if !c.afdo.afdoEnabled() {
+		return
+	}
+
 	ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
 		if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
 			info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
diff --git a/cc/api_level.go b/cc/api_level.go
index a5571f3..69a0d3a 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -31,7 +31,11 @@
 	case android.Arm64, android.X86_64:
 		return android.FirstLp64Version
 	case android.Riscv64:
-		return android.FutureApiLevel
+		apiLevel, err := android.ApiLevelFromUser(ctx, "VanillaIceCream")
+		if err != nil {
+			panic(err)
+		}
+		return apiLevel
 	default:
 		panic(fmt.Errorf("Unknown arch %q", arch))
 	}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 5459595..9e485d6 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -22,6 +22,7 @@
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/cc/config"
+	"android/soong/genrule"
 
 	"github.com/google/blueprint"
 
@@ -43,6 +44,12 @@
 
 	rScriptSrcPartition = "renderScript"
 
+	xsdSrcPartition = "xsd"
+
+	genrulePartition = "genrule"
+
+	hdrPartition = "hdr"
+
 	stubsSuffix = "_stub_libs_current"
 )
 
@@ -155,6 +162,7 @@
 		lSrcPartition:       bazel.LabelPartition{Extensions: []string{".l"}},
 		llSrcPartition:      bazel.LabelPartition{Extensions: []string{".ll"}},
 		rScriptSrcPartition: bazel.LabelPartition{Extensions: []string{".fs", ".rscript"}},
+		xsdSrcPartition:     bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(xsdConfigCppTarget)},
 		// 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},
@@ -165,6 +173,15 @@
 	return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
 }
 
+func partitionHeaders(ctx android.BazelConversionPathContext, hdrs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
+	labels := bazel.LabelPartitions{
+		xsdSrcPartition:  bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(xsdConfigCppTarget)},
+		genrulePartition: bazel.LabelPartition{LabelMapper: genrule.GenruleCcHeaderLabelMapper},
+		hdrPartition:     bazel.LabelPartition{Keep_remainder: true},
+	}
+	return bazel.PartitionLabelListAttribute(ctx, &hdrs, labels)
+}
+
 // bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
 func bp2BuildParseLibProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
@@ -403,7 +420,12 @@
 	srcs     bazel.LabelListAttribute
 
 	// xsd config sources
-	xsdInSrcs bazel.StringListAttribute
+	xsdSrcs       bazel.LabelListAttribute
+	exportXsdSrcs bazel.LabelListAttribute
+
+	// genrule headers
+	genruleHeaders       bazel.LabelListAttribute
+	exportGenruleHeaders bazel.LabelListAttribute
 
 	// Lex sources and options
 	lSrcs   bazel.LabelListAttribute
@@ -494,14 +516,11 @@
 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.
-	srcsList, xsdList, ok := parseSrcs(ctx, props)
+	srcsList, 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 {
@@ -568,9 +587,11 @@
 	}
 }
 
-func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs bazel.LabelListAttribute) {
+func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs, exportHdrs bazel.LabelListAttribute) {
 	ca.srcs.ResolveExcludes()
 	partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
+	partitionedImplHdrs := partitionHeaders(ctx, implementationHdrs)
+	partitionedHdrs := partitionHeaders(ctx, exportHdrs)
 
 	ca.protoSrcs = partitionedSrcs[protoSrcPartition]
 	ca.aidlSrcs = partitionedSrcs[aidlSrcPartition]
@@ -580,10 +601,22 @@
 		if lla.IsEmpty() {
 			continue
 		}
-		lla.Append(implementationHdrs)
+		lla.Append(partitionedImplHdrs[hdrPartition])
 		partitionedSrcs[p] = lla
 	}
 
+	ca.hdrs = partitionedHdrs[hdrPartition]
+
+	ca.includesFromHeaders(ctx, partitionedImplHdrs[hdrPartition], partitionedHdrs[hdrPartition])
+
+	xsdSrcs := bazel.SubtractBazelLabelListAttribute(partitionedSrcs[xsdSrcPartition], partitionedHdrs[xsdSrcPartition])
+	xsdSrcs.Append(partitionedImplHdrs[xsdSrcPartition])
+	ca.exportXsdSrcs = partitionedHdrs[xsdSrcPartition]
+	ca.xsdSrcs = bazel.FirstUniqueBazelLabelListAttribute(xsdSrcs)
+
+	ca.genruleHeaders = partitionedImplHdrs[genrulePartition]
+	ca.exportGenruleHeaders = partitionedHdrs[genrulePartition]
+
 	ca.srcs = partitionedSrcs[cppSrcPartition]
 	ca.cSrcs = partitionedSrcs[cSrcPartition]
 	ca.asSrcs = partitionedSrcs[asSrcPartition]
@@ -604,11 +637,11 @@
 }
 
 // Parse srcs from an arch or OS's props value.
-func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, []string, bool) {
+func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, 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, xsd := android.PartitionXsdSrcs(ctx, props.Generated_sources)
+	genSrcs := 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
@@ -620,7 +653,7 @@
 		anySrcs = true
 	}
 
-	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), xsd, anySrcs
+	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
 }
 
 func bp2buildStdVal(std *string, prefix string, useGnu bool) *string {
@@ -667,8 +700,43 @@
 	return split[0][2:], true
 }
 
-// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList>
-func includesFromLabelList(labelList bazel.LabelList) (relative, absolute []string) {
+// includesFromHeaders gets the include directories needed from generated headers
+func (ca *compilerAttributes) includesFromHeaders(ctx android.BazelConversionPathContext, implHdrs, hdrs bazel.LabelListAttribute) {
+	local, absolute := includesFromLabelListAttribute(implHdrs, ca.localIncludes, ca.absoluteIncludes)
+	localExport, absoluteExport := includesFromLabelListAttribute(hdrs, ca.includes.Includes, ca.includes.AbsoluteIncludes)
+
+	ca.localIncludes = local
+	ca.absoluteIncludes = absolute
+
+	ca.includes.Includes = localExport
+	ca.includes.AbsoluteIncludes = absoluteExport
+}
+
+// includesFromLabelList extracts the packages from a LabelListAttribute that should be includes and
+// combines them with existing local/absolute includes.
+func includesFromLabelListAttribute(attr bazel.LabelListAttribute, existingLocal, existingAbsolute bazel.StringListAttribute) (bazel.StringListAttribute, bazel.StringListAttribute) {
+	localAttr := existingLocal.Clone()
+	absoluteAttr := existingAbsolute.Clone()
+	if !attr.Value.IsEmpty() {
+		l, a := includesFromLabelList(attr.Value, existingLocal.Value, existingAbsolute.Value)
+		localAttr.SetSelectValue(bazel.NoConfigAxis, "", l)
+		absoluteAttr.SetSelectValue(bazel.NoConfigAxis, "", a)
+	}
+	for axis, configToLabels := range attr.ConfigurableValues {
+		for c, labels := range configToLabels {
+			local := existingLocal.SelectValue(axis, c)
+			absolute := existingAbsolute.SelectValue(axis, c)
+			l, a := includesFromLabelList(labels, local, absolute)
+			localAttr.SetSelectValue(axis, c, l)
+			absoluteAttr.SetSelectValue(axis, c, a)
+		}
+	}
+	return *localAttr, *absoluteAttr
+}
+
+// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList.
+func includesFromLabelList(labelList bazel.LabelList, existingRel, existingAbs []string) ([]string, []string) {
+	var relative, absolute []string
 	for _, hdr := range labelList.Includes {
 		if pkg, hasPkg := packageFromLabel(hdr.Label); hasPkg {
 			absolute = append(absolute, pkg)
@@ -676,6 +744,12 @@
 			relative = append(relative, pkg)
 		}
 	}
+	if len(relative)+len(existingRel) != 0 {
+		relative = android.FirstUniqueStrings(append(append([]string{}, existingRel...), relative...))
+	}
+	if len(absolute)+len(existingAbs) != 0 {
+		absolute = android.FirstUniqueStrings(append(append([]string{}, existingAbs...), absolute...))
+	}
 	return relative, absolute
 }
 
@@ -740,8 +814,6 @@
 	archVariantLinkerProps := module.GetArchVariantProperties(ctx, &BaseLinkerProperties{})
 	archVariantLibraryProperties := module.GetArchVariantProperties(ctx, &LibraryProperties{})
 
-	var implementationHdrs bazel.LabelListAttribute
-
 	axisToConfigs := map[bazel.ConfigurationAxis]map[string]bool{}
 	allAxesAndConfigs := func(cp android.ConfigurationAxisToArchVariantProperties) {
 		for axis, configMap := range cp {
@@ -761,6 +833,7 @@
 	linkerAttrs := linkerAttributes{}
 
 	var aidlLibs bazel.LabelList
+	var implementationHdrs, exportHdrs bazel.LabelListAttribute
 
 	// Iterate through these axes in a deterministic order. This is required
 	// because processing certain dependencies may result in concatenating
@@ -770,9 +843,9 @@
 	for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
 		configs := axisToConfigs[axis]
 		for cfg := range configs {
-			var allHdrs, allHdrsXsd []string
+			var allHdrs []string
 			if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
-				allHdrs, allHdrsXsd = android.PartitionXsdSrcs(ctx, baseCompilerProps.Generated_headers)
+				allHdrs = baseCompilerProps.Generated_headers
 
 				if baseCompilerProps.Lex != nil {
 					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
@@ -786,36 +859,17 @@
 				aidlLibs.Append(android.BazelLabelForModuleDeps(ctx, baseCompilerProps.Aidl.Libs))
 			}
 
-			var exportHdrs, exportHdrsXsd []string
+			var exportedHdrs []string
 
 			if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
-				exportHdrs, exportHdrsXsd = android.PartitionXsdSrcs(ctx, baseLinkerProps.Export_generated_headers)
+				exportedHdrs = 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)
+			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportedHdrs, android.BazelLabelForModuleDeps)
 
 			implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
-			compilerAttrs.hdrs.SetSelectValue(axis, cfg, headers.export)
-
-			exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
-			compilerAttrs.includes.Includes.SetSelectValue(axis, cfg, exportIncludes)
-			compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, cfg, exportAbsoluteIncludes)
-
-			includes, absoluteIncludes := includesFromLabelList(headers.implementation)
-			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, cfg)
-			currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
-
-			compilerAttrs.absoluteIncludes.SetSelectValue(axis, cfg, currAbsoluteIncludes)
-
-			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, cfg)
-			currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
-
-			compilerAttrs.localIncludes.SetSelectValue(axis, cfg, currIncludes)
+			exportHdrs.SetSelectValue(axis, cfg, headers.export)
 
 			if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
 				if axis == bazel.NoConfigAxis {
@@ -835,14 +889,6 @@
 				}
 			}
 
-			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)
-			}
 		}
 	}
 
@@ -860,11 +906,17 @@
 	(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
 	(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
 
-	(&compilerAttrs).finalize(ctx, implementationHdrs)
+	(&compilerAttrs).finalize(ctx, implementationHdrs, exportHdrs)
 	(&linkerAttrs).finalize(ctx)
 
 	(&compilerAttrs.srcs).Add(bp2BuildYasm(ctx, module, compilerAttrs))
 
+	(&linkerAttrs).deps.Append(compilerAttrs.exportGenruleHeaders)
+	(&linkerAttrs).implementationDeps.Append(compilerAttrs.genruleHeaders)
+
+	(&linkerAttrs).wholeArchiveDeps.Append(compilerAttrs.exportXsdSrcs)
+	(&linkerAttrs).implementationWholeArchiveDeps.Append(compilerAttrs.xsdSrcs)
+
 	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
 
 	// bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
@@ -1139,6 +1191,7 @@
 	wholeArchiveDeps                 bazel.LabelListAttribute
 	implementationWholeArchiveDeps   bazel.LabelListAttribute
 	systemDynamicDeps                bazel.LabelListAttribute
+	usedSystemDynamicDepAsStaticDep  map[string]bool
 	usedSystemDynamicDepAsDynamicDep map[string]bool
 
 	useVersionLib                 bazel.BoolAttribute
@@ -1201,6 +1254,18 @@
 	// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
 	la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
 
+	if isBinary && module.StaticExecutable() {
+		usedSystemStatic := android.FilterListPred(staticLibs, func(s string) bool {
+			return android.InList(s, soongSystemSharedLibs) && !android.InList(s, props.Exclude_static_libs)
+		})
+
+		for _, el := range usedSystemStatic {
+			if la.usedSystemDynamicDepAsStaticDep == nil {
+				la.usedSystemDynamicDepAsStaticDep = map[string]bool{}
+			}
+			la.usedSystemDynamicDepAsStaticDep[el] = true
+		}
+	}
 	staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(
 		ctx,
 		!isBinary,
@@ -1233,6 +1298,7 @@
 	usedSystem := android.FilterListPred(sharedLibs, func(s string) bool {
 		return android.InList(s, soongSystemSharedLibs) && !android.InList(s, excludeSharedLibs)
 	})
+
 	for _, el := range usedSystem {
 		if la.usedSystemDynamicDepAsDynamicDep == nil {
 			la.usedSystemDynamicDepAsDynamicDep = map[string]bool{}
@@ -1625,6 +1691,15 @@
 		}
 		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidPlatform, bazel.MakeLabelList(stubsToRemove))
 	}
+	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsStaticDep) > 0 {
+		toRemove := bazelLabelForStaticDeps(ctx, android.SortedKeys(la.usedSystemDynamicDepAsStaticDep))
+		la.deps.Exclude(bazel.NoConfigAxis, "", toRemove)
+		la.deps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
+		la.deps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+		la.implementationDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
+		la.implementationDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
+		la.implementationDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+	}
 
 	la.deps.ResolveExcludes()
 	la.implementationDeps.ResolveExcludes()
@@ -1725,16 +1800,8 @@
 	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 xsdConfigCppTarget(xsd android.XsdConfigBp2buildTargets) string {
+	return xsd.CppBp2buildTargetName()
 }
 
 func bazelLabelForWholeDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
@@ -1819,7 +1886,7 @@
 			if blocklist != nil {
 				// Format the blocklist name to be used in a feature name
 				blocklistFeatureSuffix := strings.Replace(strings.ToLower(*blocklist), ".", "_", -1)
-				features = append(features, "ubsan_blocklist_"+blocklistFeatureSuffix)
+				features = append(features, "sanitizer_blocklist_"+blocklistFeatureSuffix)
 			}
 			if sanitizerProps.Sanitize.Cfi != nil && !proptools.Bool(sanitizerProps.Sanitize.Cfi) {
 				features = append(features, "-android_cfi")
diff --git a/cc/cc.go b/cc/cc.go
index 6087970..be67286 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -42,6 +42,7 @@
 func init() {
 	RegisterCCBuildComponents(android.InitRegistrationContext)
 
+	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/cc/config")
 }
 
@@ -568,6 +569,24 @@
 	props() []interface{}
 }
 
+// Information returned from Generator about the source code it's generating
+type GeneratedSource struct {
+	IncludeDirs    android.Paths
+	Sources        android.Paths
+	Headers        android.Paths
+	ReexportedDirs android.Paths
+}
+
+// generator allows injection of generated code
+type Generator interface {
+	GeneratorProps() []interface{}
+	GeneratorInit(ctx BaseModuleContext)
+	GeneratorDeps(ctx DepsContext, deps Deps) Deps
+	GeneratorFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags
+	GeneratorSources(ctx ModuleContext) GeneratedSource
+	GeneratorBuildActions(ctx ModuleContext, flags Flags, deps PathDeps)
+}
+
 // compiler is the interface for a compiler helper object. Different module decorators may implement
 // this helper differently.
 type compiler interface {
@@ -850,6 +869,7 @@
 	// type-specific logic. These members may reference different objects or the same object.
 	// Functions of these decorators will be invoked to initialize and register type-specific
 	// build statements.
+	generators   []Generator
 	compiler     compiler
 	linker       linker
 	installer    installer
@@ -1079,6 +1099,10 @@
 	return false
 }
 
+func (c *Module) RlibStd() bool {
+	panic(fmt.Errorf("RlibStd called on non-Rust module: %q", c.BaseModuleName()))
+}
+
 func (c *Module) RustLibraryInterface() bool {
 	return false
 }
@@ -1196,6 +1220,9 @@
 
 func (c *Module) Init() android.Module {
 	c.AddProperties(&c.Properties, &c.VendorProperties)
+	for _, generator := range c.generators {
+		c.AddProperties(generator.GeneratorProps()...)
+	}
 	if c.compiler != nil {
 		c.AddProperties(c.compiler.compilerProps()...)
 	}
@@ -1367,7 +1394,7 @@
 
 func (c *Module) isCfi() bool {
 	if sanitize := c.sanitize; sanitize != nil {
-		return Bool(sanitize.Properties.Sanitize.Cfi)
+		return Bool(sanitize.Properties.SanitizeMutated.Cfi)
 	}
 	return false
 }
@@ -2144,6 +2171,25 @@
 		return
 	}
 
+	for _, generator := range c.generators {
+		gen := generator.GeneratorSources(ctx)
+		deps.IncludeDirs = append(deps.IncludeDirs, gen.IncludeDirs...)
+		deps.ReexportedDirs = append(deps.ReexportedDirs, gen.ReexportedDirs...)
+		deps.GeneratedDeps = append(deps.GeneratedDeps, gen.Headers...)
+		deps.ReexportedGeneratedHeaders = append(deps.ReexportedGeneratedHeaders, gen.Headers...)
+		deps.ReexportedDeps = append(deps.ReexportedDeps, gen.Headers...)
+		if len(deps.Objs.objFiles) == 0 {
+			// If we are reusuing object files (which happens when we're a shared library and we're
+			// reusing our static variant's object files), then skip adding the actual source files,
+			// because we already have the object for it.
+			deps.GeneratedSources = append(deps.GeneratedSources, gen.Sources...)
+		}
+	}
+
+	if ctx.Failed() {
+		return
+	}
+
 	if c.stubLibraryMultipleApexViolation(actx) {
 		actx.PropertyErrorf("apex_available",
 			"Stub libraries should have a single apex_available (test apexes excluded). Got %v", c.ApexAvailable())
@@ -2158,6 +2204,9 @@
 		Toolchain: c.toolchain(ctx),
 		EmitXrefs: ctx.Config().EmitXrefRules(),
 	}
+	for _, generator := range c.generators {
+		flags = generator.GeneratorFlags(ctx, flags, deps)
+	}
 	if c.compiler != nil {
 		flags = c.compiler.compilerFlags(ctx, flags, deps)
 	}
@@ -2215,6 +2264,10 @@
 
 	flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.Local.AsFlags)
 
+	for _, generator := range c.generators {
+		generator.GeneratorBuildActions(ctx, flags, deps)
+	}
+
 	var objs Objects
 	if c.compiler != nil {
 		objs = c.compiler.compile(ctx, flags, deps)
@@ -2302,6 +2355,9 @@
 }
 
 func (c *Module) begin(ctx BaseModuleContext) {
+	for _, generator := range c.generators {
+		generator.GeneratorInit(ctx)
+	}
 	if c.compiler != nil {
 		c.compiler.compilerInit(ctx)
 	}
@@ -2337,6 +2393,9 @@
 func (c *Module) deps(ctx DepsContext) Deps {
 	deps := Deps{}
 
+	for _, generator := range c.generators {
+		deps = generator.GeneratorDeps(ctx, deps)
+	}
 	if c.compiler != nil {
 		deps = c.compiler.compilerDeps(ctx, deps)
 	}
@@ -2945,20 +3004,20 @@
 			ctx.ModuleErrorf("links %q built against newer API version %q",
 				ctx.OtherModuleName(to.Module()), "current")
 		} else {
-			fromApi, err := strconv.Atoi(from.SdkVersion())
+			fromApi, err := android.ApiLevelFromUserWithConfig(ctx.Config(), from.SdkVersion())
 			if err != nil {
 				ctx.PropertyErrorf("sdk_version",
-					"Invalid sdk_version value (must be int or current): %q",
+					"Invalid sdk_version value (must be int, preview or current): %q",
 					from.SdkVersion())
 			}
-			toApi, err := strconv.Atoi(to.SdkVersion())
+			toApi, err := android.ApiLevelFromUserWithConfig(ctx.Config(), to.SdkVersion())
 			if err != nil {
 				ctx.PropertyErrorf("sdk_version",
-					"Invalid sdk_version value (must be int or current): %q",
+					"Invalid sdk_version value (must be int, preview or current): %q",
 					to.SdkVersion())
 			}
 
-			if toApi > fromApi {
+			if toApi.GreaterThan(fromApi) {
 				ctx.ModuleErrorf("links %q built against newer API version %q",
 					ctx.OtherModuleName(to.Module()), to.SdkVersion())
 			}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7534db2..d95ed3f 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3261,7 +3261,7 @@
 							},
 						},
 						LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
-							"//:test": cquery.CcUnstrippedInfo{
+							"//:test__tf_internal": cquery.CcUnstrippedInfo{
 								CcAndroidMkInfo: tc.androidMkInfo,
 							},
 							"//:binary": cquery.CcUnstrippedInfo{
diff --git a/cc/compiler.go b/cc/compiler.go
index 16f4a6e..5bed8a7 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -189,13 +189,13 @@
 			// build the recovery variant of the C/C++ module.
 			Exclude_generated_sources []string
 		}
-		Vendor_ramdisk struct {
+		Ramdisk, Vendor_ramdisk struct {
 			// list of source files that should not be used to
-			// build the vendor ramdisk variant of the C/C++ module.
+			// build the ramdisk variants of the C/C++ module.
 			Exclude_srcs []string `android:"path"`
 
-			// List of additional cflags that should be used to build the vendor ramdisk
-			// variant of the C/C++ module.
+			// List of additional cflags that should be used to build the ramdisk
+			// variants of the C/C++ module.
 			Cflags []string
 		}
 		Platform struct {
@@ -351,6 +351,7 @@
 	CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
 	CheckBadCompilerFlags(ctx, "product.cflags", compiler.Properties.Target.Product.Cflags)
 	CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
+	CheckBadCompilerFlags(ctx, "ramdisk.cflags", compiler.Properties.Target.Ramdisk.Cflags)
 	CheckBadCompilerFlags(ctx, "vendor_ramdisk.cflags", compiler.Properties.Target.Vendor_ramdisk.Cflags)
 	CheckBadCompilerFlags(ctx, "platform.cflags", compiler.Properties.Target.Platform.Cflags)
 
@@ -536,6 +537,9 @@
 	if ctx.inVendorRamdisk() {
 		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor_ramdisk.Cflags)...)
 	}
+	if ctx.inRamdisk() {
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Ramdisk.Cflags)...)
+	}
 	if !ctx.useSdk() {
 		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Platform.Cflags)...)
 	}
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 e450ba7..ff5ab05 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{}
@@ -147,6 +149,7 @@
 	commonGlobalLldflags = []string{
 		"-fuse-ld=lld",
 		"-Wl,--icf=safe",
+		"-Wl,--no-demangle",
 	}
 
 	deviceGlobalCppflags = []string{
@@ -305,7 +308,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 +377,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, " ")
 	})
 
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 3bc1e69..40919c0 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -26,9 +26,6 @@
 		// Help catch common 32/64-bit errors.
 		"-Werror=implicit-function-declaration",
 		"-fno-emulated-tls",
-		// A temporary fix for SExtWRemoval miscompilation bug.
-		"-mllvm",
-		"-riscv-disable-sextw-removal=true",
 		"-march=rv64gc_zba_zbb_zbs",
 	}
 
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index a0ef575..62f75d1 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -261,5 +261,3 @@
 func LibFuzzerRuntimeInterceptors(t Toolchain) string {
 	return LibclangRuntimeLibrary(t, "fuzzer_interceptors")
 }
-
-var inList = android.InList
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index dd612ce..f9b3eac 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -21,6 +21,7 @@
 var VndkMustUseVendorVariantList = []string{
 	"android.hardware.nfc@1.2",
 	"libbinder",
+	"libdumpstateutil",
 	"libcrypto",
 	"libexpat",
 	"libgatekeeper",
diff --git a/cc/coverage.go b/cc/coverage.go
index c0f6973..cbd8a6f 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -48,6 +48,7 @@
 func getGcovProfileLibraryName(ctx ModuleContextIntf) string {
 	// This function should only ever be called for a cc.Module, so the
 	// following statement should always succeed.
+	// LINT.IfChange
 	if ctx.useSdk() {
 		return "libprofile-extras_ndk"
 	} else {
@@ -63,6 +64,7 @@
 	} else {
 		return "libprofile-clang-extras"
 	}
+	// LINT.ThenChange(library.go)
 }
 
 func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 636ad85..227fe8b 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -29,6 +29,7 @@
 func init() {
 	android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
 	android.RegisterParallelSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
+	android.RegisterParallelSingletonType("cc_fuzz_presubmit_packaging", fuzzPackagingFactoryPresubmit)
 }
 
 type FuzzProperties struct {
@@ -258,25 +259,29 @@
 
 func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
 	fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
-	builder := android.NewRuleBuilder(pctx, ctx)
 	intermediateDir := android.PathForModuleOut(ctx, "corpus")
+
+	// Create one rule per file to avoid MAX_ARG_STRLEN hardlimit.
 	for _, entry := range fuzzPackagedModule.Corpus {
-		builder.Command().Text("cp").
-			Input(entry).
-			Output(intermediateDir.Join(ctx, entry.Base()))
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Output: intermediateDir.Join(ctx, entry.Base()),
+			Input:  entry,
+		})
 	}
-	builder.Build("copy_corpus", "copy corpus")
 	fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
 
 	fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
-	builder = android.NewRuleBuilder(pctx, ctx)
 	intermediateDir = android.PathForModuleOut(ctx, "data")
+
+	// Create one rule per file to avoid MAX_ARG_STRLEN hardlimit.
 	for _, entry := range fuzzPackagedModule.Data {
-		builder.Command().Text("cp").
-			Input(entry).
-			Output(intermediateDir.Join(ctx, entry.Rel()))
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Output: intermediateDir.Join(ctx, entry.Rel()),
+			Input:  entry,
+		})
 	}
-	builder.Build("copy_data", "copy data")
 	fuzzPackagedModule.DataIntermediateDir = intermediateDir
 
 	if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
@@ -352,9 +357,10 @@
 // their architecture & target/host specific zip file.
 type ccRustFuzzPackager struct {
 	fuzz.FuzzPackager
-	fuzzPackagingArchModules         string
-	fuzzTargetSharedDepsInstallPairs string
-	allFuzzTargetsName               string
+	fuzzPackagingArchModules         			string
+	fuzzTargetSharedDepsInstallPairs 			string
+	allFuzzTargetsName               			string
+	onlyIncludePresubmits						bool
 }
 
 func fuzzPackagingFactory() android.Singleton {
@@ -363,6 +369,18 @@
 		fuzzPackagingArchModules:         "SOONG_FUZZ_PACKAGING_ARCH_MODULES",
 		fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
 		allFuzzTargetsName:               "ALL_FUZZ_TARGETS",
+		onlyIncludePresubmits:			  false,
+	}
+	return fuzzPackager
+}
+
+func fuzzPackagingFactoryPresubmit() android.Singleton {
+
+	fuzzPackager := &ccRustFuzzPackager{
+		fuzzPackagingArchModules:         "SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES",
+		fuzzTargetSharedDepsInstallPairs: "PRESUBMIT_FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+		allFuzzTargetsName:               "ALL_PRESUBMIT_FUZZ_TARGETS",
+		onlyIncludePresubmits:			  true,
 	}
 	return fuzzPackager
 }
@@ -386,7 +404,6 @@
 		if !ok || ccModule.PreventInstall() {
 			return
 		}
-
 		// Discard non-fuzz targets.
 		if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
 			return
@@ -403,6 +420,9 @@
 		} else if ccModule.Host() {
 			hostOrTargetString = "host"
 		}
+		if s.onlyIncludePresubmits == true {
+			hostOrTargetString = "presubmit-" + hostOrTargetString
+		}
 
 		fpm := fuzz.FuzzPackagedModule{}
 		if ok {
@@ -427,6 +447,14 @@
 		// The executable.
 		files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, ccModule, "unstripped")})
 
+		if s.onlyIncludePresubmits == true {
+			if fpm.FuzzProperties.Fuzz_config == nil {
+				return
+			}
+			if !BoolDefault(fpm.FuzzProperties.Fuzz_config.Use_for_presubmit, false){
+				return
+			}
+		}
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
 			return
diff --git a/cc/generated_cc_library.go b/cc/generated_cc_library.go
new file mode 100644
index 0000000..55e19f9
--- /dev/null
+++ b/cc/generated_cc_library.go
@@ -0,0 +1,38 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+)
+
+func GeneratedCcLibraryModuleFactory(moduleName string, callbacks Generator) android.Module {
+	module, _ := NewLibrary(android.HostAndDeviceSupported)
+
+	// Can be used as both a static and a shared library.
+	module.sdkMemberTypes = []android.SdkMemberType{
+		sharedLibrarySdkMemberType,
+		staticLibrarySdkMemberType,
+		staticAndSharedLibrarySdkMemberType,
+	}
+
+	//	TODO: Need to be bazelable
+	//	module.bazelable = true
+	//	module.bazelHandler = &ccLibraryBazelHandler{module: module}
+
+	module.generators = append(module.generators, callbacks)
+
+	return module.Init()
+}
diff --git a/cc/image.go b/cc/image.go
index e65a9aa..f91762a 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -678,10 +678,17 @@
 	}
 }
 
+func squashRamdiskSrcs(m *Module) {
+	if lib, ok := m.compiler.(*libraryDecorator); ok {
+		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Ramdisk.Exclude_srcs...)
+	}
+}
+
 func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
 	m := module.(*Module)
 	if variant == android.RamdiskVariation {
 		m.MakeAsPlatform()
+		squashRamdiskSrcs(m)
 	} else if variant == android.VendorRamdiskVariation {
 		m.MakeAsPlatform()
 		squashVendorRamdiskSrcs(m)
diff --git a/cc/library.go b/cc/library.go
index aec6433..df1dbc5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,6 +32,20 @@
 	"github.com/google/blueprint/proptools"
 )
 
+var (
+	alwaysLinkLibraries = map[string]bool{
+		// Coverage libraries are _always_ added as a whole_static_dep. By converting as these as
+		// alwayslink = True, we can add these as to deps (e.g. as a regular static dep) in Bazel
+		// without any extra complications in cc_shared_library roots to prevent linking the same
+		// library repeatedly.
+		"libprofile-extras_ndk":               true,
+		"libprofile-extras":                   true,
+		"libprofile-clang-extras_ndk":         true,
+		"libprofile-clang-extras_cfi_support": true,
+		"libprofile-clang-extras":             true,
+	}
+)
+
 // LibraryProperties is a collection of properties shared by cc library rules/cc.
 type LibraryProperties struct {
 	// local file name to pass to the linker as -unexported_symbols_list
@@ -435,6 +449,10 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
 	}
 
+	if _, ok := alwaysLinkLibraries[m.Name()]; ok {
+		staticTargetAttrs.Alwayslink = proptools.BoolPtr(true)
+	}
+
 	var tagsForStaticVariant bazel.StringListAttribute
 	if compilerAttrs.stubsSymbolFile == nil && len(compilerAttrs.stubsVersions.Value) == 0 {
 		tagsForStaticVariant = android.ApexAvailableTagsWithoutTestApexes(ctx, m)
@@ -1963,6 +1981,10 @@
 
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
+	// Most opt-in libraries do not have dumps for all default architectures.
+	if ctx.Config().HasDeviceProduct() {
+		errorMessage += " -products " + ctx.Config().DeviceProduct()
+	}
 
 	library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
 		isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
@@ -2271,7 +2293,7 @@
 
 			// do not install vndk libs
 			// vndk libs are packaged into VNDK APEX
-			if ctx.isVndk() && !ctx.IsVndkExt() {
+			if ctx.isVndk() && !ctx.IsVndkExt() && !ctx.Config().IsVndkDeprecated() {
 				return
 			}
 		} else if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
@@ -2947,6 +2969,10 @@
 	var attrs interface{}
 	if isStatic {
 		commonAttrs.Deps.Add(baseAttributes.protoDependency)
+		var alwayslink *bool
+		if _, ok := alwaysLinkLibraries[module.Name()]; ok && isStatic {
+			alwayslink = proptools.BoolPtr(true)
+		}
 		attrs = &bazelCcLibraryStaticAttributes{
 			staticOrSharedAttributes: commonAttrs,
 			Rtti:                     compilerAttrs.rtti,
@@ -2960,8 +2986,10 @@
 			Conlyflags: compilerAttrs.conlyFlags,
 			Asflags:    asFlags,
 
-			Features: *features,
+			Alwayslink: alwayslink,
+			Features:   *features,
 		}
+
 	} else {
 		commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
 
@@ -3043,7 +3071,8 @@
 	Conlyflags bazel.StringListAttribute
 	Asflags    bazel.StringListAttribute
 
-	Features bazel.StringListAttribute
+	Alwayslink *bool
+	Features   bazel.StringListAttribute
 }
 
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
diff --git a/cc/linkable.go b/cc/linkable.go
index 19e6501..2099399 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -87,6 +87,12 @@
 	// SnapshotStaticLibs returns the list of static library dependencies for this module.
 	SnapshotStaticLibs() []string
 
+	// SnapshotDylibs returns the list of dylib library dependencies for this module.
+	SnapshotDylibs() []string
+
+	// SnapshotRlibs returns the list of rlib library dependencies for this module.
+	SnapshotRlibs() []string
+
 	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
 	IsSnapshotPrebuilt() bool
 }
@@ -239,6 +245,9 @@
 	// Dylib returns true if this is an dylib module.
 	Dylib() bool
 
+	// RlibStd returns true if this is an rlib which links against an rlib libstd.
+	RlibStd() bool
+
 	// Static returns true if this is a static library module.
 	Static() bool
 
diff --git a/cc/lto.go b/cc/lto.go
index 510dd79..df9ca0a 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,37 @@
 }
 
 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
+	} else if ctx.Arch().ArchType.Multilib == "lib32" {
+		// LP32 has many subtle issues and less test coverage.
+		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 {
@@ -80,7 +111,7 @@
 		var ltoLdFlags []string
 
 		// The module did not explicitly turn on LTO. Only leverage LTO's
-		// better dead code elinmination and CFG simplification, but do
+		// better dead code elimination and CFG simplification, but do
 		// not perform costly optimizations for a balance between compile
 		// time, binary size and performance.
 		if !lto.ThinLTO() {
@@ -104,10 +135,14 @@
 			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.Darwin() && !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
-			ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
+		// Reduce the inlining threshold for a better balance of binary size and
+		// performance.
+		if !ctx.Darwin() {
+			if ctx.isPgoCompile() || ctx.isAfdoCompile() {
+				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40")
+			} else {
+				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
+			}
 		}
 
 		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlags...)
@@ -118,34 +153,6 @@
 	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)
 }
@@ -155,15 +162,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
 		}
 
@@ -238,6 +243,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_library.go b/cc/ndk_library.go
index f0b7cc5..9281aeb 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -31,9 +31,8 @@
 
 func init() {
 	pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
-	pctx.HostBinToolVariable("abidiff", "abidiff")
-	pctx.HostBinToolVariable("abitidy", "abitidy")
-	pctx.HostBinToolVariable("abidw", "abidw")
+	pctx.HostBinToolVariable("stg", "stg")
+	pctx.HostBinToolVariable("stgdiff", "stgdiff")
 }
 
 var (
@@ -44,28 +43,20 @@
 			CommandDeps: []string{"$ndkStubGenerator"},
 		}, "arch", "apiLevel", "apiMap", "flags")
 
-	abidw = pctx.AndroidStaticRule("abidw",
+	stg = pctx.AndroidStaticRule("stg",
 		blueprint.RuleParams{
-			Command: "$abidw --type-id-style hash --no-corpus-path " +
-				"--no-show-locs --no-comp-dir-path -w $symbolList " +
-				"$in --out-file $out",
-			CommandDeps: []string{"$abidw"},
+			Command:     "$stg -S :$symbolList --elf $in -o $out",
+			CommandDeps: []string{"$stg"},
 		}, "symbolList")
 
-	abitidy = pctx.AndroidStaticRule("abitidy",
-		blueprint.RuleParams{
-			Command:     "$abitidy --all $flags -i $in -o $out",
-			CommandDeps: []string{"$abitidy"},
-		}, "flags")
-
-	abidiff = pctx.AndroidStaticRule("abidiff",
+	stgdiff = pctx.AndroidStaticRule("stgdiff",
 		blueprint.RuleParams{
 			// Need to create *some* output for ninja. We don't want to use tee
 			// because we don't want to spam the build output with "nothing
 			// changed" messages, so redirect output message to $out, and if
 			// changes were detected print the output and fail.
-			Command:     "$abidiff $args $in > $out || (cat $out && false)",
-			CommandDeps: []string{"$abidiff"},
+			Command:     "$stgdiff $args --stg $in -o $out || (cat $out && false)",
+			CommandDeps: []string{"$stgdiff"},
 		}, "args")
 
 	ndkLibrarySuffix = ".ndk"
@@ -107,12 +98,6 @@
 	// https://github.com/android-ndk/ndk/issues/265.
 	Unversioned_until *string
 
-	// If true, does not emit errors when APIs lacking type information are
-	// found. This is false by default and should not be enabled outside bionic,
-	// where it is enabled pending a fix for http://b/190554910 (no debug info
-	// for asm implemented symbols).
-	Allow_untyped_symbols *bool
-
 	// Headers presented by this library to the Public API Surface
 	Export_header_libs []string
 }
@@ -326,7 +311,7 @@
 	apiLevel android.ApiLevel) android.OptionalPath {
 
 	subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
-		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml")
+		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.stg")
 	return android.ExistentPathForSource(ctx, subpath)
 }
 
@@ -359,34 +344,17 @@
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
 	implementationLibrary := this.findImplementationLibrary(ctx)
-	abiRawPath := getNdkAbiDumpInstallBase(ctx).Join(ctx,
-		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.raw.xml")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        abidw,
-		Description: fmt.Sprintf("abidw %s", implementationLibrary),
-		Input:       implementationLibrary,
-		Output:      abiRawPath,
-		Implicit:    symbolList,
-		Args: map[string]string{
-			"symbolList": symbolList.String(),
-		},
-	})
-
 	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.xml")
-	untypedFlag := "--abort-on-untyped-symbols"
-	if proptools.BoolDefault(this.properties.Allow_untyped_symbols, false) {
-		untypedFlag = ""
-	}
+		this.libraryName(ctx), "abi.stg")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        abitidy,
-		Description: fmt.Sprintf("abitidy %s", implementationLibrary),
-		Input:       abiRawPath,
+		Rule:        stg,
+		Description: fmt.Sprintf("stg %s", implementationLibrary),
+		Input:       implementationLibrary,
+		Implicit:    symbolList,
 		Output:      this.abiDumpPath,
 		Args: map[string]string{
-			"flags": untypedFlag,
+			"symbolList": symbolList.String(),
 		},
 	})
 }
@@ -405,7 +373,7 @@
 func (this *stubDecorator) diffAbi(ctx ModuleContext) {
 	// Catch any ABI changes compared to the checked-in definition of this API
 	// level.
-	abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
+	abiDiffPath := android.PathForModuleOut(ctx, "stgdiff.timestamp")
 	prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
 	missingPrebuiltError := fmt.Sprintf(
 		"Did not find prebuilt ABI dump for %q (%q). Generate with "+
@@ -421,11 +389,14 @@
 		})
 	} else {
 		ctx.Build(pctx, android.BuildParams{
-			Rule: abidiff,
-			Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump,
+			Rule: stgdiff,
+			Description: fmt.Sprintf("Comparing ABI %s %s", prebuiltAbiDump,
 				this.abiDumpPath),
 			Output: abiDiffPath,
 			Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
+			Args: map[string]string{
+				"args": "--format=small",
+			},
 		})
 	}
 	this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
@@ -452,13 +423,13 @@
 			})
 		} else {
 			ctx.Build(pctx, android.BuildParams{
-				Rule: abidiff,
+				Rule: stgdiff,
 				Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
 					nextAbiDump),
 				Output: nextAbiDiffPath,
 				Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
 				Args: map[string]string{
-					"args": "--no-added-syms",
+					"args": "--format=small --ignore=interface_addition",
 				},
 			})
 		}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 0cf21b6..feb3880 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -58,7 +58,6 @@
 
 func init() {
 	RegisterNdkModuleTypes(android.InitRegistrationContext)
-	pctx.Import("android/soong/android")
 }
 
 func RegisterNdkModuleTypes(ctx android.RegistrationContext) {
diff --git a/cc/rs.go b/cc/rs.go
index 6507259..93acdc7 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -101,11 +101,12 @@
 func rsFlags(ctx ModuleContext, flags Flags, properties *BaseCompilerProperties) Flags {
 	targetApi := String(properties.Renderscript.Target_api)
 	if targetApi == "" && ctx.useSdk() {
-		switch ctx.sdkVersion() {
-		case "current", "system_current", "test_current":
-			// Nothing
-		default:
-			targetApi = android.GetNumericSdkVersion(ctx.sdkVersion())
+		targetApiLevel := android.ApiLevelOrPanic(ctx, ctx.sdkVersion())
+		if targetApiLevel.IsCurrent() || targetApiLevel.IsPreview() {
+			// If the target level is current or preview, leave the 'target-api' unset.
+			// This signals to llvm-rs-cc that the development API should be used.
+		} else {
+			targetApi = targetApiLevel.String()
 		}
 	}
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 62e31d1..c1ef970 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -56,10 +56,6 @@
 		// higher number of "optimized out" stack variables.
 		// b/112437883.
 		"-instcombine-lower-dbg-declare=0",
-		// TODO(b/159343917): HWASan and GlobalISel don't play nicely, and
-		// GlobalISel is the default at -O0 on aarch64.
-		"--aarch64-enable-global-isel-at-O=-1",
-		"-fast-isel=false",
 		"-hwasan-use-after-scope=1",
 		"-dom-tree-reachability-max-bbs-to-explore=128",
 	}
@@ -646,10 +642,6 @@
 	if (ctx.Arch().ArchType != android.Arm64 && ctx.Arch().ArchType != android.Riscv64) || !ctx.toolchain().Bionic() {
 		s.Scs = nil
 	}
-	// ...but temporarily globally disabled on riscv64 (http://b/277909695).
-	if ctx.Arch().ArchType == android.Riscv64 {
-		s.Scs = nil
-	}
 
 	// Memtag_heap is only implemented on AArch64.
 	// Memtag ABI is Android specific for now, so disable for host.
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index a5729df..bb13310 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -100,6 +100,7 @@
 	snapshotBinarySuffix = "_binary."
 	snapshotObjectSuffix = "_object."
 	SnapshotRlibSuffix   = "_rlib."
+	SnapshotDylibSuffix  = "_dylib."
 )
 
 type SnapshotProperties struct {
@@ -107,6 +108,7 @@
 	Static_libs []string `android:"arch_variant"`
 	Shared_libs []string `android:"arch_variant"`
 	Rlibs       []string `android:"arch_variant"`
+	Dylibs      []string `android:"arch_variant"`
 	Vndk_libs   []string `android:"arch_variant"`
 	Binaries    []string `android:"arch_variant"`
 	Objects     []string `android:"arch_variant"`
@@ -186,6 +188,7 @@
 	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, SnapshotStaticSuffix)
 	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, SnapshotSharedSuffix)
 	rlibs := collectSnapshotMap(s.properties.Rlibs, snapshotSuffix, SnapshotRlibSuffix)
+	dylibs := collectSnapshotMap(s.properties.Dylibs, snapshotSuffix, SnapshotDylibSuffix)
 	vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix)
 	for k, v := range vndkLibs {
 		sharedLibs[k] = v
@@ -198,11 +201,12 @@
 		StaticLibs: staticLibs,
 		SharedLibs: sharedLibs,
 		Rlibs:      rlibs,
+		Dylibs:     dylibs,
 	})
 }
 
 type SnapshotInfo struct {
-	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs, Rlibs map[string]string
+	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs, Rlibs, Dylibs map[string]string
 }
 
 var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps")
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index cf4617d..1ee120e 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -57,6 +57,14 @@
 	return m.Properties.SnapshotStaticLibs
 }
 
+func (m *Module) SnapshotRlibs() []string {
+	return []string{}
+}
+
+func (m *Module) SnapshotDylibs() []string {
+	return []string{}
+}
+
 // snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
 type snapshotLibraryInterface interface {
 	libraryInterface
diff --git a/cc/stl.go b/cc/stl.go
index f1433ef..8f92dcb 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -80,8 +80,7 @@
 			return ""
 		}
 		s = deduplicateStlInput(s)
-		archHasNDKStl := ctx.Arch().ArchType != android.Riscv64
-		if ctx.useSdk() && ctx.Device() && archHasNDKStl {
+		if ctx.useSdk() && ctx.Device() {
 			switch s {
 			case "", "system":
 				return "ndk_system"
@@ -120,11 +119,6 @@
 	}()
 }
 
-func needsLibAndroidSupport(ctx BaseModuleContext) bool {
-	version := nativeApiLevelOrPanic(ctx, ctx.sdkVersion())
-	return version.LessThan(android.FirstNonLibAndroidSupportVersion)
-}
-
 func staticUnwinder(ctx android.BaseModuleContext) string {
 	vndkVersion := ctx.Module().(*Module).VndkVersion()
 
@@ -184,11 +178,6 @@
 		} else {
 			deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
 		}
-		if needsLibAndroidSupport(ctx) {
-			// Use LateStaticLibs for ndk_libandroid_support so that its include directories
-			// come after ndk_libc++_static or ndk_libc++_shared.
-			deps.LateStaticLibs = append(deps.LateStaticLibs, "ndk_libandroid_support")
-		}
 		deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
diff --git a/cc/test.go b/cc/test.go
index 3f5f710..0be2301 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -267,7 +267,7 @@
 	return BoolDefault(test.LinkerProperties.Gtest, true)
 }
 
-func (test *testDecorator) isolated(ctx BaseModuleContext) bool {
+func (test *testDecorator) isolated(ctx android.EarlyModuleContext) bool {
 	return BoolDefault(test.LinkerProperties.Isolated, false)
 }
 
@@ -641,14 +641,27 @@
 
 var _ BazelHandler = (*ccTestBazelHandler)(nil)
 
+// The top level target named $label is a test_suite target,
+// not the internal cc_test executable target.
+//
+// This is to ensure `b test //$label` runs the test_suite target directly,
+// which depends on tradefed_test targets, instead of the internal cc_test
+// target, which doesn't have tradefed integrations.
+//
+// However, for cquery, we want the internal cc_test executable target, which
+// has the suffix "__tf_internal".
+func mixedBuildsTestLabel(label string) string {
+	return label + "__tf_internal"
+}
+
 func (handler *ccTestBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(mixedBuildsTestLabel(label), cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx))
 }
 
 func (handler *ccTestBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKey(ctx))
+	info, err := bazelCtx.GetCcUnstrippedInfo(mixedBuildsTestLabel(label), android.GetConfigKey(ctx))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
@@ -669,8 +682,7 @@
 type testBinaryAttributes struct {
 	binaryAttributes
 
-	Gtest    bool
-	Isolated bool
+	Gtest *bool
 
 	tidyAttributes
 	tradefed.TestConfigAttributes
@@ -708,13 +720,15 @@
 
 	m.convertTidyAttributes(ctx, &testBinaryAttrs.tidyAttributes)
 
-	for _, propIntf := range m.GetProperties() {
-		if testLinkerProps, ok := propIntf.(*TestLinkerProperties); ok {
-			testBinaryAttrs.Gtest = proptools.BoolDefault(testLinkerProps.Gtest, true)
-			testBinaryAttrs.Isolated = proptools.BoolDefault(testLinkerProps.Isolated, true)
-			break
-		}
-	}
+	testBinary := m.linker.(*testBinary)
+	gtest := testBinary.gtest()
+	gtestIsolated := testBinary.isolated(ctx)
+	// Use the underling bool pointer for Gtest in attrs
+	// This ensures that if this property is not set in Android.bp file, it will not be set in BUILD file either
+	// cc_test macro will default gtest to True
+	testBinaryAttrs.Gtest = testBinary.LinkerProperties.Gtest
+
+	addImplicitGtestDeps(ctx, &testBinaryAttrs, gtest, gtestIsolated)
 
 	for _, testProps := range m.GetProperties() {
 		if p, ok := testProps.(*TestBinaryProperties); ok {
@@ -727,7 +741,7 @@
 				p.Auto_gen_config,
 				p.Test_options.Test_suite_tag,
 				p.Test_config_template,
-				getTradefedConfigOptions(ctx, p, testBinaryAttrs.Isolated),
+				getTradefedConfigOptions(ctx, p, gtestIsolated),
 				&testInstallBase,
 			)
 			testBinaryAttrs.TestConfigAttributes = testConfigAttributes
@@ -747,3 +761,28 @@
 		},
 		&testBinaryAttrs)
 }
+
+// cc_test that builds using gtest needs some additional deps
+// addImplicitGtestDeps makes these deps explicit in the generated BUILD files
+func addImplicitGtestDeps(ctx android.BazelConversionPathContext, attrs *testBinaryAttributes, gtest, gtestIsolated bool) {
+	addDepsAndDedupe := func(lla *bazel.LabelListAttribute, modules []string) {
+		moduleLabels := android.BazelLabelForModuleDeps(ctx, modules)
+		lla.Value.Append(moduleLabels)
+		// Dedupe
+		lla.Value = bazel.FirstUniqueBazelLabelList(lla.Value)
+	}
+	// this must be kept in sync with Soong's implementation in:
+	// https://cs.android.com/android/_/android/platform/build/soong/+/460fb2d6d546b5ab493a7e5479998c4933a80f73:cc/test.go;l=300-313;drc=ec7314336a2b35ea30ce5438b83949c28e3ac429;bpv=1;bpt=0
+	if gtest {
+		// TODO - b/244433197: Handle canUseSdk
+		if gtestIsolated {
+			addDepsAndDedupe(&attrs.Deps, []string{"libgtest_isolated_main"})
+			addDepsAndDedupe(&attrs.Dynamic_deps, []string{"liblog"})
+		} else {
+			addDepsAndDedupe(&attrs.Deps, []string{
+				"libgtest_main",
+				"libgtest",
+			})
+		}
+	}
+}
diff --git a/cc/testing.go b/cc/testing.go
index d346739..d1632aa 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -423,11 +423,6 @@
 			export_include_dirs: ["ndk_libc++_shared"],
 		}
 
-		ndk_prebuilt_static_stl {
-			name: "ndk_libandroid_support",
-			export_include_dirs: ["ndk_libandroid_support"],
-		}
-
 		cc_library_static {
 			name: "libgoogle-benchmark",
 			sdk_version: "current",
@@ -573,16 +568,15 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
-		"defaults/cc/common/libc.map.txt":           nil,
-		"defaults/cc/common/libdl.map.txt":          nil,
-		"defaults/cc/common/libm.map.txt":           nil,
-		"defaults/cc/common/ndk_libandroid_support": nil,
-		"defaults/cc/common/ndk_libc++_shared":      nil,
-		"defaults/cc/common/crtbegin_so.c":          nil,
-		"defaults/cc/common/crtbegin.c":             nil,
-		"defaults/cc/common/crtend_so.c":            nil,
-		"defaults/cc/common/crtend.c":               nil,
-		"defaults/cc/common/crtbrand.c":             nil,
+		"defaults/cc/common/libc.map.txt":      nil,
+		"defaults/cc/common/libdl.map.txt":     nil,
+		"defaults/cc/common/libm.map.txt":      nil,
+		"defaults/cc/common/ndk_libc++_shared": nil,
+		"defaults/cc/common/crtbegin_so.c":     nil,
+		"defaults/cc/common/crtbegin.c":        nil,
+		"defaults/cc/common/crtend_so.c":       nil,
+		"defaults/cc/common/crtend.c":          nil,
+		"defaults/cc/common/crtbrand.c":        nil,
 
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil,
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a":   nil,
diff --git a/cc/util.go b/cc/util.go
index 6d8ac43..c93646b 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -28,8 +28,8 @@
 	return android.JoinWithPrefix(dirs.Strings(), "-I")
 }
 
-var indexList = android.IndexList
-var inList = android.InList
+var indexList = android.IndexList[string]
+var inList = android.InList[string]
 var filterList = android.FilterList
 var removeListFromList = android.RemoveListFromList
 var removeFromList = android.RemoveFromList
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index d2531c0..9ea337b 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -108,10 +108,10 @@
 				return false
 			}
 		}
-		if sanitizable.Static() {
+		if sanitizable.Static() || sanitizable.Rlib() {
 			return sanitizable.OutputFile().Valid() && !isPrivate(image, m)
 		}
-		if sanitizable.Shared() || sanitizable.Rlib() {
+		if sanitizable.Shared() || sanitizable.Dylib() {
 			if !sanitizable.OutputFile().Valid() {
 				return false
 			}
@@ -153,6 +153,8 @@
 	SharedLibs  []string `json:",omitempty"`
 	StaticLibs  []string `json:",omitempty"`
 	RuntimeLibs []string `json:",omitempty"`
+	Dylibs      []string `json:",omitempty"`
+	Rlibs       []string `json:",omitempty"`
 
 	// extra config files
 	InitRc         []string `json:",omitempty"`
@@ -283,8 +285,17 @@
 			if m.Shared() {
 				prop.SharedLibs = m.SnapshotSharedLibs()
 			}
-			// static libs dependencies are required to collect the NOTICE files.
+
+			// dylibs collect both shared and dylib dependencies.
+			if m.Dylib() {
+				prop.SharedLibs = m.SnapshotSharedLibs()
+				prop.Dylibs = m.SnapshotDylibs()
+			}
+
+			// static and rlib libs dependencies are required to collect the NOTICE files.
 			prop.StaticLibs = m.SnapshotStaticLibs()
+			prop.Rlibs = m.SnapshotRlibs()
+
 			if sanitizable, ok := m.(PlatformSanitizeable); ok {
 				if sanitizable.Static() && sanitizable.SanitizePropDefined() {
 					prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded()
@@ -299,13 +310,15 @@
 				libType = "shared"
 			} else if m.Rlib() {
 				libType = "rlib"
+			} else if m.Dylib() {
+				libType = "dylib"
 			} else {
 				libType = "header"
 			}
 
 			var stem string
 
-			// install .a or .so
+			// install .a, .rlib, .dylib.so, or .so
 			if libType != "header" {
 				libPath := m.OutputFile().Path()
 				stem = libPath.Base()
@@ -328,6 +341,12 @@
 						}
 					}
 				}
+				if m.Rlib() && m.RlibStd() {
+					// rlibs produce both rlib-std and dylib-std variants
+					ext := filepath.Ext(stem)
+					stem = strings.TrimSuffix(stem, ext) + ".rlib-std" + ext
+					prop.ModuleName += ".rlib-std"
+				}
 				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem)
 				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
 			} else {
@@ -341,8 +360,12 @@
 			prop.StaticExecutable = m.StaticExecutable()
 			prop.InstallInRoot = m.InstallInRoot()
 			prop.SharedLibs = m.SnapshotSharedLibs()
-			// static libs dependencies are required to collect the NOTICE files.
+			prop.Dylibs = m.SnapshotDylibs()
+
+			// static and rlib dependencies are required to collect the NOTICE files.
 			prop.StaticLibs = m.SnapshotStaticLibs()
+			prop.Rlibs = m.SnapshotRlibs()
+
 			// install bin
 			binPath := m.OutputFile().Path()
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index e3d1179..a70a9d1 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -122,7 +122,7 @@
 }
 
 func (be ZipEntryFromBuffer) WriteToZip(dest string, zw *zip.Writer) error {
-	w, err := zw.CreateHeader(be.fh)
+	w, err := zw.CreateHeaderAndroid(be.fh)
 	if err != nil {
 		return err
 	}
@@ -562,6 +562,8 @@
 		}
 	}
 
+	var jarServices jar.Services
+
 	// Finally, add entries from all the input zips.
 	for _, inputZip := range inputZips {
 		_, copyFully := zipsToNotStrip[inputZip.Name()]
@@ -570,6 +572,14 @@
 		}
 
 		for i, entry := range inputZip.Entries() {
+			if emulateJar && jarServices.IsServiceFile(entry) {
+				// If this is a jar, collect service files to combine  instead of adding them to the zip.
+				err := jarServices.AddServiceFile(entry)
+				if err != nil {
+					return err
+				}
+				continue
+			}
 			if copyFully || !out.isEntryExcluded(entry.Name) {
 				if err := out.copyEntry(inputZip, i); err != nil {
 					return err
@@ -585,6 +595,16 @@
 	}
 
 	if emulateJar {
+		// Combine all the service files into a single list of combined service files and add them to the zip.
+		for _, serviceFile := range jarServices.ServiceFiles() {
+			_, err := out.addZipEntry(serviceFile.Name, ZipEntryFromBuffer{
+				fh:      serviceFile.FileHeader,
+				content: serviceFile.Contents,
+			})
+			if err != nil {
+				return err
+			}
+		}
 		return out.writeEntries(out.jarSorted())
 	} else if sortEntries {
 		return out.writeEntries(out.alphanumericSorted())
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
index cb58436..767d4e61 100644
--- a/cmd/merge_zips/merge_zips_test.go
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"fmt"
+	"hash/crc32"
 	"os"
 	"strconv"
 	"strings"
@@ -27,28 +28,34 @@
 )
 
 type testZipEntry struct {
-	name string
-	mode os.FileMode
-	data []byte
+	name   string
+	mode   os.FileMode
+	data   []byte
+	method uint16
 }
 
 var (
-	A     = testZipEntry{"A", 0755, []byte("foo")}
-	a     = testZipEntry{"a", 0755, []byte("foo")}
-	a2    = testZipEntry{"a", 0755, []byte("FOO2")}
-	a3    = testZipEntry{"a", 0755, []byte("Foo3")}
-	bDir  = testZipEntry{"b/", os.ModeDir | 0755, nil}
-	bbDir = testZipEntry{"b/b/", os.ModeDir | 0755, nil}
-	bbb   = testZipEntry{"b/b/b", 0755, nil}
-	ba    = testZipEntry{"b/a", 0755, []byte("foob")}
-	bc    = testZipEntry{"b/c", 0755, []byte("bar")}
-	bd    = testZipEntry{"b/d", 0700, []byte("baz")}
-	be    = testZipEntry{"b/e", 0700, []byte("")}
+	A     = testZipEntry{"A", 0755, []byte("foo"), zip.Deflate}
+	a     = testZipEntry{"a", 0755, []byte("foo"), zip.Deflate}
+	a2    = testZipEntry{"a", 0755, []byte("FOO2"), zip.Deflate}
+	a3    = testZipEntry{"a", 0755, []byte("Foo3"), zip.Deflate}
+	bDir  = testZipEntry{"b/", os.ModeDir | 0755, nil, zip.Deflate}
+	bbDir = testZipEntry{"b/b/", os.ModeDir | 0755, nil, zip.Deflate}
+	bbb   = testZipEntry{"b/b/b", 0755, nil, zip.Deflate}
+	ba    = testZipEntry{"b/a", 0755, []byte("foo"), zip.Deflate}
+	bc    = testZipEntry{"b/c", 0755, []byte("bar"), zip.Deflate}
+	bd    = testZipEntry{"b/d", 0700, []byte("baz"), zip.Deflate}
+	be    = testZipEntry{"b/e", 0700, []byte(""), zip.Deflate}
 
-	metainfDir     = testZipEntry{jar.MetaDir, os.ModeDir | 0755, nil}
-	manifestFile   = testZipEntry{jar.ManifestFile, 0755, []byte("manifest")}
-	manifestFile2  = testZipEntry{jar.ManifestFile, 0755, []byte("manifest2")}
-	moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
+	service1a        = testZipEntry{"META-INF/services/service1", 0755, []byte("class1\nclass2\n"), zip.Store}
+	service1b        = testZipEntry{"META-INF/services/service1", 0755, []byte("class1\nclass3\n"), zip.Deflate}
+	service1combined = testZipEntry{"META-INF/services/service1", 0755, []byte("class1\nclass2\nclass3\n"), zip.Store}
+	service2         = testZipEntry{"META-INF/services/service2", 0755, []byte("class1\nclass2\n"), zip.Deflate}
+
+	metainfDir     = testZipEntry{jar.MetaDir, os.ModeDir | 0755, nil, zip.Deflate}
+	manifestFile   = testZipEntry{jar.ManifestFile, 0755, []byte("manifest"), zip.Deflate}
+	manifestFile2  = testZipEntry{jar.ManifestFile, 0755, []byte("manifest2"), zip.Deflate}
+	moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info"), zip.Deflate}
 )
 
 type testInputZip struct {
@@ -236,6 +243,15 @@
 				"in1": true,
 			},
 		},
+		{
+			name: "services",
+			in: [][]testZipEntry{
+				{service1a, service2},
+				{service1b},
+			},
+			jar: true,
+			out: []testZipEntry{service1combined, service2},
+		},
 	}
 
 	for _, test := range testCases {
@@ -256,7 +272,7 @@
 
 			closeErr := writer.Close()
 			if closeErr != nil {
-				t.Fatal(err)
+				t.Fatal(closeErr)
 			}
 
 			if test.err != "" {
@@ -266,12 +282,16 @@
 					t.Fatal("incorrect err, want:", test.err, "got:", err)
 				}
 				return
+			} else if err != nil {
+				t.Fatal("unexpected err: ", err)
 			}
 
 			if !bytes.Equal(want, out.Bytes()) {
 				t.Error("incorrect zip output")
 				t.Errorf("want:\n%s", dumpZip(want))
 				t.Errorf("got:\n%s", dumpZip(out.Bytes()))
+				os.WriteFile("/tmp/got.zip", out.Bytes(), 0755)
+				os.WriteFile("/tmp/want.zip", want, 0755)
 			}
 		})
 	}
@@ -286,8 +306,14 @@
 			Name: e.name,
 		}
 		fh.SetMode(e.mode)
+		fh.Method = e.method
+		fh.UncompressedSize64 = uint64(len(e.data))
+		fh.CRC32 = crc32.ChecksumIEEE(e.data)
+		if fh.Method == zip.Store {
+			fh.CompressedSize64 = fh.UncompressedSize64
+		}
 
-		w, err := zw.CreateHeader(&fh)
+		w, err := zw.CreateHeaderAndroid(&fh)
 		if err != nil {
 			panic(err)
 		}
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index ba0648d..3fb4454 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -556,7 +556,8 @@
     {{- end}}
     {{- end}}
     {{- if .IsApk}}
-    presigned: true
+    preprocessed: true,
+    presigned: true,
     {{- end}}
 
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 22d64a2..62b3333 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -121,6 +121,10 @@
 	defer ctx.EventHandler.End("mixed_build")
 
 	bazelHook := func() error {
+		err := ctx.Config().BazelContext.QueueBazelSandwichCqueryRequests(ctx.Config())
+		if err != nil {
+			return err
+		}
 		return ctx.Config().BazelContext.InvokeBazel(ctx.Config(), ctx)
 	}
 	ctx.SetBeforePrepareBuildActionsHook(bazelHook)
@@ -225,7 +229,7 @@
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 
 	// Create soong_injection repository
-	soongInjectionFiles, err := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
+	soongInjectionFiles, workspaceFiles, err := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
 	maybeQuit(err, "")
 	absoluteSoongInjectionDir := shared.JoinPath(topDir, ctx.Config().SoongOutDir(), bazel.SoongInjectionDirName)
 	for _, file := range soongInjectionFiles {
@@ -236,6 +240,9 @@
 		// to allow users to edit/experiment in the synthetic workspace.
 		writeReadWriteFile(absoluteSoongInjectionDir, file)
 	}
+	for _, file := range workspaceFiles {
+		writeReadWriteFile(absoluteApiBp2buildDir, file)
+	}
 
 	workspace := shared.JoinPath(ctx.Config().SoongOutDir(), "api_bp2build")
 	// Create the symlink forest
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 682fb75..4097e8a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -218,6 +218,11 @@
 
 	trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
 
+	log.Verbose("Command Line: ")
+	for i, arg := range os.Args {
+		log.Verbosef("  [%d] %s", i, arg)
+	}
+
 	defer func() {
 		stat.Finish()
 		criticalPath.WriteToMetrics(met)
diff --git a/cmd/zip2zip/BUILD.bazel b/cmd/zip2zip/BUILD.bazel
deleted file mode 100644
index 1915a2d..0000000
--- a/cmd/zip2zip/BUILD.bazel
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-alias(
-    name = "zip2zip",
-    actual = "//prebuilts/build-tools:linux-x86/bin/zip2zip",
-)
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index bb83dc8..ba41f4a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -45,7 +45,8 @@
 	BootJars     android.ConfiguredJarList // modules for jars that form the boot class path
 	ApexBootJars android.ConfiguredJarList // jars within apex that form the boot class path
 
-	ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX
+	ArtApexJars              android.ConfiguredJarList // modules for jars that are in the ART APEX
+	TestOnlyArtBootImageJars android.ConfiguredJarList // modules for jars to be included in the ART boot image for testing
 
 	SystemServerJars               android.ConfiguredJarList // system_server classpath jars on the platform
 	SystemServerApps               []string                  // apps that are loaded into system server
@@ -700,6 +701,7 @@
 		BootJars:                           android.EmptyConfiguredJarList(),
 		ApexBootJars:                       android.EmptyConfiguredJarList(),
 		ArtApexJars:                        android.EmptyConfiguredJarList(),
+		TestOnlyArtBootImageJars:           android.EmptyConfiguredJarList(),
 		SystemServerJars:                   android.EmptyConfiguredJarList(),
 		SystemServerApps:                   nil,
 		ApexSystemServerJars:               android.EmptyConfiguredJarList(),
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 6ed0736..147a562 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -111,6 +111,13 @@
 	})
 }
 
+// FixtureSetTestOnlyArtBootImageJars enables dexpreopt and sets the TestOnlyArtBootImageJars property.
+func FixtureSetTestOnlyArtBootImageJars(bootJars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.TestOnlyArtBootImageJars = android.CreateTestConfiguredJarList(bootJars)
+	})
+}
+
 // FixtureSetBootJars enables dexpreopt and sets the BootJars property.
 func FixtureSetBootJars(bootJars ...string) android.FixturePreparer {
 	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index ada4712..94b795f 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -371,6 +371,8 @@
 	// Specifies whether fuzz target should check presubmitted code changes for crashes.
 	// Defaults to false.
 	Use_for_presubmit *bool `json:"use_for_presubmit,omitempty"`
+	// Specify which paths to exclude from fuzzing coverage reports
+	Exclude_paths_from_reports []string `json:"exclude_paths_from_reports,omitempty"`
 }
 
 type FuzzFrameworks struct {
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index c767685..afa52cc 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -56,7 +56,6 @@
 		"RSTest-rscript",
 		"BluetoothGeneratedDumpsysBinarySchema_bfbs",
 		"TracingVMProtoStub_h",
-		"FrontendStub_h",
 		"VehicleServerProtoStub_cc",
 		"AudioFocusControlProtoStub_cc",
 		"AudioFocusControlProtoStub_h",
@@ -98,9 +97,6 @@
 		"BlueberryFacadeGeneratedStub_cc",
 		"BlueberryFacadeGeneratedStub_h",
 		"BluetoothGeneratedDumpsysDataSchema_h",
-		"FrontendStub_cc",
-		"OpenwrtControlServerProto_cc",
-		"OpenwrtControlServerProto_h",
 		"c2hal_test_genc++",
 		"c2hal_test_genc++_headers",
 		"hidl2aidl_test_gen_aidl",
@@ -117,6 +113,28 @@
 		"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",
+		"aidl-golden-test-build-hook-gen",
+		"PacketStreamerStub_h",
+		"FrontendStub_cc",
+		"FrontendStub_h",
+		"PacketStreamerStub_cc",
+		"pixelstatsatoms.h",
+		"pixelatoms_defs.h",
+		"pixelstatsatoms.cpp",
+		"hidl_java_impl_test_gen",
+		"cronet_aml_base_android_runtime_jni_headers__testing",
+		"cronet_aml_base_android_runtime_unchecked_jni_headers__testing",
+		"hidl_cpp_impl_test_gen-sources",
+		"fdt_test_tree_multiple_memory_ranges_dtb",
+		"fdt_test_tree_one_memory_range_dtb",
+		"fdt_test_tree_empty_memory_range_dtb",
+		"ltp_config_arm_64_lowmem",
+		"ltp_config_arm_64_lowmem_hwasan",
+		"ltp_config_x86",
+		"libbssl_sys_src_nostd",
 	}
 
 	SandboxingDenyPathList = []string{
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b29e2c9..69ba1e9 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -151,6 +151,9 @@
 
 	// input files to exclude
 	Exclude_srcs []string `android:"path,arch_variant"`
+
+	// Enable restat to update the output only if the output is changed
+	Write_if_changed *bool
 }
 
 type Module struct {
@@ -293,6 +296,9 @@
 		for _, dir := range g.properties.Export_include_dirs {
 			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
 				android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir))
+			// Also export without ModuleDir for consistency with Export_include_dirs not being set
+			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
+				android.PathForModuleGen(ctx, g.subDir, dir))
 		}
 	} else {
 		g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
@@ -435,6 +441,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 +449,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 {
@@ -467,6 +473,9 @@
 
 		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
 		rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
+		if Bool(g.properties.Write_if_changed) {
+			rule.Restat()
+		}
 		cmd := rule.Command()
 
 		for _, out := range task.out {
@@ -985,6 +994,7 @@
 
 	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 
+	bazelName := m.Name()
 	if ctx.ModuleType() == "gensrcs" {
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "gensrcs",
@@ -1012,7 +1022,6 @@
 				break
 			}
 		}
-		bazelName := m.Name()
 		for _, out := range outs {
 			if out == bazelName {
 				// This is a workaround to circumvent a Bazel warning where a genrule's
@@ -1037,6 +1046,54 @@
 			Tags: tags,
 		}, attrs)
 	}
+
+	if m.needsCcLibraryHeadersBp2build() {
+		includeDirs := make([]string, len(m.properties.Export_include_dirs)*2)
+		for i, dir := range m.properties.Export_include_dirs {
+			includeDirs[i*2] = dir
+			includeDirs[i*2+1] = filepath.Clean(filepath.Join(ctx.ModuleDir(), dir))
+		}
+		attrs := &ccHeaderLibraryAttrs{
+			Hdrs:            []string{":" + bazelName},
+			Export_includes: includeDirs,
+		}
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_library_headers",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
+		}
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+			Name: m.Name() + genruleHeaderLibrarySuffix,
+			Tags: tags,
+		}, attrs)
+
+	}
+}
+
+const genruleHeaderLibrarySuffix = "__header_library"
+
+func (m *Module) needsCcLibraryHeadersBp2build() bool {
+	return len(m.properties.Export_include_dirs) > 0
+}
+
+// GenruleCcHeaderMapper is a bazel.LabelMapper function to map genrules to a cc_library_headers
+// target when they export multiple include directories.
+func GenruleCcHeaderLabelMapper(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+	mod, exists := ctx.ModuleFromName(label.OriginalModuleName)
+	if !exists {
+		return label.Label, false
+	}
+	if m, ok := mod.(*Module); ok {
+		if m.needsCcLibraryHeadersBp2build() {
+			return label.Label + genruleHeaderLibrarySuffix, true
+		}
+	}
+	return label.Label, false
+}
+
+type ccHeaderLibraryAttrs struct {
+	Hdrs []string
+
+	Export_includes []string
 }
 
 var Bool = proptools.Bool
@@ -1090,6 +1147,7 @@
 		}
 	}).(*sandboxingAllowlistSets)
 }
+
 func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder {
 	if !ctx.DeviceConfig().GenruleSandboxing() {
 		return r.SandboxTools()
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 7c17db1..7c14531 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)
 
@@ -811,6 +845,49 @@
 		result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
 }
 
+func TestGenruleInterface(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
+		android.FixtureMergeMockFs(android.MockFS{
+			"package-dir/Android.bp": []byte(`
+				genrule {
+					name: "module-name",
+					cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+					srcs: [
+						"src/foo.proto",
+					],
+					out: ["proto.h", "bar/proto.h"],
+					export_include_dirs: [".", "bar"],
+				}
+			`),
+		}),
+	).RunTest(t)
+
+	exportedIncludeDirs := []string{
+		"out/soong/.intermediates/package-dir/module-name/gen/package-dir",
+		"out/soong/.intermediates/package-dir/module-name/gen",
+		"out/soong/.intermediates/package-dir/module-name/gen/package-dir/bar",
+		"out/soong/.intermediates/package-dir/module-name/gen/bar",
+	}
+	gen := result.Module("module-name", "").(*Module)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"include path",
+		exportedIncludeDirs,
+		gen.GeneratedHeaderDirs(),
+	)
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"files",
+		[]string{
+			"out/soong/.intermediates/package-dir/module-name/gen/proto.h",
+			"out/soong/.intermediates/package-dir/module-name/gen/bar/proto.h",
+		},
+		gen.GeneratedSourceFiles(),
+	)
+}
+
 func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForGenRuleTest,
diff --git a/jar/Android.bp b/jar/Android.bp
index 46113d8..c03e491 100644
--- a/jar/Android.bp
+++ b/jar/Android.bp
@@ -21,6 +21,7 @@
     pkgPath: "android/soong/jar",
     srcs: [
         "jar.go",
+        "services.go",
     ],
     testSrcs: [
         "jar_test.go",
diff --git a/jar/services.go b/jar/services.go
new file mode 100644
index 0000000..d06a6dc
--- /dev/null
+++ b/jar/services.go
@@ -0,0 +1,128 @@
+// 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 jar
+
+import (
+	"android/soong/third_party/zip"
+	"bufio"
+	"hash/crc32"
+	"sort"
+	"strings"
+)
+
+const servicesPrefix = "META-INF/services/"
+
+// Services is used to collect service files from multiple zip files and produce a list of ServiceFiles containing
+// the unique lines from all the input zip entries with the same name.
+type Services struct {
+	services map[string]*ServiceFile
+}
+
+// ServiceFile contains the combined contents of all input zip entries with a single name.
+type ServiceFile struct {
+	Name       string
+	FileHeader *zip.FileHeader
+	Contents   []byte
+	Lines      []string
+}
+
+// IsServiceFile returns true if the zip entry is in the META-INF/services/ directory.
+func (Services) IsServiceFile(entry *zip.File) bool {
+	return strings.HasPrefix(entry.Name, servicesPrefix)
+}
+
+// AddServiceFile adds a zip entry in the META-INF/services/ directory to the list of service files that need
+// to be combined.
+func (j *Services) AddServiceFile(entry *zip.File) error {
+	if j.services == nil {
+		j.services = map[string]*ServiceFile{}
+	}
+
+	service := entry.Name
+	serviceFile := j.services[service]
+	fh := entry.FileHeader
+	if serviceFile == nil {
+		serviceFile = &ServiceFile{
+			Name:       service,
+			FileHeader: &fh,
+		}
+		j.services[service] = serviceFile
+	}
+
+	f, err := entry.Open()
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	scanner := bufio.NewScanner(f)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if line != "" {
+			serviceFile.Lines = append(serviceFile.Lines, line)
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ServiceFiles returns the list of combined service files, each containing all the unique lines from the
+// corresponding service files in the input zip entries.
+func (j *Services) ServiceFiles() []ServiceFile {
+	services := make([]ServiceFile, 0, len(j.services))
+
+	for _, serviceFile := range j.services {
+		serviceFile.Lines = dedupServicesLines(serviceFile.Lines)
+		serviceFile.Lines = append(serviceFile.Lines, "")
+		serviceFile.Contents = []byte(strings.Join(serviceFile.Lines, "\n"))
+
+		serviceFile.FileHeader.UncompressedSize64 = uint64(len(serviceFile.Contents))
+		serviceFile.FileHeader.CRC32 = crc32.ChecksumIEEE(serviceFile.Contents)
+		if serviceFile.FileHeader.Method == zip.Store {
+			serviceFile.FileHeader.CompressedSize64 = serviceFile.FileHeader.UncompressedSize64
+		}
+
+		services = append(services, *serviceFile)
+	}
+
+	sort.Slice(services, func(i, j int) bool {
+		return services[i].Name < services[j].Name
+	})
+
+	return services
+}
+
+func dedupServicesLines(in []string) []string {
+	writeIndex := 0
+outer:
+	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
+			}
+		}
+		if readIndex != writeIndex {
+			in[writeIndex] = in[readIndex]
+		}
+		writeIndex++
+	}
+	return in[0:writeIndex]
+}
diff --git a/java/Android.bp b/java/Android.bp
index e079869..4450c42 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -80,6 +80,7 @@
     ],
     testSrcs: [
         "aar_test.go",
+        "android_manifest_test.go",
         "androidmk_test.go",
         "app_import_test.go",
         "app_set_test.go",
diff --git a/java/aar.go b/java/aar.go
index 29e86e6..180e1d7 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
 }
@@ -89,35 +88,49 @@
 	// do not include AndroidManifest from dependent libraries
 	Dont_merge_manifests *bool
 
+	// If use_resource_processor is set, use Bazel's resource processor instead of aapt2 to generate R.class files.
+	// The resource processor produces more optimal R.class files that only list resources in the package of the
+	// library that provided them, as opposed to aapt2 which produces R.java files for every package containing
+	// every resource.  Using the resource processor can provide significant build time speedups, but requires
+	// fixing the module to use the correct package to reference each resource, and to avoid having any other
+	// libraries in the tree that use the same package name.  Defaults to false, but will default to true in the
+	// future.
+	Use_resource_processor *bool
+
 	// true if RRO is enforced for any of the dependent modules
 	RROEnforcedForDependent bool `blueprint:"mutated"`
 }
 
 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
+	transitiveAaptRJars            android.Paths
+	transitiveAaptResourcePackages android.Paths
+	exportPackage                  android.Path
+	manifestPath                   android.Path
+	proguardOptionsFile            android.Path
+	rTxt                           android.Path
+	rJar                           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 {
@@ -138,20 +151,23 @@
 	}
 }
 
+func (a *aapt) useResourceProcessorBusyBox() bool {
+	return BoolDefault(a.aaptProperties.Use_resource_processor, false)
+}
+
 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) {
@@ -175,8 +191,6 @@
 	// Flags specified in Android.bp
 	linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...)
 
-	linkFlags = append(linkFlags, "--no-static-lib-packages")
-
 	// Find implicit or explicit asset and resource dirs
 	assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
 	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
@@ -291,7 +305,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 +328,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,14 +354,27 @@
 
 	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")
 	}
 
+	if a.isLibrary && a.useResourceProcessorBusyBox() {
+		// When building an android_library using ResourceProcessorBusyBox the resources are merged into
+		// package-res.apk with --merge-only, but --no-static-lib-packages is not used so that R.txt only
+		// contains resources from this library.
+		linkFlags = append(linkFlags, "--merge-only")
+	} else {
+		// When building and app or when building an android_library without ResourceProcessorBusyBox
+		// --no-static-lib-packages is used to put all the resources into the app.  If ResourceProcessorBusyBox
+		// is used then the app's R.txt will be post-processed along with the R.txt files from dependencies to
+		// sort resources into the right packages in R.class.
+		linkFlags = append(linkFlags, "--no-static-lib-packages")
+	}
+
 	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
 	// the subdir "android" is required to be filtered by package names
 	srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar")
@@ -348,6 +382,7 @@
 	rTxt := android.PathForModuleOut(ctx, "R.txt")
 	// This file isn't used by Soong, but is generated for exporting
 	extraPackages := android.PathForModuleOut(ctx, "extra_packages")
+	var transitiveRJars android.Paths
 
 	var compiledResDirs []android.Paths
 	for _, dir := range resDirs {
@@ -363,7 +398,27 @@
 
 	var compiledRes, compiledOverlay android.Paths
 
-	compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
+	// AAPT2 overlays are in lowest to highest priority order, reverse the topological order
+	// of transitiveStaticLibs.
+	transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages())
+
+	if a.isLibrary && a.useResourceProcessorBusyBox() {
+		// When building an android_library with ResourceProcessorBusyBox enabled treat static library dependencies
+		// as imports.  The resources from dependencies will not be merged into this module's package-res.apk, and
+		// instead modules depending on this module will reference package-res.apk from all transitive static
+		// dependencies.
+		for _, staticDep := range staticDeps {
+			linkDeps = append(linkDeps, staticDep.resPackage)
+			linkFlags = append(linkFlags, "-I "+staticDep.resPackage.String())
+			if staticDep.usedResourceProcessor {
+				transitiveRJars = append(transitiveRJars, staticDep.rJar)
+			}
+		}
+	} else {
+		// When building an app or building a library without ResourceProcessorBusyBox enabled all static
+		// dependencies are compiled into this module's package-res.apk as overlays.
+		compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
+	}
 
 	if len(transitiveStaticLibs) > 0 {
 		// If we are using static android libraries, every source file becomes an overlay.
@@ -404,12 +459,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,
@@ -420,21 +481,142 @@
 		a.assetPackage = android.OptionalPathForPath(assets)
 	}
 
+	if a.useResourceProcessorBusyBox() {
+		rJar := android.PathForModuleOut(ctx, "busybox/R.jar")
+		resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary)
+		transitiveRJars = append(transitiveRJars, rJar)
+		a.rJar = rJar
+	}
+
 	a.aaptSrcJar = srcJar
+	a.transitiveAaptRJars = transitiveRJars
+	a.transitiveAaptResourcePackages = staticDeps.resPackages()
 	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,
+			additionalManifests: additionalManifests,
+			rTxt:                a.rTxt,
+			rJar:                a.rJar,
+			assets:              a.assetPackage,
+
+			usedResourceProcessor: a.useResourceProcessorBusyBox(),
+		}).
+		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).
+		DirectSlice(additionalManifests).
+		Transitive(staticManifestsDepSet).Build()
+}
+
+var resourceProcessorBusyBox = pctx.AndroidStaticRule("resourceProcessorBusyBox",
+	blueprint.RuleParams{
+		Command: "${config.JavaCmd} -cp ${config.ResourceProcessorBusyBox} " +
+			"com.google.devtools.build.android.ResourceProcessorBusyBox --tool=GENERATE_BINARY_R -- @${out}.args && " +
+			"if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out}; fi",
+		CommandDeps:    []string{"${config.ResourceProcessorBusyBox}"},
+		Rspfile:        "${out}.args",
+		RspfileContent: "--primaryRTxt ${rTxt} --primaryManifest ${manifest} --classJarOutput ${out}.tmp ${args}",
+		Restat:         true,
+	}, "rTxt", "manifest", "args")
+
+// resourceProcessorBusyBoxGenerateBinaryR converts the R.txt file produced by aapt2 into R.class files
+// using Bazel's ResourceProcessorBusyBox tool, which is faster than compiling the R.java files and
+// supports producing classes for static dependencies that only include resources from that dependency.
+func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, manifest android.Path,
+	rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool) {
+
+	var args []string
+	var deps android.Paths
+
+	if !isLibrary {
+		// When compiling an app, pass all R.txt and AndroidManifest.xml from transitive static library dependencies
+		// to ResourceProcessorBusyBox so that it can regenerate R.class files with the final resource IDs for each
+		// package.
+		args, deps = transitiveDeps.resourceProcessorDeps()
+	} else {
+		// When compiling a library don't pass any dependencies as it only needs to generate an R.class file for this
+		// library.  Pass --finalFields=false so that the R.class file contains non-final fields so they don't get
+		// inlined into the library before the final IDs are assigned during app compilation.
+		args = append(args, "--finalFields=false")
+	}
+
+	deps = append(deps, rTxt, manifest)
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        resourceProcessorBusyBox,
+		Output:      rJar,
+		Implicits:   deps,
+		Description: "ResourceProcessorBusyBox",
+		Args: map[string]string{
+			"rTxt":     rTxt.String(),
+			"manifest": manifest.String(),
+			"args":     strings.Join(args, " "),
+		},
+	})
+}
+
+type resourcesNode struct {
+	resPackage          android.Path
+	manifest            android.Path
+	additionalManifests android.Paths
+	rTxt                android.Path
+	rJar                android.Path
+	assets              android.OptionalPath
+
+	usedResourceProcessor bool
+}
+
+type transitiveAarDeps []*resourcesNode
+
+func (t transitiveAarDeps) resPackages() android.Paths {
+	paths := make(android.Paths, 0, len(t))
+	for _, dep := range t {
+		paths = append(paths, dep.resPackage)
+	}
+	return paths
+}
+
+func (t transitiveAarDeps) manifests() android.Paths {
+	paths := make(android.Paths, 0, len(t))
+	for _, dep := range t {
+		paths = append(paths, dep.manifest)
+		paths = append(paths, dep.additionalManifests...)
+	}
+	return paths
+}
+
+func (t transitiveAarDeps) resourceProcessorDeps() (args []string, deps android.Paths) {
+	for _, dep := range t {
+		args = append(args, "--library="+dep.rTxt.String()+","+dep.manifest.String())
+		deps = append(deps, dep.rTxt, dep.manifest)
+	}
+	return args, deps
+}
+
+func (t transitiveAarDeps) assets() android.Paths {
+	paths := make(android.Paths, 0, len(t))
+	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 +629,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 +655,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 +684,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 +695,6 @@
 	androidLibraryProperties androidLibraryProperties
 
 	aarFile android.WritablePath
-
-	exportedStaticPackages android.Paths
 }
 
 var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
@@ -532,10 +709,6 @@
 	}
 }
 
-func (a *AndroidLibrary) ExportedStaticPackages() android.Paths {
-	return a.exportedStaticPackages
-}
-
 var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
 
 func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -554,9 +727,14 @@
 
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
 
-	ctx.CheckbuildFile(a.proguardOptionsFile)
-	ctx.CheckbuildFile(a.exportPackage)
-	ctx.CheckbuildFile(a.aaptSrcJar)
+	a.stem = proptools.StringDefault(a.overridableDeviceProperties.Stem, ctx.ModuleName())
+
+	ctx.CheckbuildFile(a.aapt.proguardOptionsFile)
+	ctx.CheckbuildFile(a.aapt.exportPackage)
+	ctx.CheckbuildFile(a.aapt.aaptSrcJar)
+	if a.useResourceProcessorBusyBox() {
+		ctx.CheckbuildFile(a.aapt.rJar)
+	}
 
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
@@ -568,7 +746,22 @@
 	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles,
 		a.proguardOptionsFile)
 
-	a.Module.compile(ctx, a.aaptSrcJar)
+	var extraSrcJars android.Paths
+	var extraCombinedJars android.Paths
+	var extraClasspathJars android.Paths
+	if a.useResourceProcessorBusyBox() {
+		// When building a library with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox for this
+		// library and each of the transitive static android_library dependencies has already created an
+		// R.class file for the appropriate package.  Add all of those R.class files to the classpath.
+		extraClasspathJars = a.transitiveAaptRJars
+	} else {
+		// When building a library without ResourceProcessorBusyBox the aapt2 rule creates R.srcjar containing
+		// R.java files for the library's package and the packages from all transitive static android_library
+		// dependencies.  Compile the srcjar alongside the rest of the sources.
+		extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
+	}
+
+	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
 
 	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
 	var res android.Paths
@@ -579,19 +772,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) {
@@ -674,14 +863,18 @@
 
 	properties AARImportProperties
 
-	classpathFile         android.WritablePath
-	proguardFlags         android.WritablePath
-	exportPackage         android.WritablePath
-	extraAaptPackagesFile android.WritablePath
-	manifest              android.WritablePath
-	assetsPackage         android.WritablePath
+	classpathFile                  android.WritablePath
+	proguardFlags                  android.WritablePath
+	exportPackage                  android.WritablePath
+	transitiveAaptResourcePackages android.Paths
+	extraAaptPackagesFile          android.WritablePath
+	manifest                       android.WritablePath
+	assetsPackage                  android.WritablePath
+	rTxt                           android.WritablePath
+	rJar                           android.WritablePath
 
-	exportedStaticPackages android.Paths
+	resourcesNodesDepSet *android.DepSet[*resourcesNode]
+	manifestsDepSet      *android.DepSet[android.Path]
 
 	hideApexVariantFromMake bool
 
@@ -738,25 +931,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
@@ -852,12 +1040,13 @@
 	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
 	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
+	aarRTxt := extractedAARDir.Join(ctx, "R.txt")
 	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage},
+		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage, aarRTxt},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
@@ -877,46 +1066,70 @@
 	// the subdir "android" is required to be filtered by package names
 	srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar")
 	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
-	rTxt := android.PathForModuleOut(ctx, "R.txt")
+	a.rTxt = android.PathForModuleOut(ctx, "R.txt")
 	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
 
 	var linkDeps android.Paths
 
 	linkFlags := []string{
 		"--static-lib",
-		"--no-static-lib-packages",
+		"--merge-only",
 		"--auto-add-overlay",
 	}
 
 	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...)
+	linkDeps = append(linkDeps, sharedLibs...)
+	linkDeps = append(linkDeps, staticDeps.resPackages()...)
 	linkFlags = append(linkFlags, libFlags...)
 
-	overlayRes := append(android.Paths{flata}, transitiveStaticLibs...)
+	overlayRes := android.Paths{flata}
 
-	aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
+	// Treat static library dependencies of static libraries as imports.
+	transitiveStaticLibs := staticDeps.resPackages()
+	linkDeps = append(linkDeps, transitiveStaticLibs...)
+	for _, staticLib := range transitiveStaticLibs {
+		linkFlags = append(linkFlags, "-I "+staticLib.String())
+	}
+
+	transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets())
+	aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, a.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
-	}
+	a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar")
+	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true)
+
+	resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL)
+	resourcesNodesDepSetBuilder.Direct(&resourcesNode{
+		resPackage: a.exportPackage,
+		manifest:   a.manifest,
+		rTxt:       a.rTxt,
+		rJar:       a.rJar,
+		assets:     android.OptionalPathForPath(a.assetsPackage),
+
+		usedResourceProcessor: true,
+	})
+	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.transitiveAaptResourcePackages = staticDeps.resPackages()
 
 	a.collectTransitiveHeaderJars(ctx)
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
@@ -925,6 +1138,7 @@
 		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
 		ImplementationJars:             android.PathsIfNonNil(a.classpathFile),
+		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
 
 	if proptools.Bool(a.properties.Extract_jni) {
diff --git a/java/android_manifest_test.go b/java/android_manifest_test.go
new file mode 100644
index 0000000..b12d778
--- /dev/null
+++ b/java/android_manifest_test.go
@@ -0,0 +1,103 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestManifestMerger(t *testing.T) {
+	bp := `
+		android_app {
+			name: "app",
+			sdk_version: "current",
+			srcs: ["app/app.java"],
+			resource_dirs: ["app/res"],
+			manifest: "app/AndroidManifest.xml",
+			additional_manifests: ["app/AndroidManifest2.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",
+			additional_manifests: ["direct/AndroidManifest2.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",
+			additional_manifests: ["transitive/AndroidManifest2.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"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithOverlayBuildComponents,
+	).RunTestWithBp(t, bp)
+
+	manifestMergerRule := result.ModuleForTests("app", "android_common").Rule("manifestMerger")
+	android.AssertPathRelativeToTopEquals(t, "main manifest",
+		"out/soong/.intermediates/app/android_common/manifest_fixer/AndroidManifest.xml",
+		manifestMergerRule.Input)
+	android.AssertPathsRelativeToTopEquals(t, "lib manifests",
+		[]string{
+			"app/AndroidManifest2.xml",
+			"out/soong/.intermediates/direct/android_common/manifest_fixer/AndroidManifest.xml",
+			"direct/AndroidManifest2.xml",
+			"out/soong/.intermediates/transitive/android_common/manifest_fixer/AndroidManifest.xml",
+			"transitive/AndroidManifest2.xml",
+			"out/soong/.intermediates/transitive_import/android_common/aar/AndroidManifest.xml",
+			"out/soong/.intermediates/direct_import/android_common/aar/AndroidManifest.xml",
+			// TODO(b/288358614): Soong has historically not merged manifests from dependencies of
+			// android_library_import modules.
+
+		},
+		manifestMergerRule.Implicits)
+}
diff --git a/java/androidmk.go b/java/androidmk.go
index 9c21633..82505e9 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -123,6 +123,8 @@
 					if library.dexpreopter.configPath != nil {
 						entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
 					}
+
+					entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", library.getTransitiveAconfigFiles().ToList())
 				},
 			},
 		})
@@ -220,6 +222,7 @@
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
 				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
+				// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 			},
 		},
 	}}
@@ -244,6 +247,7 @@
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled)
 				}
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
+				// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 			},
 		},
 	}}
@@ -265,10 +269,12 @@
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
 				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", prebuilt.exportPackage)
+				entries.SetPaths("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", prebuilt.transitiveAaptResourcePackages)
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
 				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
 				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
+				// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 			},
 		},
 	}}
@@ -295,6 +301,7 @@
 					if len(binary.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
+					entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", binary.getTransitiveAconfigFiles().ToList())
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -340,6 +347,9 @@
 				// App module names can be overridden.
 				entries.SetString("LOCAL_MODULE", app.installApkName)
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall)
+				if app.headerJarFile != nil {
+					entries.SetPath("LOCAL_SOONG_HEADER_JAR", app.headerJarFile)
+				}
 				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage)
 				if app.dexJarFile.IsSet() {
 					entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile.Path())
@@ -368,8 +378,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)
 						}
 					}
@@ -432,6 +447,10 @@
 				}
 
 				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
+
+				if app.Name() != "framework-res" {
+					entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", app.getTransitiveAconfigFiles().ToList())
+				}
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -450,11 +469,6 @@
 	if len(a.overridableAppProperties.Overrides) > 0 {
 		overridden = append(overridden, a.overridableAppProperties.Overrides...)
 	}
-	// When APK name is overridden via PRODUCT_PACKAGE_NAME_OVERRIDES
-	// ensure that the original name is overridden.
-	if a.Stem() != a.installApkName {
-		overridden = append(overridden, a.Stem())
-	}
 	return overridden
 }
 
@@ -508,10 +522,12 @@
 		}
 
 		entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.exportPackage)
+		entries.SetPaths("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", a.transitiveAaptResourcePackages)
 		entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", a.extraAaptPackagesFile)
 		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
 		entries.AddStrings("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.exportedProguardFlagFiles.Strings()...)
 		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
+		entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", a.getTransitiveAconfigFiles().ToList())
 	})
 
 	return entriesList
@@ -684,6 +700,7 @@
 				if Bool(a.properties.Export_package_resources) {
 					entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.outputFile)
 				}
+				// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 			},
 		},
 	}}
@@ -717,6 +734,7 @@
 				entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString())
 				entries.SetPath("LOCAL_MODULE_PATH", r.installDir)
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...)
+				// TODO: LOCAL_ACONFIG_FILES -- Might eventually need aconfig flags?
 			},
 		},
 	}}
@@ -734,6 +752,7 @@
 					entries.SetPath("LOCAL_APK_SET_INSTALL_FILE", apkSet.PackedAdditionalOutputs())
 					entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
 					entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
+					// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts -- Both declarations and values
 				},
 			},
 		},
diff --git a/java/app.go b/java/app.go
index d9272e4..224bc88 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 {
@@ -521,7 +521,23 @@
 	a.dexpreopter.preventInstall = a.appProperties.PreventInstall
 
 	if ctx.ModuleName() != "framework-res" {
-		a.Module.compile(ctx, a.aaptSrcJar)
+		var extraSrcJars android.Paths
+		var extraClasspathJars android.Paths
+		var extraCombinedJars android.Paths
+		if a.useResourceProcessorBusyBox() {
+			// When building an app with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox has already
+			// created R.class files that provide IDs for resources in busybox/R.jar.  Pass that file in the
+			// classpath when compiling everything else, and add it to the final classes jar.
+			extraClasspathJars = android.Paths{a.aapt.rJar}
+			extraCombinedJars = android.Paths{a.aapt.rJar}
+		} else {
+			// When building an app without ResourceProcessorBusyBox the aapt2 rule creates R.srcjar containing
+			// R.java files for the app's package and the packages from all transitive static android_library
+			// dependencies.  Compile the srcjar alongside the rest of the sources.
+			extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
+		}
+
+		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
 	}
 
 	return a.dexJarFile.PathOrNil()
@@ -666,8 +682,17 @@
 	a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
 	a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
 
+	// Unlike installApkName, a.stem should respect base module name for override_android_app.
+	// Therefore, use ctx.ModuleName() instead of a.Name().
+	a.stem = proptools.StringDefault(a.overridableDeviceProperties.Stem, ctx.ModuleName())
+
 	// Check if the install APK name needs to be overridden.
-	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Stem())
+	// Both android_app and override_android_app module are expected to possess
+	// its module bound apk path. However, override_android_app inherits ctx.ModuleName()
+	// from the base module. Therefore, use a.Name() which represents
+	// the module name for both android_app and override_android_app.
+	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(
+		proptools.StringDefault(a.overridableDeviceProperties.Stem, a.Name()))
 
 	if ctx.ModuleName() == "framework-res" {
 		// framework-res.apk is installed as system/framework/framework-res.apk
@@ -1571,7 +1596,9 @@
 }
 
 type manifestValueAttribute struct {
-	MinSdkVersion *string
+	MinSdkVersion    *string
+	MaxSdkVersion    *string
+	TargetSdkVersion *string
 }
 
 type bazelAndroidAppAttributes struct {
@@ -1601,12 +1628,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_builder.go b/java/app_builder.go
index d20a6bf..d397ff7 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -225,8 +225,6 @@
 	})
 }
 
-const jniJarOutputPathString = "jniJarOutput.zip"
-
 func TransformJniLibsToJar(
 	ctx android.ModuleContext,
 	outputFile android.WritablePath,
@@ -258,7 +256,10 @@
 		rule = zipRE
 		args["implicits"] = strings.Join(deps.Strings(), ",")
 	}
-	jniJarPath := android.PathForModuleOut(ctx, jniJarOutputPathString)
+	var jniJarPath android.WritablePath = android.PathForModuleOut(ctx, "jniJarOutput.zip")
+	if len(prebuiltJniPackages) == 0 {
+		jniJarPath = outputFile
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: "zip jni libs",
@@ -266,12 +267,26 @@
 		Implicits:   deps,
 		Args:        args,
 	})
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        mergeAssetsRule,
-		Description: "merge prebuilt JNI packages",
-		Inputs:      append(prebuiltJniPackages, jniJarPath),
-		Output:      outputFile,
-	})
+	if len(prebuiltJniPackages) > 0 {
+		var mergeJniJarPath android.WritablePath = android.PathForModuleOut(ctx, "mergeJniJarOutput.zip")
+		if !uncompressJNI {
+			mergeJniJarPath = outputFile
+		}
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        mergeAssetsRule,
+			Description: "merge prebuilt JNI packages",
+			Inputs:      append(prebuiltJniPackages, jniJarPath),
+			Output:      mergeJniJarPath,
+		})
+
+		if uncompressJNI {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   uncompressEmbeddedJniLibsRule,
+				Input:  mergeJniJarPath,
+				Output: outputFile,
+			})
+		}
+	}
 }
 
 func (a *AndroidApp) generateJavaUsedByApex(ctx android.ModuleContext) {
diff --git a/java/app_import.go b/java/app_import.go
index 9c01960..ad1765e 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"
@@ -50,9 +51,9 @@
 		Description: "Uncompress dex files",
 	})
 
-	checkJniAndDexLibsAreUncompressedRule = pctx.AndroidStaticRule("check-jni-and-dex-libs-are-uncompressed", blueprint.RuleParams{
+	checkDexLibsAreUncompressedRule = pctx.AndroidStaticRule("check-dex-libs-are-uncompressed", blueprint.RuleParams{
 		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
-		Command: "if (zipinfo $in 'lib/*.so' '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
+		Command: "if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
 			"echo $in: Contains compressed JNI libraries and/or dex files >&2;" +
 			"exit 1; " +
 			"else " +
@@ -60,6 +61,17 @@
 			"fi",
 		Description: "Check for compressed JNI libs or dex files",
 	})
+
+	checkJniLibsAreUncompressedRule = pctx.AndroidStaticRule("check-jni-libs-are-uncompressed", blueprint.RuleParams{
+		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
+		Command: "if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
+			"echo $in: Contains compressed JNI libraries >&2;" +
+			"exit 1; " +
+			"else " +
+			"touch $out; " +
+			"fi",
+		Description: "Check for compressed JNI libs or dex files",
+	})
 )
 
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -334,11 +346,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
@@ -375,26 +395,40 @@
 }
 
 func (a *AndroidAppImport) validatePreprocessedApk(ctx android.ModuleContext, srcApk android.Path, dstApk android.WritablePath) {
+	var validations android.Paths
+
 	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "alignment.stamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   checkZipAlignment,
 		Input:  srcApk,
 		Output: alignmentStamp,
 	})
-	compressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "compression.stamp")
+
+	validations = append(validations, alignmentStamp)
+	jniCompressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "jni_compression.stamp")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   checkJniAndDexLibsAreUncompressedRule,
+		Rule:   checkJniLibsAreUncompressedRule,
 		Input:  srcApk,
-		Output: compressionStamp,
+		Output: jniCompressionStamp,
 	})
+	validations = append(validations, jniCompressionStamp)
+
+	if a.Privileged() {
+		// It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
+		dexCompressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "dex_compression.stamp")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   checkDexLibsAreUncompressedRule,
+			Input:  srcApk,
+			Output: dexCompressionStamp,
+		})
+		validations = append(validations, dexCompressionStamp)
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
-		Input:  srcApk,
-		Output: dstApk,
-		Validations: []android.Path{
-			alignmentStamp,
-			compressionStamp,
-		},
+		Rule:        android.Cp,
+		Input:       srcApk,
+		Output:      dstApk,
+		Validations: validations,
 	})
 }
 
@@ -410,6 +444,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 0f98416..8474ea7 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,514 @@
 	}
 }
 
-func TestAndroidResources(t *testing.T) {
+func TestAndroidResourceProcessor(t *testing.T) {
+	testCases := []struct {
+		name                string
+		appUsesRP           bool
+		directLibUsesRP     bool
+		transitiveLibUsesRP bool
+
+		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
+	}{
+		{
+			// Test with all modules set to use_resource_processor: false (except android_library_import modules,
+			// which always use resource processor).
+			name:                "legacy",
+			appUsesRP:           false,
+			directLibUsesRP:     false,
+			transitiveLibUsesRP: false,
+
+			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"},
+			directImportImports: []string{
+				"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
+				"out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
+			},
+
+			transitiveImportResources: nil,
+			transitiveImportOverlays:  []string{"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata"},
+			transitiveImportImports: []string{
+				"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+			},
+		},
+		{
+			// Test with all modules set to use_resource_processor: true.
+			name:                "resource_processor",
+			appUsesRP:           true,
+			directLibUsesRP:     true,
+			transitiveLibUsesRP: true,
+
+			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: nil,
+			appClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/app/android_common/busybox/R.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/busybox/R.jar",
+				"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/direct/android_common/aapt2/direct/res/values_strings.arsc.flat"},
+			directImports: []string{
+				"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive/android_common/package-res.apk",
+			},
+			directSrcJars: nil,
+			directClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
+				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
+				"out/soong/.intermediates/direct/android_common/busybox/R.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:   nil,
+			transitiveClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
+			},
+			transitiveCombined: nil,
+
+			directImportResources: nil,
+			directImportOverlays:  []string{"out/soong/.intermediates/direct_import/android_common/flat-res/gen_res.flata"},
+			directImportImports: []string{
+				"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
+				"out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
+			},
+
+			transitiveImportResources: nil,
+			transitiveImportOverlays:  []string{"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata"},
+			transitiveImportImports: []string{
+				"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+			},
+		}, {
+			// Test an app building with resource processor enabled but with dependencies built without
+			// resource processor.
+			name:                "app_resource_processor",
+			appUsesRP:           true,
+			directLibUsesRP:     false,
+			transitiveLibUsesRP: false,
+
+			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: nil,
+			appClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				// R.jar has to come before direct.jar
+				"out/soong/.intermediates/app/android_common/busybox/R.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/busybox/R.jar",
+				"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",
+			},
+
+			dontVerifyDirect:           true,
+			dontVerifyTransitive:       true,
+			dontVerifyDirectImport:     true,
+			dontVerifyTransitiveImport: true,
+		},
+		{
+			// Test an app building without resource processor enabled but with a dependency built with
+			// resource processor.
+			name:                "app_dependency_lib_resource_processor",
+			appUsesRP:           false,
+			directLibUsesRP:     true,
+			transitiveLibUsesRP: false,
+
+			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/direct/android_common/aapt2/direct/res/values_strings.arsc.flat"},
+			directImports: []string{
+				"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
+				"out/soong/.intermediates/transitive/android_common/package-res.apk",
+			},
+			directSrcJars: nil,
+			directClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
+				"out/soong/.intermediates/direct/android_common/busybox/R.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",
+			},
+
+			dontVerifyTransitive:       true,
+			dontVerifyDirectImport:     true,
+			dontVerifyTransitiveImport: true,
+		},
+		{
+			// Test a library building without resource processor enabled but with a dependency built with
+			// resource processor.
+			name:                "lib_dependency_lib_resource_processor",
+			appUsesRP:           false,
+			directLibUsesRP:     false,
+			transitiveLibUsesRP: true,
+
+			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:   nil,
+			transitiveClasspath: []string{
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
+			},
+			transitiveCombined: nil,
+
+			dontVerifyDirectImport:     true,
+			dontVerifyTransitiveImport: true,
+		},
+	}
+
+	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"],
+					use_resource_processor: %v,
+				}
+
+				android_library {
+					name: "direct",
+					sdk_version: "current",
+					srcs: ["direct/direct.java"],
+					resource_dirs: ["direct/res"],
+					manifest: "direct/AndroidManifest.xml",
+					static_libs: ["transitive", "transitive_import"],
+					use_resource_processor: %v,
+				}
+
+				android_library {
+					name: "transitive",
+					sdk_version: "current",
+					srcs: ["transitive/transitive.java"],
+					resource_dirs: ["transitive/res"],
+					manifest: "transitive/AndroidManifest.xml",
+					use_resource_processor: %v,
+				}
+
+				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"],
+				}
+			`, testCase.appUsesRP, testCase.directLibUsesRP, testCase.transitiveLibUsesRP)
+
+			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 +1453,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:"
@@ -1232,7 +1742,7 @@
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.Output(jniJarOutputPathString)
+			jniLibZip := app.Output("jnilibs.zip")
 			var abis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
 			for i := 0; i < len(args); i++ {
@@ -1365,7 +1875,7 @@
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.MaybeOutput(jniJarOutputPathString)
+			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
 				t.Errorf("expected jni packaged %v, got %v", w, g)
 			}
@@ -1456,7 +1966,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
 
-			jniLibZip := app.MaybeOutput(jniJarOutputPathString)
+			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if len(jniLibZip.Implicits) != 1 {
 				t.Fatalf("expected exactly one jni library, got %q", jniLibZip.Implicits.Strings())
 			}
@@ -2476,7 +2986,7 @@
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.Output(jniJarOutputPathString)
+			jniLibZip := app.Output("jnilibs.zip")
 			var jnis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
 			for i := 0; i < len(args); i++ {
diff --git a/java/base.go b/java/base.go
index f2ad5c2..f5eb01c 100644
--- a/java/base.go
+++ b/java/base.go
@@ -21,6 +21,7 @@
 	"strings"
 
 	"android/soong/ui/metrics/bp2build_metrics_proto"
+
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
@@ -500,6 +501,21 @@
 	maxSdkVersion android.ApiLevel
 
 	sourceExtensions []string
+
+	annoSrcJars android.Paths
+
+	// output file name based on Stem property.
+	// This should be set in every ModuleWithStem's GenerateAndroidBuildActions
+	// or the module should override Stem().
+	stem string
+
+	// Aconfig "cache files" that went directly into this module.  Transitive ones are
+	// tracked via JavaInfo.TransitiveAconfigFiles
+	// TODO: Extract to something standalone to propagate tags via GeneratedJavaLibraryModule
+	aconfigIntermediates android.Paths
+
+	// Aconfig files for all transitive deps.  Also exposed via JavaInfo
+	transitiveAconfigFiles *android.DepSet[android.Path]
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -1049,7 +1065,7 @@
 	module.properties.Generated_srcjars = append(module.properties.Generated_srcjars, path)
 }
 
-func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
+func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) {
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
 	deps := j.collectDeps(ctx)
@@ -1087,9 +1103,7 @@
 
 	srcJars := srcFiles.FilterByExt(".srcjar")
 	srcJars = append(srcJars, deps.srcJars...)
-	if aaptSrcJar != nil {
-		srcJars = append(srcJars, aaptSrcJar)
-	}
+	srcJars = append(srcJars, extraSrcJars...)
 	srcJars = append(srcJars, j.properties.Generated_srcjars...)
 	srcFiles = srcFiles.FilterOutByExt(".srcjar")
 
@@ -1097,7 +1111,7 @@
 		j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
 	}
 
-	jarName := ctx.ModuleName() + ".jar"
+	jarName := j.Stem() + ".jar"
 
 	var uniqueJavaFiles android.Paths
 	set := make(map[string]bool)
@@ -1132,6 +1146,11 @@
 	var kotlinJars android.Paths
 	var kotlinHeaderJars android.Paths
 
+	// Prepend extraClasspathJars to classpath so that the resource processor R.jar comes before
+	// any dependencies so that it can override any non-final R classes from dependencies with the
+	// final R classes from the app.
+	flags.classpath = append(android.CopyOf(extraClasspathJars), flags.classpath...)
+
 	if srcFiles.HasExt(".kt") {
 		// When using kotlin sources turbine is used to generate annotation processor sources,
 		// including for annotation processors that generate API, so we can use turbine for
@@ -1225,8 +1244,9 @@
 			// allow for the use of annotation processors that do function correctly
 			// with sharding enabled. See: b/77284273.
 		}
+		extraJars := append(android.CopyOf(extraCombinedJars), kotlinHeaderJars...)
 		headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
-			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
+			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars)
 		if ctx.Failed() {
 			return
 		}
@@ -1255,8 +1275,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)
@@ -1376,6 +1397,8 @@
 		jars = append(jars, servicesJar)
 	}
 
+	jars = append(android.CopyOf(extraCombinedJars), jars...)
+
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes.jar. If there is only one input jar this step will be skipped.
 	var outputFile android.OutputPath
@@ -1468,7 +1491,13 @@
 
 	j.implementationJarFile = outputFile
 	if j.headerJarFile == nil {
-		j.headerJarFile = j.implementationJarFile
+		// If this module couldn't generate a header jar (for example due to api generating annotation processors)
+		// then use the implementation jar.  Run it through zip2zip first to remove any files in META-INF/services
+		// so that javac on modules that depend on this module don't pick up annotation processors (which may be
+		// missing their implementations) from META-INF/services/javax.annotation.processing.Processor.
+		headerJarFile := android.PathForModuleOut(ctx, "javac-header", jarName)
+		convertImplementationJarToHeaderJar(ctx, j.implementationJarFile, headerJarFile)
+		j.headerJarFile = headerJarFile
 	}
 
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
@@ -1614,6 +1643,8 @@
 
 	ctx.CheckbuildFile(outputFile)
 
+	j.collectTransitiveAconfigFiles(ctx)
+
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
@@ -1628,6 +1659,7 @@
 		ExportedPluginClasses:          j.exportedPluginClasses,
 		ExportedPluginDisableTurbine:   j.exportedDisableTurbine,
 		JacocoReportClassesFile:        j.jacocoReportClassesFile,
+		TransitiveAconfigFiles:         j.transitiveAconfigFiles,
 	})
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -1657,13 +1689,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 +1705,10 @@
 		j.kytheFiles = append(j.kytheFiles, extractionFile)
 	}
 
+	if len(flags.processorPath) > 0 {
+		j.annoSrcJars = append(j.annoSrcJars, annoSrcJar)
+	}
+
 	return classes
 }
 
@@ -1850,6 +1888,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 {
@@ -1887,7 +1926,10 @@
 }
 
 func (j *Module) Stem() string {
-	return proptools.StringDefault(j.overridableDeviceProperties.Stem, j.Name())
+	if j.stem == "" {
+		panic("Stem() called before stem property was set")
+	}
+	return j.stem
 }
 
 func (j *Module) JacocoReportClassesFile() android.Path {
@@ -1898,6 +1940,34 @@
 	return Bool(j.properties.Installable)
 }
 
+func (j *Module) collectTransitiveAconfigFiles(ctx android.ModuleContext) {
+	// Aconfig files from this module
+	mine := j.aconfigIntermediates
+
+	// Aconfig files from transitive dependencies
+	fromDeps := []*android.DepSet[android.Path]{}
+	ctx.VisitDirectDeps(func(module android.Module) {
+		dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		if dep.TransitiveAconfigFiles != nil {
+			fromDeps = append(fromDeps, dep.TransitiveAconfigFiles)
+		}
+	})
+
+	// DepSet containing aconfig files myself and from dependencies
+	j.transitiveAconfigFiles = android.NewDepSet(android.POSTORDER, mine, fromDeps)
+}
+
+func (j *Module) AddAconfigIntermediate(path android.Path) {
+	j.aconfigIntermediates = append(j.aconfigIntermediates, path)
+}
+
+func (j *Module) getTransitiveAconfigFiles() *android.DepSet[android.Path] {
+	if j.transitiveAconfigFiles == nil {
+		panic(fmt.Errorf("java.Moduile: getTransitiveAconfigFiles called before collectTransitiveAconfigFiles module=%s", j.Name()))
+	}
+	return j.transitiveAconfigFiles
+}
+
 type sdkLinkType int
 
 const (
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index f4cef7f..c7dc3af 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},
@@ -185,6 +185,9 @@
 // The tag used for dependencies onto bootclasspath_fragments.
 var bootclasspathFragmentDepTag = bootclasspathDependencyTag{name: "fragment"}
 
+// The tag used for dependencies onto platform_bootclasspath.
+var platformBootclasspathDepTag = bootclasspathDependencyTag{name: "platform"}
+
 // BootclasspathNestedAPIProperties defines properties related to the API provided by parts of the
 // bootclasspath that are nested within the main BootclasspathAPIProperties.
 type BootclasspathNestedAPIProperties struct {
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 50429b0..dcc2dec 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -242,7 +242,7 @@
 	modulePaths []string
 
 	// Path to the boot image profile.
-	profilePath android.Path
+	profilePath android.WritablePath
 }
 
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -256,16 +256,6 @@
 	// versioned sdk.
 	produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
 
-	// produceBootImageFiles will attempt to produce rules to create the boot image files at the paths
-	// predefined in the bootImageConfig.
-	//
-	// If it could not create the files then it will return nil. Otherwise, it will return a map from
-	// android.ArchType to the predefined paths of the boot image files.
-	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs
-
-	// getImageName returns the `image_name` property of this fragment.
-	getImageName() *string
-
 	// getProfilePath returns the path to the boot image profile.
 	getProfilePath() android.Path
 }
@@ -295,9 +285,6 @@
 				return
 			}
 		}
-
-		// Initialize the contents property from the image_name.
-		bootclasspathFragmentInitContentsFromImage(ctx, m)
 	})
 	return m
 }
@@ -308,9 +295,7 @@
 	return m
 }
 
-// bootclasspathFragmentInitContentsFromImage will initialize the contents property from the image_name if
-// necessary.
-func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, m *BootclasspathFragmentModule) {
+func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.EarlyModuleContext) {
 	contents := m.properties.Contents
 	if len(contents) == 0 {
 		ctx.PropertyErrorf("contents", "required property is missing")
@@ -332,6 +317,18 @@
 	// too early in the Soong processing for that to work.
 	global := dexpreopt.GetGlobalConfig(ctx)
 	modules := global.ArtApexJars
+	configuredJars := modules.CopyOfJars()
+
+	// Skip the check if the configured jars list is empty as that is a common configuration when
+	// building targets that do not result in a system image.
+	if len(configuredJars) == 0 {
+		return
+	}
+
+	if !reflect.DeepEqual(configuredJars, contents) {
+		ctx.ModuleErrorf("inconsistency in specification of contents. ArtApexJars configuration specifies %#v, contents property specifies %#v",
+			configuredJars, contents)
+	}
 
 	// Make sure that the apex specified in the configuration is consistent and is one for which
 	// this boot image is available.
@@ -357,42 +354,11 @@
 	}
 }
 
-// bootclasspathImageNameContentsConsistencyCheck checks that the configuration that applies to this
-// module (if any) matches the contents.
-//
-// This should be a noop as if image_name="art" then the contents will be set from the ArtApexJars
-// config by bootclasspathFragmentInitContentsFromImage so it will be guaranteed to match. However,
-// in future this will not be the case.
-func (b *BootclasspathFragmentModule) bootclasspathImageNameContentsConsistencyCheck(ctx android.BaseModuleContext) {
-	imageName := proptools.String(b.properties.Image_name)
-	if imageName == "art" {
-		// Get the configuration for the art apex jars.
-		modules := b.getImageConfig(ctx).modules
-		configuredJars := modules.CopyOfJars()
-
-		// Skip the check if the configured jars list is empty as that is a common configuration when
-		// building targets that do not result in a system image.
-		if len(configuredJars) == 0 {
-			return
-		}
-
-		contents := b.properties.Contents
-		if !reflect.DeepEqual(configuredJars, contents) {
-			ctx.ModuleErrorf("inconsistency in specification of contents. ArtApexJars configuration specifies %#v, contents property specifies %#v",
-				configuredJars, contents)
-		}
-	}
-}
-
 var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider(BootclasspathFragmentApexContentInfo{})
 
 // 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 +371,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.
@@ -503,7 +465,7 @@
 	// unused prebuilt that was created without instrumentation from breaking an instrumentation
 	// build.
 	if isActiveModule(ctx.Module()) {
-		b.bootclasspathImageNameContentsConsistencyCheck(ctx)
+		b.bootclasspathFragmentPropertyCheck(ctx)
 	}
 
 	// Generate classpaths.proto config
@@ -523,34 +485,15 @@
 
 	fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
 
-	// Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a
-	// prebuilt which will not use the image config.
-	imageConfig := b.getImageConfig(ctx)
-
 	// Perform hidden API processing.
 	hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
 
-	var bootImageFiles bootImageOutputs
-	if imageConfig != nil {
-		// Delegate the production of the boot image files to a module type specific method.
-		common := ctx.Module().(commonBootclasspathFragment)
-		bootImageFiles = common.produceBootImageFiles(ctx, imageConfig)
-		b.profilePath = bootImageFiles.profile
-
-		if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
-			// Zip the boot image files up, if available. This will generate the zip file in a
-			// predefined location.
-			buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFiles.byArch)
-
-			// Copy the dex jars of this fragment's content modules to their predefined locations.
-			copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
-		}
-	}
-
-	// A prebuilt fragment cannot contribute to an apex.
-	if !android.IsModulePrebuilt(ctx.Module()) {
-		// Provide the apex content info.
-		b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFiles)
+	if android.IsModulePrebuilt(ctx.Module()) {
+		b.profilePath = ctx.Module().(*PrebuiltBootclasspathFragmentModule).produceBootImageProfile(ctx)
+	} else {
+		b.profilePath = b.produceBootImageProfileFromSource(ctx, contents, hiddenAPIOutput.EncodedBootDexFilesByModule)
+		// Provide the apex content info. A prebuilt fragment cannot contribute to an apex.
+		b.provideApexContentInfo(ctx, hiddenAPIOutput, b.profilePath)
 	}
 
 	// In order for information about bootclasspath_fragment modules to be added to module-info.json
@@ -564,45 +507,37 @@
 	}
 }
 
-// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot
-// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest
-// of the build.
-//
-// This ensures that only a single module will copy its files to the image configuration.
-func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool {
+// getProfileProviderApex returns the name of the apex that provides a boot image profile, or an
+// empty string if this module should not provide a boot image profile.
+func (b *BootclasspathFragmentModule) getProfileProviderApex(ctx android.BaseModuleContext) string {
+	// Only use the profile from the module that is preferred.
+	if !isActiveModule(ctx.Module()) {
+		return ""
+	}
+
 	// Bootclasspath fragment modules that are for the platform do not produce boot related files.
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	if apexInfo.IsForPlatform() {
-		return false
+	for _, apex := range apexInfo.InApexVariants {
+		if isProfileProviderApex(ctx, apex) {
+			return apex
+		}
 	}
 
-	// If the image configuration has no modules specified then it means that the build has been
-	// configured to build something other than a boot image, e.g. an sdk, so do not try and copy the
-	// files.
-	if imageConfig.modules.Len() == 0 {
-		return false
-	}
-
-	// Only copy files from the module that is preferred.
-	return isActiveModule(ctx.Module())
+	return ""
 }
 
 // provideApexContentInfo creates, initializes and stores the apex content info for use by other
 // modules.
-func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFiles bootImageOutputs) {
+func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, hiddenAPIOutput *HiddenAPIOutput, profile android.WritablePath) {
 	// Construct the apex content info from the config.
 	info := BootclasspathFragmentApexContentInfo{
 		// Populate the apex content info with paths to the dex jars.
 		contentModuleDexJarPaths: hiddenAPIOutput.EncodedBootDexFilesByModule,
 	}
 
-	if imageConfig != nil {
-		info.modules = imageConfig.modules
-		global := dexpreopt.GetGlobalConfig(ctx)
-		if !global.DisableGenerateProfile {
-			info.profilePathOnHost = bootImageFiles.profile
-			info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
-		}
+	if profile != nil {
+		info.profilePathOnHost = profile
+		info.profileInstallPathInApex = profileInstallPathInApex
 	}
 
 	// Make the apex content info available for other modules.
@@ -623,12 +558,12 @@
 }
 
 func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
-	if "art" == proptools.String(b.properties.Image_name) {
-		return b.getImageConfig(ctx).modules
-	}
-
 	global := dexpreopt.GetGlobalConfig(ctx)
 
+	if "art" == proptools.String(b.properties.Image_name) {
+		return global.ArtApexJars
+	}
+
 	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag)
 	jars, unknown := global.ApexBootJars.Filter(possibleUpdatableModules)
 
@@ -654,25 +589,6 @@
 	return jars
 }
 
-func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
-	// Get a map of the image configs that are supported.
-	imageConfigs := genBootImageConfigs(ctx)
-
-	// Retrieve the config for this image.
-	imageNamePtr := b.properties.Image_name
-	if imageNamePtr == nil {
-		return nil
-	}
-
-	imageName := *imageNamePtr
-	imageConfig := imageConfigs[imageName]
-	if imageConfig == nil {
-		ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedKeys(imageConfigs), ", "))
-		return nil
-	}
-	return imageConfig
-}
-
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
 
@@ -855,48 +771,22 @@
 	return output
 }
 
-// produceBootImageFiles builds the boot image files from the source if it is required.
-func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
-	// Only generate the boot image if the configuration does not skip it.
-	return b.generateBootImageBuildActions(ctx, imageConfig)
-}
-
-// generateBootImageBuildActions generates ninja rules to create the boot image if required for this
-// module.
-//
-// If it could not create the files then it will return nil. Otherwise, it will return a map from
-// android.ArchType to the predefined paths of the boot image files.
-func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
-	global := dexpreopt.GetGlobalConfig(ctx)
-	if !shouldBuildBootImages(ctx.Config(), global) {
-		return bootImageOutputs{}
+// produceBootImageProfileFromSource builds the boot image profile from the source if it is required.
+func (b *BootclasspathFragmentModule) produceBootImageProfileFromSource(ctx android.ModuleContext, contents []android.Module, modules bootDexJarByModule) android.WritablePath {
+	apex := b.getProfileProviderApex(ctx)
+	if apex == "" {
+		return nil
 	}
 
-	// Bootclasspath fragment modules that are for the platform do not produce a boot image.
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	if apexInfo.IsForPlatform() {
-		return bootImageOutputs{}
+	dexPaths := make(android.Paths, 0, len(contents))
+	dexLocations := make([]string, 0, len(contents))
+	for _, module := range contents {
+		dexPaths = append(dexPaths, modules[module.Name()])
+		dexLocations = append(dexLocations, filepath.Join("/", "apex", apex, "javalib", module.Name() + ".jar"))
 	}
 
-	// Build a profile for the image config and then use that to build the boot image.
-	profile := bootImageProfileRule(ctx, imageConfig)
-
-	// If dexpreopt of boot image jars should be skipped, generate only a profile.
-	if SkipDexpreoptBootJars(ctx) {
-		return bootImageOutputs{
-			profile: profile,
-		}
-	}
-
-	// Build boot image files for the host variants.
-	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
-
-	// Build boot image files for the android variants.
-	bootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
-
-	// Return the boot image files for the android variants for inclusion in an APEX and to be zipped
-	// up for the dist.
-	return bootImageFiles
+	// Build a profile for the modules in this fragment.
+	return bootImageProfileRuleCommon(ctx, b.Name(), dexPaths, dexLocations)
 }
 
 func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
@@ -919,10 +809,6 @@
 	return entriesList
 }
 
-func (b *BootclasspathFragmentModule) getImageName() *string {
-	return b.properties.Image_name
-}
-
 func (b *BootclasspathFragmentModule) getProfilePath() android.Path {
 	return b.profilePath
 }
@@ -1192,39 +1078,19 @@
 	return &output
 }
 
-// produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
-	if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
-		return bootImageOutputs{}
+// produceBootImageProfile extracts the boot image profile from the APEX if available.
+func (module *PrebuiltBootclasspathFragmentModule) produceBootImageProfile(ctx android.ModuleContext) android.WritablePath {
+	// This module does not provide a boot image profile.
+	if module.getProfileProviderApex(ctx) == "" {
+		return nil
 	}
 
 	di := android.FindDeapexerProviderForModule(ctx)
 	if di == nil {
-		return bootImageOutputs{} // An error has been reported by FindDeapexerProviderForModule.
+		return nil // An error has been reported by FindDeapexerProviderForModule.
 	}
 
-	profile := (android.WritablePath)(nil)
-	if imageConfig.profileInstallPathInApex != "" {
-		profile = di.PrebuiltExportPath(imageConfig.profileInstallPathInApex)
-	}
-
-	// Build the boot image files for the host variants. These are always built from the dex files
-	// provided by the contents of this module as prebuilt versions of the host boot image files are
-	// not available, i.e. there is no host specific prebuilt apex containing them. This has to be
-	// built without a profile as the prebuilt modules do not provide a profile.
-	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
-
-	if profile == nil && imageConfig.isProfileGuided() {
-		ctx.ModuleErrorf("Unable to produce boot image files: profiles not found in the prebuilt apex")
-		return bootImageOutputs{}
-	}
-	// Build boot image files for the android variants from the dex files provided by the contents
-	// of this module.
-	return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
-}
-
-func (b *PrebuiltBootclasspathFragmentModule) getImageName() *string {
-	return b.properties.Image_name
+	return di.PrebuiltExportPath(profileInstallPathInApex)
 }
 
 func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
@@ -1239,14 +1105,10 @@
 // If there is no image config associated with this fragment then it returns nil. Otherwise, it
 // returns the files that are listed in the image config.
 func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
-	imageConfig := module.getImageConfig(ctx)
-	if imageConfig != nil {
-		files := []string{}
-		if imageConfig.profileInstallPathInApex != "" {
-			// Add the boot image profile.
-			files = append(files, imageConfig.profileInstallPathInApex)
+	for _, apex := range module.ApexProperties.Apex_available {
+		if isProfileProviderApex(ctx, apex) {
+			return []string{profileInstallPathInApex}
 		}
-		return files
 	}
 	return nil
 }
@@ -1262,9 +1124,5 @@
 	android.InitApexModule(m)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
 
-	// Initialize the contents property from the image_name.
-	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
-		bootclasspathFragmentInitContentsFromImage(ctx, &m.BootclasspathFragmentModule)
-	})
 	return m
 }
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 2541f14..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,
+			}
 		`)
 }
 
diff --git a/java/builder.go b/java/builder.go
index c4395e9..debf49a 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.tmp" "$srcJarDir" "$out.tmp" && ` +
+				`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,14 +51,18 @@
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
-				`$zipTemplate${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
-				`rm -rf "$srcJarDir"`,
+				`$annoSrcJarTemplate${config.SoongZipCmd} -jar -o $annoSrcJar.tmp -C $annoDir -D $annoDir && ` +
+				`$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
+				`if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
+				`if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
+				`rm -rf "$srcJarDir" "$outDir"`,
 			CommandDeps: []string{
 				"${config.JavacCmd}",
 				"${config.SoongZipCmd}",
 				"${config.ZipSyncCmd}",
 			},
 			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+			Restat:           true,
 			Rspfile:          "$out.rsp",
 			RspfileContent:   "$in",
 		}, map[string]*remoteexec.REParams{
@@ -69,12 +74,19 @@
 			"$zipTemplate": &remoteexec.REParams{
 				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
 				Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
-				OutputFiles:  []string{"$out"},
+				OutputFiles:  []string{"$out.tmp"},
+				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.tmp"},
 				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() })
@@ -259,6 +271,12 @@
 			Description: "Check zip alignment",
 		},
 	)
+
+	convertImplementationJarToHeaderJarRule = pctx.AndroidStaticRule("convertImplementationJarToHeaderJar",
+		blueprint.RuleParams{
+			Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`,
+			CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		})
 )
 
 func init() {
@@ -312,7 +330,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 +338,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 +512,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 +559,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 +575,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(),
 		},
 	})
@@ -619,6 +639,15 @@
 	})
 }
 
+func convertImplementationJarToHeaderJar(ctx android.ModuleContext, implementationJarFile android.Path,
+	headerJarFile android.WritablePath) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   convertImplementationJarToHeaderJarRule,
+		Input:  implementationJarFile,
+		Output: headerJarFile,
+	})
+}
+
 func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	classesJar android.Path, rulesFile android.Path) {
 	ctx.Build(pctx, android.BuildParams{
diff --git a/java/config/config.go b/java/config/config.go
index 195dae1..83c27d3 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -148,6 +148,8 @@
 	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
+	pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar")
+
 	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index eadd9c6..de9a82d 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -146,12 +146,43 @@
     ],
 }
 
+java_defaults {
+    name: "core.module_lib.stubs.defaults",
+    visibility: ["//visibility:private"],
+    sdk_version: "none",
+    system_modules: "none",
+}
+
+
 // A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API
 // provided by the core libraries.
 //
 // Don't use this directly, use "sdk_version: module_current".
 java_library {
     name: "core.module_lib.stubs",
+    defaults: [
+        "core.module_lib.stubs.defaults",
+    ],
+    static_libs: [
+        "core.module_lib.stubs.from-source",
+    ],
+    product_variables: {
+        build_from_text_stub: {
+            static_libs: [
+                "core.module_lib.stubs.from-text",
+            ],
+            exclude_static_libs: [
+                "core.module_lib.stubs.from-source",
+            ],
+        },
+    },
+}
+
+java_library {
+    name: "core.module_lib.stubs.from-source",
+    defaults: [
+        "core.module_lib.stubs.defaults",
+    ],
     static_libs: [
         "art.module.public.api.stubs.module_lib",
 
@@ -161,9 +192,6 @@
         "conscrypt.module.public.api.stubs",
         "i18n.module.public.api.stubs",
     ],
-    sdk_version: "none",
-    system_modules: "none",
-    visibility: ["//visibility:private"],
 }
 
 // Produces a dist file that is used by the
@@ -249,10 +277,10 @@
     product_variables: {
         build_from_text_stub: {
             static_libs: [
-                "stable.core.platform.api.stubs.from-text",
+                "legacy.core.platform.api.stubs.from-text",
             ],
             exclude_static_libs: [
-                "stable.core.platform.api.stubs.from-source",
+                "legacy.core.platform.api.stubs.from-source",
             ],
         },
     },
diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp
index 0cf0f36..c46f8b8 100644
--- a/java/core-libraries/TxtStubLibraries.bp
+++ b/java/core-libraries/TxtStubLibraries.bp
@@ -57,19 +57,23 @@
     ],
 }
 
-java_library {
+java_api_library {
     name: "core.module_lib.stubs.from-text",
-    static_libs: [
-        "art.module.public.api.stubs.module_lib.from-text",
+    api_surface: "module-lib",
+    api_contributions: [
+        "art.module.public.api.stubs.source.api.contribution",
+        "art.module.public.api.stubs.source.system.api.contribution",
+        "art.module.public.api.stubs.source.module_lib.api.contribution",
 
-        // Replace the following with the module-lib correspondence when Conscrypt or i18N module
+        // Add the module-lib correspondence when Conscrypt or i18N module
         // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
         // @SystemApi(MODULE_LIBRARIES).
-        "conscrypt.module.public.api.stubs.from-text",
-        "i18n.module.public.api.stubs.from-text",
+        "conscrypt.module.public.api.stubs.source.api.contribution",
+        "i18n.module.public.api.stubs.source.api.contribution",
     ],
-    sdk_version: "none",
-    system_modules: "none",
+    libs: [
+        "stub-annotations",
+    ],
     visibility: ["//visibility:private"],
 }
 
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 3581040..5460dc9 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -143,6 +143,8 @@
 		ResourceJars:                   d.resourceJars,
 		SrcJarArgs:                     d.srcJarArgs,
 		SrcJarDeps:                     d.srcJarDeps,
+		// TODO: Not sure if aconfig flags that have been moved between device and host variants
+		// make sense.
 	})
 
 }
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 3c9a0f3..3413da0 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -135,6 +135,7 @@
 
 	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
+	hostJavacHeader := hostModule.Output("javac-header/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 
 	hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOSCommonTarget.String())
@@ -148,7 +149,7 @@
 
 	// check classpath of device module with dependency on host_for_device_module
 	expectedClasspath := "-classpath " + strings.Join(android.Paths{
-		hostJavac.Output,
+		hostJavacHeader.Output,
 		hostImportCombined.Output,
 	}.Strings(), ":")
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 2b0f57e..003f2de 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -16,7 +16,6 @@
 
 import (
 	"path/filepath"
-	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -224,6 +223,11 @@
 	"com.google.android.art.testing",
 }
 
+var (
+	dexpreoptBootJarDepTag  = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
+	dexBootJarsFragmentsKey = android.NewOnceKey("dexBootJarsFragments")
+)
+
 func init() {
 	RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
 }
@@ -241,6 +245,9 @@
 	// Image name (used in directory names and ninja rule names).
 	name string
 
+	// If the module with the given name exists, this config is enabled.
+	enabledIfExists string
+
 	// Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}.
 	stem string
 
@@ -257,10 +264,6 @@
 	// the location is relative to "/".
 	installDir string
 
-	// Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
-	// needed.
-	profileInstallPathInApex string
-
 	// A list of (location, jar) pairs for the Java modules in this image.
 	modules android.ConfiguredJarList
 
@@ -296,10 +299,9 @@
 	// The "--single-image" argument.
 	singleImage bool
 
-	// Profiles imported from other boot image configs. Each element must represent a
-	// `bootclasspath_fragment` of an APEX (i.e., the `name` field of each element must refer to the
-	// `image_name` property of a `bootclasspath_fragment`).
-	profileImports []*bootImageConfig
+	// Profiles imported from APEXes, in addition to the profile at the default path. Each entry must
+	// be the name of an APEX module.
+	profileImports []string
 }
 
 // Target-dependent description of a boot image.
@@ -384,7 +386,7 @@
 	m := image.modules.Jar(idx)
 	name := image.stem
 	if idx != 0 || image.extends != nil {
-		name += "-" + android.ModuleStem(m)
+		name += "-" + android.ModuleStem(ctx.Config(), image.modules.Apex(idx), m)
 	}
 	return name
 }
@@ -458,18 +460,26 @@
 	return image.compilerFilter == "speed-profile"
 }
 
+func (image *bootImageConfig) isEnabled(ctx android.BaseModuleContext) bool {
+	return ctx.OtherModuleExists(image.enabledIfExists)
+}
+
 func dexpreoptBootJarsFactory() android.SingletonModule {
 	m := &dexpreoptBootJars{}
-	android.InitAndroidModule(m)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
 	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator).Parallel()
+	})
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
-	return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
+	global := dexpreopt.GetGlobalConfig(ctx)
+	return global.DisablePreoptBootImages || !shouldBuildBootImages(ctx.Config(), global)
 }
 
 // Singleton module for generating boot image build rules.
@@ -492,39 +502,90 @@
 	dexpreoptConfigForMake android.WritablePath
 }
 
-// Provide paths to boot images for use by modules that depend upon them.
-//
-// The build rules are created in GenerateSingletonBuildActions().
-func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Placeholder for now.
+func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
+	if _, ok := ctx.Module().(*dexpreoptBootJars); !ok {
+		return
+	}
+
+	if dexpreopt.IsDex2oatNeeded(ctx) {
+		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+		// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+		dexpreopt.RegisterToolDeps(ctx)
+	}
+
+	imageConfigs := genBootImageConfigs(ctx)
+	for _, config := range imageConfigs {
+		if !config.isEnabled(ctx) {
+			continue
+		}
+		// For accessing the boot jars.
+		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag)
+	}
+
+	if ctx.OtherModuleExists("platform-bootclasspath") {
+		// For accessing all bootclasspath fragments.
+		addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platformBootclasspathDepTag)
+	} else if ctx.OtherModuleExists("art-bootclasspath-fragment") {
+		// For accessing the ART bootclasspath fragment on a thin manifest (e.g., master-art) where
+		// platform-bootclasspath doesn't exist.
+		addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", bootclasspathFragmentDepTag)
+	}
 }
 
-// Generate build rules for boot images.
-func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
-		// 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")
-	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
+func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module {
+	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
+		fragments := make(map[string]android.Module)
+		ctx.WalkDeps(func(child, parent android.Module) bool {
+			if !isActiveModule(child) {
+				return false
+			}
+			tag := ctx.OtherModuleDependencyTag(child)
+			if tag == platformBootclasspathDepTag {
+				return true
+			}
+			if tag == bootclasspathFragmentDepTag {
+				apexInfo := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
+				for _, apex := range apexInfo.InApexVariants {
+					fragments[apex] = child
+				}
+				return false
+			}
+			return false
+		})
+		return fragments
+	}).(map[string]android.Module)
+}
 
-	global := dexpreopt.GetGlobalConfig(ctx)
-	if !shouldBuildBootImages(ctx.Config(), global) {
-		return
-	}
+func getBootclasspathFragmentByApex(ctx android.ModuleContext, apexName string) android.Module {
+	return gatherBootclasspathFragments(ctx)[apexName]
+}
 
-	defaultImageConfig := defaultBootImageConfig(ctx)
-	d.defaultBootImage = defaultImageConfig
+// GenerateAndroidBuildActions generates the build rules for boot images.
+func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	imageConfigs := genBootImageConfigs(ctx)
+	d.defaultBootImage = defaultBootImageConfig(ctx)
 	d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
-	for _, config := range imageConfigs {
-		if config != defaultImageConfig {
+	for _, name := range getImageNames() {
+		config := imageConfigs[name]
+		if config != d.defaultBootImage {
 			d.otherImages = append(d.otherImages, config)
 		}
+		if !config.isEnabled(ctx) {
+			continue
+		}
+		generateBootImage(ctx, config)
+		if config == d.defaultBootImage {
+			bootFrameworkProfileRule(ctx, config)
+		}
 	}
 }
 
+// GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
+func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+	d.dexpreoptConfigForMake = android.PathForOutput(ctx, getDexpreoptDirName(ctx), "dexpreopt.config")
+	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
+}
+
 // shouldBuildBootImages determines whether boot images should be built.
 func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig) bool {
 	// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
@@ -537,6 +598,101 @@
 	return true
 }
 
+func generateBootImage(ctx android.ModuleContext, imageConfig *bootImageConfig) {
+	apexJarModulePairs := getModulesForImage(ctx, imageConfig)
+
+	// Copy module dex jars to their predefined locations.
+	bootDexJarsByModule := extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx, apexJarModulePairs)
+	copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule)
+
+	// Build a profile for the image config from the profile at the default path. The profile will
+	// then be used along with profiles imported from APEXes to build the boot image.
+	profile := bootImageProfileRule(ctx, imageConfig)
+
+	// If dexpreopt of boot image jars should be skipped, stop after generating a profile.
+	if SkipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	// Build boot image files for the android variants.
+	androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+
+	// Zip the android variant boot image files up.
+	buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFiles.byArch)
+
+	// Build boot image files for the host variants. There are use directly by ART host side tests.
+	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
+
+	// Create a `dump-oat-<image-name>` rule that runs `oatdump` for debugging purposes.
+	dumpOatRules(ctx, imageConfig)
+}
+
+type apexJarModulePair struct {
+	apex string
+	jarModule android.Module
+}
+
+func getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []apexJarModulePair {
+	modules := make([]apexJarModulePair, 0, imageConfig.modules.Len())
+	for i := 0; i < imageConfig.modules.Len(); i++ {
+		found := false
+		for _, module := range gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJarDepTag) {
+			name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+			if name == imageConfig.modules.Jar(i) {
+				modules = append(modules, apexJarModulePair{
+					apex: imageConfig.modules.Apex(i),
+					jarModule: module,
+				})
+				found = true
+				break
+			}
+		}
+		if !found && !ctx.Config().AllowMissingDependencies() {
+			ctx.ModuleErrorf(
+				"Boot image '%s' module '%s' not added as a dependency of dex_bootjars",
+				imageConfig.name,
+				imageConfig.modules.Jar(i))
+			return []apexJarModulePair{}
+		}
+	}
+	return modules
+}
+
+// extractEncodedDexJarsFromModulesOrBootclasspathFragments gets the hidden API encoded dex jars for
+// the given modules.
+func extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx android.ModuleContext, apexJarModulePairs []apexJarModulePair) bootDexJarByModule {
+	encodedDexJarsByModuleName := bootDexJarByModule{}
+	for _, pair := range apexJarModulePairs {
+		var path android.Path
+		if android.IsConfiguredJarForPlatform(pair.apex) || android.IsModulePrebuilt(pair.jarModule) {
+			// This gives us the dex jar with the hidden API flags encoded from the monolithic hidden API
+			// files or the dex jar extracted from a prebuilt APEX. We can't use this for a boot jar for
+			// a source APEX because there is no guarantee that it is the same as the jar packed into the
+			// APEX. In practice, they are the same when we are building from a full source tree, but they
+			// are different when we are building from a thin manifest (e.g., master-art), where there is
+			// no monolithic hidden API files at all.
+			path = retrieveEncodedBootDexJarFromModule(ctx, pair.jarModule)
+		} else {
+			// Use exactly the same jar that is packed into the APEX.
+			fragment := getBootclasspathFragmentByApex(ctx, pair.apex)
+			if fragment == nil {
+				ctx.ModuleErrorf("Boot jar '%[1]s' is from APEX '%[2]s', but a bootclasspath_fragment for "+
+					"APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars",
+					pair.jarModule.Name(),
+					pair.apex)
+			}
+			bootclasspathFragmentInfo := ctx.OtherModuleProvider(fragment, BootclasspathFragmentApexContentInfoProvider).(BootclasspathFragmentApexContentInfo)
+			jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
+			if err != nil {
+				ctx.ModuleErrorf("%s", err)
+			}
+			path = jar
+		}
+		encodedDexJarsByModuleName.addPath(pair.jarModule, path)
+	}
+	return encodedDexJarsByModuleName
+}
+
 // copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
 // paths in the global config.
 func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
@@ -687,10 +843,12 @@
 	rule.Command().Text("rm").Flag("-f").
 		Flag(symbolsDir.Join(ctx, "*.art").String()).
 		Flag(symbolsDir.Join(ctx, "*.oat").String()).
+		Flag(symbolsDir.Join(ctx, "*.vdex").String()).
 		Flag(symbolsDir.Join(ctx, "*.invocation").String())
 	rule.Command().Text("rm").Flag("-f").
 		Flag(outputDir.Join(ctx, "*.art").String()).
 		Flag(outputDir.Join(ctx, "*.oat").String()).
+		Flag(outputDir.Join(ctx, "*.vdex").String()).
 		Flag(outputDir.Join(ctx, "*.invocation").String())
 
 	cmd := rule.Command()
@@ -712,36 +870,31 @@
 		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
 		Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx)
 
-	if profile != nil {
-		cmd.FlagWithInput("--profile-file=", profile)
-	}
+	if image.isProfileGuided() && !global.DisableGenerateProfile {
+		if profile != nil {
+			cmd.FlagWithInput("--profile-file=", profile)
+		}
 
-	fragments := make(map[string]commonBootclasspathFragment)
-	ctx.VisitDirectDepsWithTag(bootclasspathFragmentDepTag, func(child android.Module) {
-		fragment := child.(commonBootclasspathFragment)
-		if fragment.getImageName() != nil && android.IsModulePreferred(child) {
-			fragments[*fragment.getImageName()] = fragment
+		for _, apex := range image.profileImports {
+			fragment := getBootclasspathFragmentByApex(ctx, apex)
+			if fragment == nil {
+				ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+
+					"bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+
+					"dependency of dex_bootjars",
+					image.name,
+					apex)
+				return bootImageVariantOutputs{}
+			}
+			importedProfile := fragment.(commonBootclasspathFragment).getProfilePath()
+			if importedProfile == nil {
+				ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
+					"doesn't provide a profile",
+					image.name,
+					apex)
+				return bootImageVariantOutputs{}
+			}
+			cmd.FlagWithInput("--profile-file=", importedProfile)
 		}
-	})
-
-	for _, profileImport := range image.profileImports {
-		fragment := fragments[profileImport.name]
-		if fragment == nil {
-			ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+
-				"bootclasspath_fragment with image name '%[2]s' doesn't exist or is not added as a "+
-				"dependency of '%[1]s'",
-				image.name,
-				profileImport.name)
-			return bootImageVariantOutputs{}
-		}
-		if fragment.getProfilePath() == nil {
-			ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
-				"doesn't provide a profile",
-				image.name,
-				profileImport.name)
-			return bootImageVariantOutputs{}
-		}
-		cmd.FlagWithInput("--profile-file=", fragment.getProfilePath())
 	}
 
 	dirtyImageFile := "frameworks/base/config/dirty-image-objects"
@@ -885,11 +1038,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)
 
@@ -916,28 +1065,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
 }
 
@@ -976,6 +1136,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()
@@ -984,36 +1146,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,
 	})
 }
 
@@ -1048,11 +1213,9 @@
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
 
-		var imageNames []string
 		// The primary ART boot image is exposed to Make for testing (gtests) and benchmarking
 		// (golem) purposes.
 		for _, current := range append(d.otherImages, image) {
-			imageNames = append(imageNames, current.name)
 			for _, variant := range current.variants {
 				suffix := ""
 				if variant.target.Os.Class == android.Host {
@@ -1073,8 +1236,6 @@
 			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":"))
 			ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
 		}
-		// Ensure determinism.
-		sort.Strings(imageNames)
-		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
+		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " "))
 	}
 }
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 28f50d7..0f4bd9b 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -40,57 +40,67 @@
 }
 
 var (
-	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
-	bootImageConfigRawKey  = android.NewOnceKey("bootImageConfigRaw")
-	artBootImageName       = "art"
-	frameworkBootImageName = "boot"
-	mainlineBootImageName  = "mainline"
-	bootImageStem          = "boot"
+	bootImageConfigKey       = android.NewOnceKey("bootImageConfig")
+	bootImageConfigRawKey    = android.NewOnceKey("bootImageConfigRaw")
+	frameworkBootImageName   = "boot"
+	mainlineBootImageName    = "mainline"
+	bootImageStem            = "boot"
+	profileInstallPathInApex = "etc/boot-image.prof"
 )
 
+// getImageNames returns an ordered list of image names. The order doesn't matter but needs to be
+// deterministic. The names listed here must match the map keys returned by genBootImageConfigs.
+func getImageNames() []string {
+	return []string{"art", "boot", "mainline"}
+}
+
 func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
 	return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
 
-		artModules := global.ArtApexJars
-		frameworkModules := global.BootJars // This includes `artModules`.
+		artBootImageName := "art"           // Keep this local to avoid accidental references.
+		frameworkModules := global.BootJars // This includes `global.ArtApexJars`.
 		mainlineBcpModules := global.ApexBootJars
 		frameworkSubdir := "system/framework"
 
-		// ART config for the primary boot image in the ART apex.
-		// It includes the Core Libraries.
+		profileImports := []string{"com.android.art"}
+
+		// ART boot image for testing only. Do not rely on it to make any build-time decision.
 		artCfg := bootImageConfig{
-			name:                     artBootImageName,
-			stem:                     bootImageStem,
-			installDir:               "apex/art_boot_images/javalib",
-			profileInstallPathInApex: "etc/boot-image.prof",
-			modules:                  artModules,
-			preloadedClassesFile:     "art/build/boot/preloaded-classes",
-			compilerFilter:           "speed-profile",
-			singleImage:              false,
+			name:                 artBootImageName,
+			enabledIfExists:      "art-bootclasspath-fragment",
+			stem:                 bootImageStem,
+			installDir:           "apex/art_boot_images/javalib",
+			modules:              global.TestOnlyArtBootImageJars,
+			preloadedClassesFile: "art/build/boot/preloaded-classes",
+			compilerFilter:       "speed-profile",
+			singleImage:          false,
+			profileImports:       profileImports,
 		}
 
 		// Framework config for the boot image extension.
 		// It includes framework libraries and depends on the ART config.
 		frameworkCfg := bootImageConfig{
 			name:                 frameworkBootImageName,
+			enabledIfExists:      "platform-bootclasspath",
 			stem:                 bootImageStem,
 			installDir:           frameworkSubdir,
 			modules:              frameworkModules,
 			preloadedClassesFile: "frameworks/base/config/preloaded-classes",
 			compilerFilter:       "speed-profile",
 			singleImage:          false,
-			profileImports:       []*bootImageConfig{&artCfg},
+			profileImports:       profileImports,
 		}
 
 		mainlineCfg := bootImageConfig{
-			extends:        &frameworkCfg,
-			name:           mainlineBootImageName,
-			stem:           bootImageStem,
-			installDir:     frameworkSubdir,
-			modules:        mainlineBcpModules,
-			compilerFilter: "verify",
-			singleImage:    true,
+			extends:         &frameworkCfg,
+			name:            mainlineBootImageName,
+			enabledIfExists: "platform-bootclasspath",
+			stem:            bootImageStem,
+			installDir:      frameworkSubdir,
+			modules:         mainlineBcpModules,
+			compilerFilter:  "verify",
+			singleImage:     true,
 		}
 
 		return map[string]*bootImageConfig{
@@ -105,8 +115,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)
 
@@ -181,10 +190,6 @@
 	}
 }
 
-func artBootImageConfig(ctx android.PathContext) *bootImageConfig {
-	return genBootImageConfigs(ctx)[artBootImageName]
-}
-
 func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig {
 	return genBootImageConfigs(ctx)[frameworkBootImageName]
 }
@@ -193,6 +198,18 @@
 	return genBootImageConfigs(ctx)[mainlineBootImageName]
 }
 
+// isProfileProviderApex returns true if this apex provides a boot image profile.
+func isProfileProviderApex(ctx android.PathContext, apexName string) bool {
+	for _, config := range genBootImageConfigs(ctx) {
+		for _, profileImport := range config.profileImports {
+			if profileImport == apexName {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 // Apex boot config allows to access build/install paths of apex boot jars without going
 // through the usual trouble of registering dependencies on those modules and extracting build paths
 // from those dependencies.
@@ -218,8 +235,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)
 
@@ -258,6 +274,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_test.go b/java/dexpreopt_config_test.go
index cd7f295..44d2127 100644
--- a/java/dexpreopt_config_test.go
+++ b/java/dexpreopt_config_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"runtime"
+	"sort"
 	"testing"
 
 	"android/soong/android"
@@ -35,3 +36,22 @@
 	CheckFrameworkBootImageConfig(t, result)
 	CheckMainlineBootImageConfig(t, result)
 }
+
+func TestImageNames(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForBootImageConfigTest,
+	).RunTest(t)
+
+	names := getImageNames()
+	sort.Strings(names)
+
+	ctx := &android.TestPathContext{TestResult: result}
+	configs := genBootImageConfigs(ctx)
+	namesFromConfigs := make([]string, 0, len(configs))
+	for name, _ := range configs {
+		namesFromConfigs = append(namesFromConfigs, name)
+	}
+	sort.Strings(namesFromConfigs)
+
+	android.AssertArrayString(t, "getImageNames vs genBootImageConfigs", names, namesFromConfigs)
+}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 6f3aa2b..176c251 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -29,6 +29,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 )
 
 // PrepareForBootImageConfigTest is the minimal set of preparers that are needed to be able to use
@@ -36,7 +37,17 @@
 var PrepareForBootImageConfigTest = android.GroupFixturePreparers(
 	android.PrepareForTestWithArchMutator,
 	android.PrepareForTestAccessingMakeVars,
+	PrepareForTestWithDexpreopt,
 	FixtureConfigureBootJars("com.android.art:core1", "com.android.art:core2", "platform:framework"),
+	dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:core1", "com.android.art:core2", "platform:extra1"),
+	android.FixtureAddTextFile("extra1/Android.bp", `
+		java_library {
+			name: "extra1",
+			srcs: ["extra1.java"],
+			installable: true,
+		}
+	`),
+	android.FixtureAddFile("extra1/extra1.java", nil),
 )
 
 var PrepareApexBootJarConfigs = FixtureConfigureApexBootJars(
@@ -44,18 +55,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 +75,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 +102,8 @@
 			}
 
 			bootclasspath_fragment {
-				name: "%[1]s-bootclasspathfragment",
+				name: "%[3]s",
+				%[4]s
 				contents: ["%[2]s"],
 				apex_available: ["%[1]s"],
 				hidden_api: {
@@ -100,7 +119,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 +211,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
 }
 
@@ -210,15 +229,15 @@
 		symbolsDir:               "out/soong/dexpreopt_arm64/dex_artjars_unstripped",
 		installDir:               "apex/art_boot_images/javalib",
 		profileInstallPathInApex: "etc/boot-image.prof",
-		modules:                  android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2"}),
-		dexPaths:                 []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"},
-		dexPathsDeps:             []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"},
+		modules:                  android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2", "platform:extra1"}),
+		dexPaths:                 []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/extra1.jar"},
+		dexPathsDeps:             []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/extra1.jar"},
 		zip:                      "out/soong/dexpreopt_arm64/dex_artjars/art.zip",
 		variants: []*expectedVariant{
 			{
 				archType:          android.Arm64,
-				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
-				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
+				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"},
+				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"},
 				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
 				imagePathOnDevice: "/apex/art_boot_images/javalib/arm64/boot.art",
 				imagesDeps: []string{
@@ -228,6 +247,9 @@
 					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
 					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex",
 				},
 				installs: []normalizedInstall{
 					{
@@ -246,6 +268,14 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-extra1.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-extra1.oat",
+					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
@@ -256,6 +286,10 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-extra1.vdex",
+					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
@@ -266,13 +300,17 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-extra1.oat",
+					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
 			{
 				archType:          android.Arm,
-				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
-				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
+				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"},
+				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"},
 				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
 				imagePathOnDevice: "/apex/art_boot_images/javalib/arm/boot.art",
 				imagesDeps: []string{
@@ -282,6 +320,9 @@
 					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
 					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
 					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex",
 				},
 				installs: []normalizedInstall{
 					{
@@ -300,6 +341,14 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art",
+						to:   "/apex/art_boot_images/javalib/arm/boot-extra1.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/arm/boot-extra1.oat",
+					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
@@ -310,6 +359,10 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.vdex",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex",
+						to:   "/apex/art_boot_images/javalib/arm/boot-extra1.vdex",
+					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
@@ -320,13 +373,17 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/arm/boot-extra1.oat",
+					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
 			{
 				archType:          android.X86_64,
-				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
-				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
+				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"},
+				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"},
 				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
 				imagePathOnDevice: "/apex/art_boot_images/javalib/x86_64/boot.art",
 				imagesDeps: []string{
@@ -336,6 +393,9 @@
 					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
 					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex",
 				},
 				installs: []normalizedInstall{
 					{
@@ -352,6 +412,13 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-extra1.art",
+					}, {
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-extra1.oat",
+					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
@@ -362,6 +429,10 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex",
+					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
@@ -372,13 +443,17 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-extra1.oat",
+					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
 			{
 				archType:          android.X86,
-				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
-				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
+				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"},
+				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"},
 				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
 				imagePathOnDevice: "/apex/art_boot_images/javalib/x86/boot.art",
 				imagesDeps: []string{
@@ -388,6 +463,9 @@
 					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
 					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
 					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex",
 				},
 				installs: []normalizedInstall{
 					{
@@ -404,6 +482,13 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art",
+						to:   "/apex/art_boot_images/javalib/x86/boot-extra1.art",
+					}, {
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/x86/boot-extra1.oat",
+					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
@@ -414,6 +499,10 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.vdex",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex",
+						to:   "/apex/art_boot_images/javalib/x86/boot-extra1.vdex",
+					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
@@ -424,6 +513,10 @@
 						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.oat",
 					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat",
+						to:   "/apex/art_boot_images/javalib/x86/boot-extra1.oat",
+					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
@@ -805,8 +898,8 @@
 			},
 		},
 		profileInstalls: []normalizedInstall{
+			{from: "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", to: "/system/etc/boot-image.prof"},
 			{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"},
 		},
 		profileLicenseMetadataFile: expectedLicenseMetadataFile,
 	}
@@ -1136,7 +1229,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())
@@ -1195,10 +1287,10 @@
 DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS=/apex/com.android.art/javalib/core1.jar /apex/com.android.art/javalib/core2.jar /system/framework/framework.jar
 DEXPREOPT_BOOT_JARS_MODULES=com.android.art:core1:com.android.art:core2:platform:framework
 DEXPREOPT_GEN=out/host/linux-x86/bin/dexpreopt_gen
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art:/apex/art_boot_images/javalib/arm/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art:/apex/art_boot_images/javalib/arm64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art:/apex/art_boot_images/javalib/x86/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art:/apex/art_boot_images/javalib/x86_64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat:/apex/art_boot_images/javalib/x86_64/boot-extra1.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art:/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art:/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art:/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art:/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art:/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art:/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
@@ -1207,10 +1299,10 @@
 DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art:/system/framework/arm64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art:/system/framework/x86/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art:/system/framework/x86_64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
-DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex
+DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex
+DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex
+DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex
 DEXPREOPT_IMAGE_DEPS_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex
 DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex
 DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex
@@ -1223,14 +1315,14 @@
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm64=%[1]s
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86=%[1]s
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86_64=%[1]s
-DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86_64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86_64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
 DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEart=/apex/art_boot_images/javalib/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEmainline=/system/framework/boot.art:/system/framework/boot-framework-foo.art
@@ -1238,12 +1330,12 @@
 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_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
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof:/system/etc/boot-image.prof out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof
+DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/default/java/dex_bootjars/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 out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.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 out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat:/apex/art_boot_images/javalib/x86_64/boot-extra1.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
@@ -1252,10 +1344,10 @@
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex:/apex/art_boot_images/javalib/arm/boot-extra1.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex:/apex/art_boot_images/javalib/arm64/boot-extra1.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex:/apex/art_boot_images/javalib/x86/boot-extra1.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex:/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex
 DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex:/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex:/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex
 DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex:/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex:/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex:/system/framework/arm64/boot-framework.vdex
 DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex:/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex:/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex
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/fuzz_test.go b/java/fuzz_test.go
index dd1e96b..f29c913 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -71,8 +71,8 @@
 	}
 
 	baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String()
-	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac", "bar.jar")
-	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac", "baz.jar")
+	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac-header", "bar.jar")
+	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac-header", "baz.jar")
 
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
index 1b3de9f..f9baa85 100644
--- a/java/generated_java_library.go
+++ b/java/generated_java_library.go
@@ -30,7 +30,7 @@
 
 	// Called from inside GenerateAndroidBuildActions. Add the build rules to
 	// make the srcjar, and return the path to it.
-	GenerateSourceJarBuildActions(ctx android.ModuleContext) android.Path
+	GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path
 }
 
 // GeneratedJavaLibraryModuleFactory provides a utility for modules that are generated
@@ -88,7 +88,7 @@
 	checkPropertyEmpty(ctx, module, "plugins", module.Library.properties.Plugins)
 	checkPropertyEmpty(ctx, module, "exported_plugins", module.Library.properties.Exported_plugins)
 
-	srcJarPath := module.callbacks.GenerateSourceJarBuildActions(ctx)
+	srcJarPath := module.callbacks.GenerateSourceJarBuildActions(module, ctx)
 	module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath)
 	module.Library.GenerateAndroidBuildActions(ctx)
 }
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index 68f1f7e..7f52fd1 100644
--- a/java/generated_java_library_test.go
+++ b/java/generated_java_library_test.go
@@ -36,7 +36,8 @@
 func (callbacks *JavaGenLibTestCallbacks) DepsMutator(module *GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) {
 }
 
-func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(ctx android.ModuleContext) android.Path {
+func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path {
+	module.AddAconfigIntermediate(android.PathForOutput(ctx, "aconfig_cache_file"))
 	return android.PathForOutput(ctx, "blah.srcjar")
 }
 
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index d25096b..4d08b83 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -106,7 +106,7 @@
 	h.uncompressDexState = uncompressedDexState
 
 	// If hiddenapi processing is disabled treat this as inactive.
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	if ctx.Config().DisableHiddenApiChecks() {
 		return
 	}
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index c6b921b..f31f5d1 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -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..8ec1797 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -121,8 +121,8 @@
 
 // hiddenAPI singleton rules
 func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	// Don't run any hiddenapi rules if hiddenapi checks are disabled
+	if ctx.Config().DisableHiddenApiChecks() {
 		return
 	}
 
@@ -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 50d48ab..f29f738 100644
--- a/java/java.go
+++ b/java/java.go
@@ -274,7 +274,14 @@
 	// instrumented by jacoco.
 	JacocoReportClassesFile android.Path
 
-	// TODO: Add device config declarations here?
+	// set of aconfig flags for all transitive libs deps
+	// TODO(joeo): It would be nice if this were over in the aconfig package instead of here.
+	// In order to do that, generated_java_library would need a way doing
+	// collectTransitiveAconfigFiles with one of the callbacks, and having that automatically
+	// propagated. If we were to clean up more of the stuff on JavaInfo that's not part of
+	// core java rules (e.g. AidlIncludeDirs), then maybe adding more framework to do that would be
+	// worth it.
+	TransitiveAconfigFiles *android.DepSet[android.Path]
 }
 
 var JavaInfoProvider = blueprint.NewProvider(JavaInfo{})
@@ -676,6 +683,8 @@
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 	j.maxSdkVersion = j.MaxSdkVersion(ctx)
 
+	j.stem = proptools.StringDefault(j.overridableDeviceProperties.Stem, ctx.ModuleName())
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
@@ -690,7 +699,7 @@
 		j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 		j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	}
-	j.compile(ctx, nil)
+	j.compile(ctx, nil, nil, nil)
 
 	// Collect the module directory for IDE info in java/jdeps.go.
 	j.modulePaths = append(j.modulePaths, ctx.ModuleDir())
@@ -728,6 +737,7 @@
 		}
 	})
 	j.exportedProguardFlagFiles = android.FirstUniquePaths(j.exportedProguardFlagFiles)
+
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -1468,6 +1478,8 @@
 }
 
 func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.stem = proptools.StringDefault(j.overridableDeviceProperties.Stem, ctx.ModuleName())
+
 	if ctx.Arch().ArchType == android.Common {
 		// Compile the jar
 		if j.binaryProperties.Main_class != nil {
@@ -1716,8 +1728,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").
@@ -1878,8 +1889,10 @@
 		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{})
+			android.Paths{al.stubsSrcJar}, annoSrcJar, flags, android.Paths{})
 	}
 
 	builder := android.NewRuleBuilder(pctx, ctx)
@@ -1911,6 +1924,7 @@
 		ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar),
 		ImplementationJars:             android.PathsIfNonNil(al.stubsJar),
 		AidlIncludeDirs:                android.Paths{},
+		// No aconfig libraries on api libraries
 	})
 }
 
@@ -2232,6 +2246,7 @@
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
 		ImplementationJars:             android.PathsIfNonNil(j.combinedClasspathFile),
 		AidlIncludeDirs:                j.exportAidlIncludeDirs,
+		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
 }
 
@@ -2652,7 +2667,7 @@
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
-var inList = android.InList
+var inList = android.InList[string]
 
 // Add class loader context (CLC) of a given dependency to the current CLC.
 func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
@@ -2766,11 +2781,12 @@
 type javaCommonAttributes struct {
 	*javaResourcesAttributes
 	*kotlinAttributes
-	Srcs         bazel.LabelListAttribute
-	Plugins      bazel.LabelListAttribute
-	Javacopts    bazel.StringListAttribute
-	Sdk_version  bazel.StringAttribute
-	Java_version bazel.StringAttribute
+	Srcs                    bazel.LabelListAttribute
+	Plugins                 bazel.LabelListAttribute
+	Javacopts               bazel.StringListAttribute
+	Sdk_version             bazel.StringAttribute
+	Java_version            bazel.StringAttribute
+	Errorprone_force_enable bazel.BoolAttribute
 }
 
 type javaDependencyLabels struct {
@@ -2803,12 +2819,8 @@
 	hasKotlin bool
 }
 
-// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-java
-func xsdConfigJavaTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
-	callback := func(xsd android.XsdConfigBp2buildTargets) string {
-		return xsd.JavaBp2buildTargetName()
-	}
-	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
+func javaXsdTargetName(xsd android.XsdConfigBp2buildTargets) string {
+	return xsd.JavaBp2buildTargetName()
 }
 
 // convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with
@@ -2819,21 +2831,14 @@
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
 	var srcs bazel.LabelListAttribute
 	var deps bazel.LabelListAttribute
-	var staticDeps bazel.LabelList
+	var staticDeps bazel.LabelListAttribute
 
 	archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
 			if archProps, ok := _props.(*CommonProperties); ok {
-				srcsNonXsd, srcsXsd := android.PartitionXsdSrcs(ctx, archProps.Srcs)
-				excludeSrcsNonXsd, _ := android.PartitionXsdSrcs(ctx, archProps.Exclude_srcs)
-				archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, srcsNonXsd, excludeSrcsNonXsd)
+				archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Srcs, archProps.Exclude_srcs)
 				srcs.SetSelectValue(axis, config, archSrcs)
-
-				// Add to static deps
-				xsdJavaConfigLibraryLabels := android.BazelLabelForModuleDepsWithFn(ctx, srcsXsd, xsdConfigJavaTarget)
-				staticDeps.Append(xsdJavaConfigLibraryLabels)
-
 			}
 		}
 	}
@@ -2841,6 +2846,7 @@
 
 	javaSrcPartition := "java"
 	protoSrcPartition := "proto"
+	xsdSrcPartition := "xsd"
 	logtagSrcPartition := "logtag"
 	aidlSrcPartition := "aidl"
 	kotlinPartition := "kotlin"
@@ -2849,6 +2855,7 @@
 		logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}},
 		protoSrcPartition:  android.ProtoSrcLabelPartition,
 		aidlSrcPartition:   android.AidlSrcLabelPartition,
+		xsdSrcPartition:    bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(javaXsdTargetName)},
 		kotlinPartition:    bazel.LabelPartition{Extensions: []string{".kt"}},
 	})
 
@@ -2856,6 +2863,8 @@
 	kotlinSrcs := srcPartitions[kotlinPartition]
 	javaSrcs.Append(kotlinSrcs)
 
+	staticDeps.Append(srcPartitions[xsdSrcPartition])
+
 	if !srcPartitions[logtagSrcPartition].IsEmpty() {
 		logtagsLibName := m.Name() + "_logtags"
 		ctx.CreateBazelTargetModule(
@@ -2909,29 +2918,38 @@
 			},
 		)
 
-		staticDeps.Add(&bazel.Label{Label: ":" + javaAidlLibName})
+		staticDeps.Append(bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + javaAidlLibName}))
 	}
 
-	var javacopts []string
+	var javacopts bazel.StringListAttribute //[]string
+	plugins := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
+	)
 	if m.properties.Javacflags != nil {
-		javacopts = append(javacopts, m.properties.Javacflags...)
+		javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
 	}
 
 	epEnabled := m.properties.Errorprone.Enabled
-	//TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
-	if Bool(epEnabled) {
-		javacopts = append(javacopts, m.properties.Errorprone.Javacflags...)
+	epJavacflags := m.properties.Errorprone.Javacflags
+	var errorproneForceEnable bazel.BoolAttribute
+	if epEnabled == nil {
+		//TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
+	} else if *epEnabled {
+		plugins.Append(bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, m.properties.Errorprone.Extra_check_modules)))
+		javacopts.Append(bazel.MakeStringListAttribute(epJavacflags))
+		errorproneForceEnable.Value = epEnabled
+	} else {
+		javacopts.Append(bazel.MakeStringListAttribute([]string{"-XepDisableAllChecks"}))
 	}
 
 	commonAttrs := &javaCommonAttributes{
 		Srcs:                    javaSrcs,
 		javaResourcesAttributes: m.convertJavaResourcesAttributes(ctx),
-		Plugins: bazel.MakeLabelListAttribute(
-			android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
-		),
-		Javacopts:    bazel.MakeStringListAttribute(javacopts),
-		Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
-		Sdk_version:  bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
+		Plugins:                 plugins,
+		Javacopts:               javacopts,
+		Java_version:            bazel.StringAttribute{Value: m.properties.Java_version},
+		Sdk_version:             bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
+		Errorprone_force_enable: errorproneForceEnable,
 	}
 
 	for axis, configToProps := range archVariantProps {
@@ -2955,7 +2973,9 @@
 	// by protoc are included directly in the resulting JAR. Thus upstream dependencies
 	// that depend on a java_library with proto sources can link directly to the protobuf API,
 	// and so this should be a static dependency.
-	staticDeps.Add(protoDepLabel)
+	if protoDepLabel != nil {
+		staticDeps.Append(bazel.MakeSingleLabelListAttribute(*protoDepLabel))
+	}
 
 	depLabels := &javaDependencyLabels{}
 	depLabels.Deps = deps
@@ -2970,7 +2990,7 @@
 			}
 		}
 	}
-	depLabels.StaticDeps.Value.Append(staticDeps)
+	depLabels.StaticDeps.Append(staticDeps)
 
 	hasKotlin := !kotlinSrcs.IsEmpty()
 	commonAttrs.kotlinAttributes = &kotlinAttributes{
@@ -3140,6 +3160,7 @@
 
 type javaTestHostAttributes struct {
 	*javaCommonAttributes
+	Srcs         bazel.LabelListAttribute
 	Deps         bazel.LabelListAttribute
 	Runtime_deps bazel.LabelListAttribute
 }
@@ -3176,8 +3197,10 @@
 		hasKotlin: bp2BuildInfo.hasKotlin,
 	}
 	libName := createLibraryTarget(ctx, libInfo)
-	attrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
 
+	attrs.Srcs = commonAttrs.Srcs
+	attrs.Deps = deps
+	attrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
 	// Create the BazelTargetModule.
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 }
@@ -3290,7 +3313,8 @@
 		HeaderJars:                     android.PathsIfNonNil(i.combinedClasspathFile),
 		ImplementationAndResourcesJars: android.PathsIfNonNil(i.combinedClasspathFile),
 		ImplementationJars:             android.PathsIfNonNil(i.combinedClasspathFile),
-		//TODO(b/240308299) include AIDL information from Bazel
+		// TODO(b/240308299) include AIDL information from Bazel
+		// TODO: aconfig files?
 	})
 
 	i.maybeInstall(ctx, jarName, outputFile)
diff --git a/java/java_test.go b/java/java_test.go
index 4738304..dd98677 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2351,3 +2351,22 @@
 		`stable.core.platform.api.stubs`,
 	})
 }
+
+func TestJavaLibraryWithResourcesStem(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+    java_library {
+        name: "foo",
+        java_resource_dirs: ["test-jar"],
+        stem: "test",
+    }
+    `,
+		map[string][]byte{
+			"test-jar/test/resource.txt": nil,
+		})
+
+	m := ctx.ModuleForTests("foo", "android_common")
+	outputs := fmt.Sprint(m.AllOutputs())
+	if !strings.Contains(outputs, "test.jar") {
+		t.Errorf("Module output does not contain expected jar %s", "test.jar")
+	}
+}
diff --git a/java/kotlin.go b/java/kotlin.go
index f28d6c7..3637e2e 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -42,7 +42,7 @@
 			` -P plugin:org.jetbrains.kotlin.jvm.abi:outputDir=$headerClassesDir && ` +
 			`${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir -write_if_changed && ` +
 			`${config.SoongZipCmd} -jar -o $headerJar -C $headerClassesDir -D $headerClassesDir -write_if_changed && ` +
-			`rm -rf "$srcJarDir"`,
+			`rm -rf "$srcJarDir" "$classesDir" "$headerClassesDir"`,
 		CommandDeps: []string{
 			"${config.KotlincCmd}",
 			"${config.KotlinCompilerJar}",
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 0d4db7c..ade7395 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -113,7 +113,7 @@
 }
 
 func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	if ctx.Config().DisableHiddenApiChecks() {
 		return
 	}
 
@@ -123,15 +123,15 @@
 }
 
 func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
-	// Add dependencies on all the modules configured in the "art" boot image.
-	artImageConfig := genBootImageConfigs(ctx)[artBootImageName]
-	addDependenciesOntoBootImageModules(ctx, artImageConfig.modules, platformBootclasspathArtBootJarDepTag)
+	// Add dependencies on all the ART jars.
+	global := dexpreopt.GetGlobalConfig(ctx)
+	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag)
 
-	// Add dependencies on all the non-updatable module configured in the "boot" boot image. That does
-	// not include modules configured in the "art" boot image.
+	// Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable
+	// APEXes.
 	addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag)
 
-	// Add dependencies on all the apex jars.
+	// Add dependencies on all the updatable jars, except the ART jars.
 	apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
 	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
 
@@ -186,7 +186,6 @@
 	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
-	b.generateBootImageBuildActions(ctx)
 	b.copyApexBootJarsForAppsDexpreopt(ctx, apexModules)
 }
 
@@ -218,7 +217,8 @@
 }
 
 func (b *platformBootclasspathModule) platformJars(ctx android.PathContext) android.ConfiguredJarList {
-	return defaultBootImageConfig(ctx).modules.RemoveList(artBootImageConfig(ctx).modules)
+	global := dexpreopt.GetGlobalConfig(ctx)
+	return global.BootJars.RemoveList(global.ArtApexJars)
 }
 
 // checkPlatformModules ensures that the non-updatable modules supplied are not part of an
@@ -275,10 +275,10 @@
 
 	bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules)
 
-	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
+	// Don't run any hiddenapi rules if hidden api checks are disabled. This is a performance
 	// optimization that can be used to reduce the incremental build time but as its name suggests it
 	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	if ctx.Config().DisableHiddenApiChecks() {
 		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
 		for _, path := range paths {
 			ctx.Build(pctx, android.BuildParams{
@@ -399,78 +399,9 @@
 	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", b.hiddenAPIFlagsCSV.String())
 }
 
-// generateBootImageBuildActions generates ninja rules related to the boot image creation.
-func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext) {
-	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
-	// GenerateSingletonBuildActions method as it cannot create it for itself.
-	dexpreopt.GetGlobalSoongConfig(ctx)
-
-	global := dexpreopt.GetGlobalConfig(ctx)
-	if !shouldBuildBootImages(ctx.Config(), global) {
-		return
-	}
-
-	frameworkBootImageConfig := defaultBootImageConfig(ctx)
-	bootFrameworkProfileRule(ctx, frameworkBootImageConfig)
-	b.generateBootImage(ctx, frameworkBootImageName)
-	b.generateBootImage(ctx, mainlineBootImageName)
-	dumpOatRules(ctx, frameworkBootImageConfig)
-}
-
-func (b *platformBootclasspathModule) generateBootImage(ctx android.ModuleContext, imageName string) {
-	imageConfig := genBootImageConfigs(ctx)[imageName]
-
-	modules := b.getModulesForImage(ctx, imageConfig)
-
-	// Copy module dex jars to their predefined locations.
-	bootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, modules)
-	copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule)
-
-	// Build a profile for the image config and then use that to build the boot image.
-	profile := bootImageProfileRule(ctx, imageConfig)
-
-	// If dexpreopt of boot image jars should be skipped, generate only a profile.
-	global := dexpreopt.GetGlobalConfig(ctx)
-	if global.DisablePreoptBootImages {
-		return
-	}
-
-	// Build boot image files for the android variants.
-	androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
-
-	// Zip the android variant boot image files up.
-	buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFiles.byArch)
-
-	// Build boot image files for the host variants. There are use directly by ART host side tests.
-	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
-}
-
 // Copy apex module dex jars to their predefined locations. They will be used for dexpreopt for apps.
 func (b *platformBootclasspathModule) copyApexBootJarsForAppsDexpreopt(ctx android.ModuleContext, apexModules []android.Module) {
 	config := GetApexBootConfig(ctx)
 	apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules)
 	copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule)
 }
-
-func (b *platformBootclasspathModule) getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []android.Module {
-	modules := make([]android.Module, 0, imageConfig.modules.Len())
-	for i := 0; i < imageConfig.modules.Len(); i++ {
-		found := false
-		for _, module := range b.configuredModules {
-			name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-			if name == imageConfig.modules.Jar(i) {
-				modules = append(modules, module)
-				found = true
-				break
-			}
-		}
-		if !found && !ctx.Config().AllowMissingDependencies() {
-			ctx.ModuleErrorf(
-				"Boot image '%s' module '%s' not added as a dependency of platform_bootclasspath",
-				imageConfig.name,
-				imageConfig.modules.Jar(i))
-			return []android.Module{}
-		}
-	}
-	return modules
-}
diff --git a/python/bp2build.go b/python/bp2build.go
index cd3f2a1..60cabc4 100644
--- a/python/bp2build.go
+++ b/python/bp2build.go
@@ -223,7 +223,8 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		// Use the native py_binary rule.
-		Rule_class: "py_test",
+		Rule_class:        "py_test",
+		Bzl_load_location: "//build/bazel/rules/python:py_test.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
diff --git a/rust/binary.go b/rust/binary.go
index 2de92c1..e6f1539 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -158,9 +158,6 @@
 	// Binaries default to dylib dependencies for device, rlib for host.
 	if binary.preferRlib() {
 		return rlibAutoDep
-	} else if mod, ok := ctx.Module().(*Module); ok && mod.InVendor() {
-		// Vendor Rust binaries should prefer rlibs.
-		return rlibAutoDep
 	} else if ctx.Device() {
 		return dylibAutoDep
 	} else {
@@ -171,8 +168,6 @@
 func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
 	if binary.preferRlib() {
 		return RlibLinkage
-	} else if ctx.RustModule().InVendor() {
-		return RlibLinkage
 	}
 	return binary.baseCompiler.stdLinkage(ctx)
 }
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 96645b0..c2bf6af 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r487747c"
+	bindgenClangVersion = "clang-r498229"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -61,15 +61,18 @@
 		"${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/${bindgenClangLibdir}")
 
 	//TODO(ivanlozano) Switch this to RuleBuilder
+	//
+	//TODO Pass the flag files directly to bindgen e.g. with @file when it supports that.
+	//See https://github.com/rust-lang/rust-bindgen/issues/2508.
 	bindgen = pctx.AndroidStaticRule("bindgen",
 		blueprint.RuleParams{
 			Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " +
-				"$cmd $flags $in -o $out -- -MD -MF $out.d $cflags",
+				"$cmd $flags $$(cat $flagfiles) $in -o $out -- -MD -MF $out.d $cflags",
 			CommandDeps: []string{"$cmd"},
 			Deps:        blueprint.DepsGCC,
 			Depfile:     "$out.d",
 		},
-		"cmd", "flags", "cflags")
+		"cmd", "flags", "flagfiles", "cflags")
 )
 
 func init() {
@@ -90,6 +93,9 @@
 	// list of bindgen-specific flags and options
 	Bindgen_flags []string `android:"arch_variant"`
 
+	// list of files containing extra bindgen flags
+	Bindgen_flag_files []string `android:"arch_variant"`
+
 	// module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom
 	// binary must expect arguments in a similar fashion to bindgen, e.g.
 	//
@@ -216,6 +222,14 @@
 	bindgenFlags := defaultBindgenFlags
 	bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...)
 
+	// cat reads from stdin if its command line is empty,
+	// so we pass in /dev/null if there are no other flag files
+	bindgenFlagFiles := []string{"/dev/null"}
+	for _, flagFile := range b.Properties.Bindgen_flag_files {
+		bindgenFlagFiles = append(bindgenFlagFiles, android.PathForModuleSrc(ctx, flagFile).String())
+		implicits = append(implicits, android.PathForModuleSrc(ctx, flagFile))
+	}
+
 	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
 	if !wrapperFile.Valid() {
 		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
@@ -247,7 +261,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"
@@ -261,9 +275,10 @@
 		Input:       wrapperFile.Path(),
 		Implicits:   implicits,
 		Args: map[string]string{
-			"cmd":    cmd,
-			"flags":  strings.Join(bindgenFlags, " "),
-			"cflags": strings.Join(cflags, " "),
+			"cmd":       cmd,
+			"flags":     strings.Join(bindgenFlags, " "),
+			"flagfiles": strings.Join(bindgenFlagFiles, " "),
+			"cflags":    strings.Join(cflags, " "),
 		},
 	})
 
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index af04cfc..12cdb3c 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -168,3 +168,28 @@
 		}
 	`)
 }
+
+func TestBindgenFlagFile(t *testing.T) {
+	ctx := testRust(t, `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			bindgen_flag_files: [
+				"flag_file.txt",
+			],
+		}
+	`)
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+
+	if !strings.Contains(libbindgen.Args["flagfiles"], "/dev/null") {
+		t.Errorf("missing /dev/null in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
+	}
+	if !strings.Contains(libbindgen.Args["flagfiles"], "flag_file.txt") {
+		t.Errorf("missing bindgen flags file in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
+	}
+	// TODO: The best we can do right now is check $flagfiles. Once bindgen.go switches to RuleBuilder,
+	// we may be able to check libbinder.RuleParams.Command to see if it contains $(cat /dev/null flag_file.txt)
+}
diff --git a/rust/builder.go b/rust/builder.go
index c31bc88..fbceecc 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -228,6 +228,17 @@
 		pkgVersion := ctx.RustModule().compiler.CargoPkgVersion()
 		if pkgVersion != "" {
 			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
+
+			// Ensure the version is in the form of "x.y.z" (approximately semver compliant).
+			//
+			// For our purposes, we don't care to enforce that these are integers since they may
+			// include other characters at times (e.g. sometimes the patch version is more than an integer).
+			if strings.Count(pkgVersion, ".") == 2 {
+				var semver_parts = strings.Split(pkgVersion, ".")
+				envVars = append(envVars, "CARGO_PKG_VERSION_MAJOR="+semver_parts[0])
+				envVars = append(envVars, "CARGO_PKG_VERSION_MINOR="+semver_parts[1])
+				envVars = append(envVars, "CARGO_PKG_VERSION_PATCH="+semver_parts[2])
+			}
 		}
 	}
 
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index ae783e8..08ac2ef 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -26,11 +26,18 @@
 	Arm64LinkFlags            = []string{}
 
 	Arm64ArchVariantRustFlags = map[string][]string{
-		"armv8-a":            []string{},
-		"armv8-a-branchprot": []string{},
-		"armv8-2a":           []string{},
-		"armv8-2a-dotprod":   []string{},
-		"armv9-a":            []string{},
+		"armv8-a": []string{},
+		"armv8-a-branchprot": []string{
+			// branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard
+			"-Z branch-protection=bti,pac-ret",
+		},
+		"armv8-2a":         []string{},
+		"armv8-2a-dotprod": []string{},
+		"armv9-a": []string{
+			// branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard
+			"-Z branch-protection=bti,pac-ret",
+			"-Z stack-protector=none",
+		},
 	}
 )
 
diff --git a/rust/config/global.go b/rust/config/global.go
index d844a25..c39341e 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.70.0"
+	RustDefaultVersion = "1.71.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/image.go b/rust/image.go
index 50bf02a..c2e250c 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -220,9 +220,6 @@
 }
 
 func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	// Rust does not support installing to the product image yet.
-	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
-
 	if Bool(mod.VendorProperties.Double_loadable) {
 		mctx.PropertyErrorf("double_loadable",
 			"Rust modules do not yet support double loading")
@@ -232,16 +229,6 @@
 			mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
 		}
 	}
-	if vendorSpecific {
-		if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
-			mctx.PropertyErrorf("vendor", "Vendor-only dylibs are not yet supported, use rust_library_rlib.")
-		}
-	}
-	if mctx.ProductSpecific() {
-		if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
-			mctx.PropertyErrorf("product", "Product-only dylibs are not yet supported, use rust_library_rlib.")
-		}
-	}
 
 	cc.MutateImage(mctx, mod)
 
diff --git a/rust/library.go b/rust/library.go
index 331763a..3f480a2 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -21,7 +21,6 @@
 
 	"android/soong/android"
 	"android/soong/cc"
-	"android/soong/snapshot"
 )
 
 var (
@@ -236,10 +235,7 @@
 }
 
 func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
-	if ctx.Module().(*Module).InVendor() {
-		// Vendor modules should statically link libstd.
-		return rlibAutoDep
-	} else if library.preferRlib() {
+	if library.preferRlib() {
 		return rlibAutoDep
 	} else if library.rlib() || library.static() {
 		return rlibAutoDep
@@ -251,10 +247,7 @@
 }
 
 func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if ctx.RustModule().InVendor() {
-		// Vendor modules should statically link libstd.
-		return RlibLinkage
-	} else if library.static() || library.MutatedProperties.VariantIsStaticStd {
+	if library.static() || library.MutatedProperties.VariantIsStaticStd {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
@@ -693,24 +686,6 @@
 				v.(*Module).Disable()
 			}
 
-			variation := v.(*Module).ModuleBase.ImageVariation().Variation
-			if strings.HasPrefix(variation, cc.VendorVariationPrefix) {
-				// TODO(b/204303985)
-				// Disable vendor dylibs until they are supported
-				v.(*Module).Disable()
-			}
-
-			if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
-				m.HasVendorVariant() &&
-				!snapshot.IsVendorProprietaryModule(mctx) &&
-				strings.TrimPrefix(variation, cc.VendorVariationPrefix) == mctx.DeviceConfig().VndkVersion() {
-
-				// cc.MutateImage runs before LibraryMutator, so vendor variations which are meant for rlibs only are
-				// produced for Dylibs; however, dylibs should not be enabled for boardVndkVersion for
-				// non-vendor proprietary modules.
-				v.(*Module).Disable()
-			}
-
 		case "source":
 			v.(*Module).compiler.(libraryInterface).setSource()
 			// The source variant does not produce any library.
@@ -747,10 +722,9 @@
 				dylib := modules[1].(*Module)
 				rlib.compiler.(libraryInterface).setRlibStd()
 				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation ||
-					strings.HasPrefix(dylib.ModuleBase.ImageVariation().Variation, cc.VendorVariationPrefix) {
+				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
 					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on vendor and vendor ramdisk variations until those dylib
+					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
 					// variants are properly supported.
 					dylib.Disable()
 				}
diff --git a/rust/rust.go b/rust/rust.go
index e524c9f..dab3532 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -43,6 +43,7 @@
 	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
+	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
 	android.InitRegistrationContext.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
@@ -91,6 +92,8 @@
 	// Used by vendor snapshot to record dependencies from snapshot modules.
 	SnapshotSharedLibs []string `blueprint:"mutated"`
 	SnapshotStaticLibs []string `blueprint:"mutated"`
+	SnapshotRlibs      []string `blueprint:"mutated"`
+	SnapshotDylibs     []string `blueprint:"mutated"`
 
 	// Make this module available when building for ramdisk.
 	// On device without a dedicated recovery partition, the module is only
@@ -258,6 +261,15 @@
 	return false
 }
 
+func (mod *Module) RlibStd() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok && library.rlib() {
+			return library.rlibStd()
+		}
+	}
+	panic(fmt.Errorf("RlibStd() called on non-rlib module: %q", mod.BaseModuleName()))
+}
+
 func (mod *Module) Rlib() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -1225,6 +1237,8 @@
 				}
 				directDylibDeps = append(directDylibDeps, rustDep)
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
+				mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
+
 			case rlibDepTag:
 
 				rlib, ok := rustDep.compiler.(libraryInterface)
@@ -1234,6 +1248,8 @@
 				}
 				directRlibDeps = append(directRlibDeps, rustDep)
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
+				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
+
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
@@ -1290,12 +1306,17 @@
 				}
 			}
 			linkObject := ccDep.OutputFile()
-			linkPath := linkPathFromFilePath(linkObject.Path())
-
 			if !linkObject.Valid() {
-				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+				if !ctx.Config().AllowMissingDependencies() {
+					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+				} else {
+					ctx.AddMissingDependencies([]string{depName})
+				}
+				return
 			}
 
+			linkPath := linkPathFromFilePath(linkObject.Path())
+
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
@@ -1340,6 +1361,14 @@
 				// Re-get linkObject as ChooseStubOrImpl actually tells us which
 				// object (either from stub or non-stub) to use.
 				linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
+				if !linkObject.Valid() {
+					if !ctx.Config().AllowMissingDependencies() {
+						ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+					} else {
+						ctx.AddMissingDependencies([]string{depName})
+					}
+					return
+				}
 				linkPath = linkPathFromFilePath(linkObject.Path())
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
@@ -1518,10 +1547,10 @@
 	}
 
 	// dylibs
-	actx.AddVariationDependencies(
-		append(commonDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: dylibVariation}}...),
-		dylibDepTag, deps.Dylibs...)
+	dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation})
+	for _, lib := range deps.Dylibs {
+		addDylibDependency(actx, lib, mod, &snapshotInfo, dylibDepVariations, dylibDepTag)
+	}
 
 	// rustlibs
 	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
@@ -1536,8 +1565,11 @@
 				// otherwise select the rlib variant.
 				autoDepVariations := append(commonDepVariations,
 					blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation})
-				if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) {
-					actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib)
+
+				replacementLib := cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Dylibs)
+
+				if actx.OtherModuleDependencyVariantExists(autoDepVariations, replacementLib) {
+					addDylibDependency(actx, lib, mod, &snapshotInfo, autoDepVariations, autoDep.depTag)
 				} else {
 					// If there's no dylib dependency available, try to add the rlib dependency instead.
 					addRlibDependency(actx, lib, mod, &snapshotInfo, rlibDepVariations)
@@ -1549,16 +1581,14 @@
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
-				depTag := rlibDepTag
 				lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
-
 				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
-					depTag, lib)
+					rlibDepTag, lib)
 			}
 		} else {
-			actx.AddVariationDependencies(
-				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}),
-				dylibDepTag, deps.Stdlibs...)
+			for _, lib := range deps.Stdlibs {
+				addDylibDependency(actx, lib, mod, &snapshotInfo, dylibDepVariations, dylibDepTag)
+			}
 		}
 	}
 
@@ -1636,6 +1666,11 @@
 	actx.AddVariationDependencies(variations, rlibDepTag, lib)
 }
 
+func addDylibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo **cc.SnapshotInfo, variations []blueprint.Variation, depTag dependencyTag) {
+	lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, snapshotInfo, actx).Dylibs)
+	actx.AddVariationDependencies(variations, depTag, lib)
+}
+
 func BeginMutator(ctx android.BottomUpMutatorContext) {
 	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
 		mod.beginMutator(ctx)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 64f90b6..3f4e296 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -79,14 +79,18 @@
 }
 
 const (
-	sharedVendorVariant   = "android_vendor.29_arm64_armv8-a_shared"
-	rlibVendorVariant     = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
-	sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared"
-	rlibRecoveryVariant   = "android_recovery_arm64_armv8-a_rlib_rlib-std"
-	binaryCoreVariant     = "android_arm64_armv8-a"
-	binaryVendorVariant   = "android_vendor.29_arm64_armv8-a"
-	binaryProductVariant  = "android_product.29_arm64_armv8-a"
-	binaryRecoveryVariant = "android_recovery_arm64_armv8-a"
+	sharedVendorVariant        = "android_vendor.29_arm64_armv8-a_shared"
+	rlibVendorVariant          = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+	rlibDylibStdVendorVariant  = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+	dylibVendorVariant         = "android_vendor.29_arm64_armv8-a_dylib"
+	sharedRecoveryVariant      = "android_recovery_arm64_armv8-a_shared"
+	rlibRecoveryVariant        = "android_recovery_arm64_armv8-a_rlib_dylib-std"
+	rlibRlibStdRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std"
+	dylibRecoveryVariant       = "android_recovery_arm64_armv8-a_dylib"
+	binaryCoreVariant          = "android_arm64_armv8-a"
+	binaryVendorVariant        = "android_vendor.29_arm64_armv8-a"
+	binaryProductVariant       = "android_product.29_arm64_armv8-a"
+	binaryRecoveryVariant      = "android_recovery_arm64_armv8-a"
 )
 
 func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 0f7cf6e..862baf7 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -223,11 +223,16 @@
 	if !sanitize.Properties.SanitizerEnabled {
 		return flags, deps
 	}
+
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
-	} else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+	}
+
+	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
 		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
-	} else if Bool(sanitize.Properties.Sanitize.Address) {
+	}
+
+	if Bool(sanitize.Properties.Sanitize.Address) {
 		flags.RustFlags = append(flags.RustFlags, asanFlags...)
 	}
 	return flags, deps
@@ -267,14 +272,12 @@
 		var depTag blueprint.DependencyTag
 		var deps []string
 
-		if mod.IsSanitizerEnabled(cc.Asan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && (mctx.Arch().ArchType != android.Arm64 || !mctx.Os().Bionic())) {
+		if mod.IsSanitizerEnabled(cc.Asan) {
 			variations = append(variations,
 				blueprint.Variation{Mutator: "link", Variation: "shared"})
 			depTag = cc.SharedDepTag()
 			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
-		} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64 && mctx.Os().Bionic()) {
+		} else if mod.IsSanitizerEnabled(cc.Hwasan) {
 			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
 			if binary, ok := mod.compiler.(binaryInterface); ok {
 				if binary.staticallyLinked() {
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
index 2f79cc5..32d3916 100644
--- a/rust/snapshot_prebuilt.go
+++ b/rust/snapshot_prebuilt.go
@@ -21,10 +21,6 @@
 	"github.com/google/blueprint/proptools"
 )
 
-const (
-	snapshotRlibSuffix = "_rlib."
-)
-
 type snapshotLibraryDecorator struct {
 	cc.BaseSnapshotDecorator
 	*libraryDecorator
@@ -44,6 +40,8 @@
 func registerRustSnapshotModules(ctx android.RegistrationContext) {
 	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
 		"vendor_snapshot_rlib", VendorSnapshotRlibFactory)
+	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
+		"vendor_snapshot_dylib", VendorSnapshotDylibFactory)
 	cc.RecoverySnapshotImageSingleton.RegisterAdditionalModule(ctx,
 		"recovery_snapshot_rlib", RecoverySnapshotRlibFactory)
 }
@@ -77,12 +75,11 @@
 		variant = cc.SnapshotSharedSuffix
 	} else if library.rlib() {
 		variant = cc.SnapshotRlibSuffix
+	} else if library.dylib() {
+		variant = cc.SnapshotDylibSuffix
 	}
 
-	if !library.dylib() {
-		// TODO(184042776): Remove this check when dylibs are supported in snapshots.
-		library.SetSnapshotAndroidMkSuffix(ctx, variant)
-	}
+	library.SetSnapshotAndroidMkSuffix(ctx, variant)
 
 	if !library.MatchesWithDevice(ctx.DeviceConfig()) {
 		return buildOutput{}
@@ -107,6 +104,17 @@
 	return module.Init()
 }
 
+// vendor_snapshot_dylib is a special prebuilt dylib library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_dylib
+// overrides the vendor variant of the rust dylib library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotDylibFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(cc.VendorSnapshotImageSingleton, cc.SnapshotDylibSuffix)
+	prebuilt.libraryDecorator.BuildOnlyDylib()
+	prebuilt.libraryDecorator.setNoStdlibs()
+	return module.Init()
+}
+
 func RecoverySnapshotRlibFactory() android.Module {
 	module, prebuilt := snapshotLibraryFactory(cc.RecoverySnapshotImageSingleton, cc.SnapshotRlibSuffix)
 	prebuilt.libraryDecorator.BuildOnlyRlib()
diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go
index 8dabd9b..55c85e6 100644
--- a/rust/snapshot_utils.go
+++ b/rust/snapshot_utils.go
@@ -42,8 +42,7 @@
 
 func (mod *Module) IsSnapshotLibrary() bool {
 	if lib, ok := mod.compiler.(libraryInterface); ok {
-		// Rust-native dylibs are not snapshot supported yet. Only snapshot the rlib-std variants of rlibs.
-		return lib.shared() || lib.static() || (lib.rlib() && lib.rlibStd())
+		return lib.shared() || lib.static() || lib.rlib() || lib.dylib()
 	}
 	return false
 }
@@ -61,6 +60,14 @@
 	return mod.Properties.SnapshotStaticLibs
 }
 
+func (mod *Module) SnapshotRlibs() []string {
+	return mod.Properties.SnapshotRlibs
+}
+
+func (mod *Module) SnapshotDylibs() []string {
+	return mod.Properties.SnapshotDylibs
+}
+
 func (mod *Module) Symlinks() []string {
 	// TODO update this to return the list of symlinks when Rust supports defining symlinks
 	return nil
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 2e7a330..387d170 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -48,15 +48,13 @@
 		crate_name: "rustvendor_available",
 		srcs: ["lib.rs"],
 		vendor_available: true,
-		include_dirs: ["rust_headers/"],
 	}
 
-	rust_library_rlib {
+	rust_library {
 		name: "librustvendor",
 		crate_name: "rustvendor",
 		srcs: ["lib.rs"],
 		vendor: true,
-		include_dirs: ["rust_headers/"],
 	}
 
 	rust_binary {
@@ -116,7 +114,7 @@
 			filepath.Join(staticDir, "libffivendor.a.json"))
 
 		// For rlib libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_dylib-std", archType, archVariant)
 		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.rlib", rlibDir, rlibVariant)
@@ -125,6 +123,25 @@
 		jsonFiles = append(jsonFiles,
 			filepath.Join(rlibDir, "librustvendor.rlib.json"))
 
+		// For rlib libraries, all rlib-std variants vendor:true and vendor_available modules (including VNDK) are captured.
+		rlibStdVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib-std.rlib", rlibDir, rlibStdVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.rlib-std.rlib", rlibDir, rlibStdVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librustvendor.rlib.json"))
+
+		// For dylib libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		dylibVariant := fmt.Sprintf("android_vendor.29_%s_%s_dylib", archType, archVariant)
+		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.dylib.so", dylibDir, dylibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.dylib.so", dylibDir, dylibVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(dylibDir, "librustvendor_available.dylib.so.json"))
+		jsonFiles = append(jsonFiles,
+			filepath.Join(dylibDir, "librustvendor.dylib.so.json"))
+
 		// For binary executables, all vendor:true and vendor_available modules are captured.
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
@@ -209,21 +226,32 @@
 		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
 
 		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_dylib-std", archType, archVariant)
+		rlibRlibStdVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+		dylibVariant := fmt.Sprintf("android_vendor.29_%s_%s_dylib", archType, archVariant)
+		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
 
 		// Included modules
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.dylib.so", dylibDir, dylibVariant)
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_available.rlib-std.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librustvendor_available.dylib.so.json"))
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_available.so.json"))
 
 		// Excluded modules. Modules not included in the directed vendor snapshot
 		// are still include as fake modules.
 		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.dylib.so", dylibDir, dylibVariant)
 		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "libffivendor_exclude", "libffivendor_exclude.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_exclude.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_exclude.rlib-std.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librustvendor_exclude.dylib.so.json"))
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_exclude.so.json"))
 	}
 
@@ -274,7 +302,7 @@
 			vendor_available: true,
 		}
 
-		rust_library_rlib {
+		rust_library {
 			name: "librust_exclude",
 			crate_name: "rust_exclude",
 			srcs: ["exclude.rs"],
@@ -308,6 +336,14 @@
 	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, rlibVendorVariant)
 	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, rlibVendorVariant)
 
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, rlibDylibStdVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, rlibDylibStdVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, rlibDylibStdVendorVariant)
+
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, dylibVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, dylibVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, dylibVendorVariant)
+
 	// Verify the content of the vendor snapshot.
 
 	snapshotDir := "vendor-snapshot"
@@ -327,14 +363,22 @@
 
 		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_dylib-std", archType, archVariant)
+		rlibRlibStdVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
 		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+		dylibVariant := fmt.Sprintf("android_vendor.29_%s_%s_dylib", archType, archVariant)
+		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
 
 		// Included modules
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.rlib", rlibDir, rlibVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librust_include.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librust_include.rlib-std.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.dylib.so", dylibDir, dylibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librust_include.dylib.so.json"))
 
 		// Excluded modules
 		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
@@ -345,6 +389,12 @@
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_exclude.rlib.json"))
 		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.rlib", rlibDir, rlibVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_available_exclude.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_available_exclude.rlib.rlib-std.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_exclude", "librust_exclude.dylib.so", dylibDir, dylibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(dylibDir, "librust_exclude.dylib.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.dylib.so", dylibDir, dylibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(dylibDir, "librust_available_exclude.dylib.so.json"))
 	}
 
 	// Verify that each json file for an included module has a rule.
@@ -525,7 +575,7 @@
 		srcs: ["client.rs"],
 	}
 
-	rust_library_rlib {
+	rust_library {
 		name: "libclient_rust",
 		crate_name: "client_rust",
 		vendor: true,
@@ -572,6 +622,11 @@
 				rlibs: [
 					"libstd",
 					"librust_vendor_available",
+					"librust_vendor_available.rlib-std"
+				],
+				dylibs: [
+					"libstd",
+					"librust_vendor_available",
 				],
 				binaries: [
 					"bin",
@@ -600,6 +655,10 @@
 					"libstd",
 					"librust_vendor_available",
 				],
+				dylibs: [
+					"libstd",
+					"librust_vendor_available",
+				],
 				binaries: [
 					"bin32",
 				],
@@ -679,6 +738,52 @@
 		},
 	}
 
+	vendor_snapshot_rlib {
+		name: "librust_vendor_available.rlib-std",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "librust_vendor_available.rlib-std.rlib",
+			},
+			arm: {
+				src: "librust_vendor_available.rlib-std.rlib",
+			},
+		},
+	}
+
+	vendor_snapshot_dylib {
+		name: "libstd",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		sysroot: true,
+		arch: {
+			arm64: {
+				src: "libstd.dylib.so",
+			},
+			arm: {
+				src: "libstd.dylib.so",
+			},
+		},
+	}
+
+	vendor_snapshot_dylib {
+		name: "librust_vendor_available",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "librust_vendor_available.dylib.so",
+			},
+			arm: {
+				src: "librust_vendor_available.dylib.so",
+			},
+		},
+	}
+
 	vendor_snapshot_object {
 		name: "crtend_android",
 		version: "30",
@@ -921,6 +1026,9 @@
 		"vendor/liblog.so":                              nil,
 		"vendor/libstd.rlib":                            nil,
 		"vendor/librust_vendor_available.rlib":          nil,
+		"vendor/librust_vendor_available.rlib-std.rlib": nil,
+		"vendor/libstd.dylib.so":                        nil,
+		"vendor/librust_vendor_available.dylib.so":      nil,
 		"vendor/crtbegin_so.o":                          nil,
 		"vendor/crtend_so.o":                            nil,
 		"vendor/libclang_rt.builtins-aarch64-android.a": nil,
@@ -931,7 +1039,9 @@
 	}
 
 	sharedVariant := "android_vendor.30_arm64_armv8-a_shared"
-	rlibVariant := "android_vendor.30_arm64_armv8-a_rlib_rlib-std"
+	rlibVariant := "android_vendor.30_arm64_armv8-a_rlib_dylib-std"
+	rlibRlibStdVariant := "android_vendor.30_arm64_armv8-a_rlib_rlib-std"
+	dylibVariant := "android_vendor.30_arm64_armv8-a_dylib"
 	staticVariant := "android_vendor.30_arm64_armv8-a_static"
 	binaryVariant := "android_vendor.30_arm64_armv8-a"
 
@@ -963,14 +1073,9 @@
 		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
 	}
 
-	libclientAndroidMkRlibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkRlibs
-	if g, w := libclientAndroidMkRlibs, []string{"librust_vendor_available.vendor.rlib-std", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
-	}
-
 	libclientAndroidMkDylibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkDylibs
-	if len(libclientAndroidMkDylibs) > 0 {
-		t.Errorf("wanted libclient libclientAndroidMkDylibs [], got %q", libclientAndroidMkDylibs)
+	if g, w := libclientAndroidMkDylibs, []string{"librust_vendor_available.vendor", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient libclientAndroidMkDylibs %q, got %q", w, libclientAndroidMkDylibs)
 	}
 
 	libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs
@@ -979,22 +1084,39 @@
 	}
 
 	libclientRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibVariant).Module().(*Module).Properties.AndroidMkRlibs
-	if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor.rlib-std", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted rlib libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	}
+
+	libclientRlibStdRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibRlibStdVariant).Module().(*Module).Properties.AndroidMkRlibs
+	if g, w := libclientRlibStdRustAndroidMkRlibs, []string{"librust_vendor_available.vendor.rlib-std", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted rlib libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	}
+
+	libclientRustDylibAndroidMkDylibs := ctx.ModuleForTests("libclient_rust", dylibVariant).Module().(*Module).Properties.AndroidMkDylibs
+	if g, w := libclientRustDylibAndroidMkDylibs, []string{"librust_vendor_available.vendor", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted dylib libclient libclientRustDylibAndroidMkDylibs %q, got %q", w, g)
 	}
 
 	// rust vendor snapshot must have ".vendor" suffix in AndroidMk
 	librustVendorAvailableSnapshotModule := ctx.ModuleForTests("librust_vendor_available.vendor_rlib.30.arm64", rlibVariant).Module()
 	librustVendorSnapshotMkName := android.AndroidMkEntriesForTest(t, ctx, librustVendorAvailableSnapshotModule)[0].EntryMap["LOCAL_MODULE"][0]
-	expectedRustVendorSnapshotName := "librust_vendor_available.vendor.rlib-std"
+	expectedRustVendorSnapshotName := "librust_vendor_available.vendor"
 	if librustVendorSnapshotMkName != expectedRustVendorSnapshotName {
 		t.Errorf("Unexpected rust vendor snapshot name in AndroidMk: %q, expected: %q\n", librustVendorSnapshotMkName, expectedRustVendorSnapshotName)
 	}
 
+	librustVendorAvailableDylibSnapshotModule := ctx.ModuleForTests("librust_vendor_available.vendor_dylib.30.arm64", dylibVariant).Module()
+	librustVendorSnapshotDylibMkName := android.AndroidMkEntriesForTest(t, ctx, librustVendorAvailableDylibSnapshotModule)[0].EntryMap["LOCAL_MODULE"][0]
+	expectedRustVendorDylibSnapshotName := "librust_vendor_available.vendor"
+	if librustVendorSnapshotDylibMkName != expectedRustVendorDylibSnapshotName {
+		t.Errorf("Unexpected rust vendor snapshot name in AndroidMk: %q, expected: %q\n", librustVendorSnapshotDylibMkName, expectedRustVendorDylibSnapshotName)
+	}
+
 	rustVendorBinModule := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Module()
-	rustVendorBinMkRlibName := android.AndroidMkEntriesForTest(t, ctx, rustVendorBinModule)[0].EntryMap["LOCAL_RLIB_LIBRARIES"][0]
-	if rustVendorBinMkRlibName != expectedRustVendorSnapshotName {
-		t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkRlibName, expectedRustVendorSnapshotName)
+	rustVendorBinMkDylibName := android.AndroidMkEntriesForTest(t, ctx, rustVendorBinModule)[0].EntryMap["LOCAL_DYLIB_LIBRARIES"][0]
+	if rustVendorBinMkDylibName != expectedRustVendorSnapshotName {
+		t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkDylibName, expectedRustVendorSnapshotName)
 	}
 
 	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustLink").Args["linkFlags"]
@@ -1051,18 +1173,18 @@
 		crate_name: "recovery_available",
 	}
 
-	rust_library_rlib {
-		name: "librecovery_rlib",
+	rust_library {
+		name: "librecovery_rustlib",
 		recovery: true,
 		srcs: ["foo.rs"],
-		crate_name: "recovery_rlib",
+		crate_name: "recovery_rustlib",
 	}
 
-	rust_library_rlib {
-		name: "librecovery_available_rlib",
+	rust_library {
+		name: "librecovery_available_rustlib",
 		recovery_available: true,
 		srcs: ["foo.rs"],
-		crate_name: "recovery_available_rlib",
+		crate_name: "recovery_available_rustlib",
 	}
 
 	rust_binary {
@@ -1113,13 +1235,29 @@
 			filepath.Join(staticDir, "librecovery_available.a.json"))
 
 		// For rlib libraries, all recovery:true and recovery_available modules are captured.
-		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_dylib-std", archType, archVariant)
 		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rlib", "librecovery_available_rlib.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib", rlibDir, rlibVariant)
 		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librecovery_rlib.rlib.json"),
-			filepath.Join(rlibDir, "librecovery_available_rlib.rlib.json"))
+			filepath.Join(rlibDir, "librecovery_rustlib.rlib.json"),
+			filepath.Join(rlibDir, "librecovery_available_rustlib.rlib.json"))
+
+		rlibRlibStdVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librecovery_rustlib.rlib-std.rlib.json"),
+			filepath.Join(rlibDir, "librecovery_available_rustlib.rlib-std.rlib.json"))
+
+		// For dylib libraries, all recovery:true and recovery_available modules are captured.
+		dylibVariant := fmt.Sprintf("android_recovery_%s_%s_dylib", archType, archVariant)
+		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.dylib.so", dylibDir, dylibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.dylib.so", dylibDir, dylibVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(dylibDir, "librecovery_rustlib.dylib.so.json"),
+			filepath.Join(dylibDir, "librecovery_available_rustlib.dylib.so.json"))
 
 		// For binary executables, all recovery:true and recovery_available modules are captured.
 		if archType == "arm64" {
@@ -1169,25 +1307,25 @@
 			exclude_from_recovery_snapshot: true,
 			crate_name: "available_exclude",
 		}
-		rust_library_rlib {
-			name: "libinclude_rlib",
+		rust_library {
+			name: "libinclude_rustlib",
 			srcs: ["src/include.rs"],
 			recovery_available: true,
-			crate_name: "include_rlib",
+			crate_name: "include_rustlib",
 		}
-		rust_library_rlib {
-			name: "libexclude_rlib",
+		rust_library {
+			name: "libexclude_rustlib",
 			srcs: ["src/exclude.rs"],
 			recovery: true,
 			exclude_from_recovery_snapshot: true,
-			crate_name: "exclude_rlib",
+			crate_name: "exclude_rustlib",
 		}
-		rust_library_rlib {
-			name: "libavailable_exclude_rlib",
+		rust_library {
+			name: "libavailable_exclude_rustlib",
 			srcs: ["src/exclude.rs"],
 			recovery_available: true,
 			exclude_from_recovery_snapshot: true,
-			crate_name: "available_exclude_rlib",
+			crate_name: "available_exclude_rustlib",
 		}
 	`
 
@@ -1198,11 +1336,11 @@
 			recovery: true,
 			crate_name: "recovery",
 		}
-		rust_library_rlib {
-			name: "librecovery_rlib",
+		rust_library {
+			name: "librecovery_rustlib",
 			srcs: ["recovery.rs"],
 			recovery: true,
-			crate_name: "recovery_rlib",
+			crate_name: "recovery_rustlib",
 		}
 	`
 
@@ -1220,14 +1358,25 @@
 	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, sharedRecoveryVariant)
 	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, sharedRecoveryVariant)
 	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, sharedRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rlib", false, rlibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rlib", true, rlibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rlib", true, rlibRecoveryVariant)
+
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rustlib", false, rlibRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rustlib", true, rlibRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rustlib", true, rlibRlibStdRecoveryVariant)
+
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rustlib", false, rlibRlibStdRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rustlib", true, rlibRlibStdRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rustlib", true, rlibRlibStdRecoveryVariant)
+
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rustlib", false, dylibRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rustlib", true, dylibRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rustlib", true, dylibRecoveryVariant)
 
 	// A recovery module is excluded, but by its path not the exclude_from_recovery_snapshot property
 	// ('device/' and 'vendor/' are default excluded). See snapshot/recovery_snapshot.go for more detail.
 	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, sharedRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rlib", false, rlibRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rustlib", false, rlibRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rustlib", false, rlibRlibStdRecoveryVariant)
+	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rustlib", false, dylibRecoveryVariant)
 
 	// Verify the content of the recovery snapshot.
 
@@ -1246,15 +1395,21 @@
 		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
 
 		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_dylib-std", archType, archVariant)
+		rlibRlibStdVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+		dylibVariant := fmt.Sprintf("android_recovery_%s_%s_dylib", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
 
 		// Included modules
+
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rlib", "libinclude_rlib.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rlib.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rustlib", "libinclude_rustlib.rlib", rlibDir, rlibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rustlib.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rustlib", "libinclude_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rustlib.rlib-std.rlib.json"))
 
 		// Excluded modules
 		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
@@ -1263,12 +1418,27 @@
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
 		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rlib", "libexclude_rlib.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rlib.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rlib.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rlib", "libavailable_exclude_rlib.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rlib.rlib.json"))
+
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rustlib", "libexclude_rustlib.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rustlib.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rustlib", "libavailable_exclude_rustlib.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rustlib.rlib.json"))
+
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rustlib", "libexclude_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rustlib.rlib-std.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib-std.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rustlib", "libavailable_exclude_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rustlib.rlib-std.rlib.json"))
+
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rustlib", "libexclude_rustlib.dylib.so", dylibDir, dylibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rustlib.dylib.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.dylib.so", dylibDir, dylibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.dylib.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rustlib", "libavailable_exclude_rustlib.dylib.so", dylibDir, dylibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rustlib.dylib.so.json"))
 	}
 
 	// Verify that each json file for an included module has a rule.
@@ -1302,15 +1472,15 @@
 		srcs: ["foo.rs"],
 	}
 
-	rust_library_rlib {
-		name: "librecovery_rlib",
+	rust_library {
+		name: "librecovery_rustlib",
 		recovery: true,
 		crate_name: "recovery",
 		srcs: ["foo.rs"],
 	}
 
-	rust_library_rlib {
-		name: "librecovery_available_rlib",
+	rust_library {
+		name: "librecovery_available_rustlib",
 		recovery_available: true,
 		crate_name: "recovery_available",
 		srcs: ["foo.rs"],
@@ -1335,7 +1505,7 @@
 	ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "current", "29", "current")
 	ctx.Config().TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
 	ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery"] = true
-	ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery_rlib"] = true
+	ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery_rustlib"] = true
 	ctx.Config().TestProductVariables.DirectedRecoverySnapshot = true
 
 	// Check recovery snapshot output.
@@ -1353,15 +1523,22 @@
 		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
 
 		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_dylib-std", archType, archVariant)
+		rlibRlibStdVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+		dylibVariant := fmt.Sprintf("android_recovery_%s_%s_dylib", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
 
 		// Included modules
 		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rlib.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib", rlibDir, rlibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib-std.rlib.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.dylib.so", dylibDir, dylibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librecovery_rustlib.dylib.so.json"))
 
 		// TODO: When Rust supports the "prefer" property for prebuilts, perform this check.
 		/*
@@ -1374,8 +1551,12 @@
 		// are still included as fake modules.
 		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rlib", "librecovery_available_rlib.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rlib.rlib.json"))
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib", rlibDir, rlibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rustlib.rlib.json"))
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rustlib.rlib-std.rlib.json"))
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.dylib.so", dylibDir, dylibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librecovery_available_rustlib.dylib.so.json"))
 	}
 
 	// Verify that each json file for an included module has a rule.
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
index 2a20e45..18f4da2 100755
--- a/scripts/unpack-prebuilt-apex.sh
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -17,7 +17,7 @@
 # limitations under the License.
 
 # Tool to unpack an apex file and verify that the required files were extracted.
-if [ $# -lt 7 ]; then
+if [ $# -lt 6 ]; then
   echo "usage: $0 <deapaxer_path> <debugfs_path> <fsck.erofs_path> <apex file> <output_dir> <required_files>+" >&2
   exit 1
 fi
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index d830968..ee1b5db 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,18 +142,18 @@
 	).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.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+	java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
+	java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
 
 	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(`
 // 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,24 +213,24 @@
 					java.ApexBootJarDexJarPaths...,
 				)...,
 			)
-			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/mybootclasspathfragment/android_common_com.android.art/meta_lic")
-			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
+			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
 		}),
 
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
 
 		// 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.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
+			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
 		}),
 
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
 
 		// 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.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
+			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
 		}),
 	)
 
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index c018671..680494f 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -160,7 +160,7 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac-header/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
 `),
 	)
@@ -206,7 +206,7 @@
 `),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
-.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac-header/myjavalib.jar -> java/linux_glibc/myjavalib.jar
 `),
 	)
 }
@@ -799,7 +799,7 @@
     libs: ["mysdk_system-module"],
 }
 `),
-		checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac/system-module.jar -> java/system-module.jar"),
+		checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac-header/system-module.jar -> java/system-module.jar"),
 	)
 }
 
@@ -879,7 +879,7 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/hostjavalib/linux_glibc_common/javac/hostjavalib.jar -> java/hostjavalib.jar
+.intermediates/hostjavalib/linux_glibc_common/javac-header/hostjavalib.jar -> java/hostjavalib.jar
 .intermediates/androidjavalib/android_common/turbine-combined/androidjavalib.jar -> java/androidjavalib.jar
 .intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/android/myjavalib.jar
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
diff --git a/starlark_import/unmarshal.go b/starlark_import/unmarshal.go
index 1b54437..b243471 100644
--- a/starlark_import/unmarshal.go
+++ b/starlark_import/unmarshal.go
@@ -25,13 +25,18 @@
 )
 
 func Unmarshal[T any](value starlark.Value) (T, error) {
-	var zero T
-	x, err := UnmarshalReflect(value, reflect.TypeOf(zero))
+	x, err := UnmarshalReflect(value, reflect.TypeOf((*T)(nil)).Elem())
 	return x.Interface().(T), err
 }
 
 func UnmarshalReflect(value starlark.Value, ty reflect.Type) (reflect.Value, error) {
+	if ty == reflect.TypeOf((*starlark.Value)(nil)).Elem() {
+		return reflect.ValueOf(value), nil
+	}
 	zero := reflect.Zero(ty)
+	if value == nil {
+		panic("nil value")
+	}
 	var result reflect.Value
 	if ty.Kind() == reflect.Interface {
 		var err error
@@ -286,3 +291,14 @@
 		return nil, fmt.Errorf("unimplemented starlark type: %s", value.Type())
 	}
 }
+
+// UnmarshalNoneable is like Unmarshal, but it will accept None as the top level (but not nested)
+// starlark value. If the value is None, a nil pointer will be returned, otherwise a pointer
+// to the result of Unmarshal will be returned.
+func UnmarshalNoneable[T any](value starlark.Value) (*T, error) {
+	if _, ok := value.(starlark.NoneType); ok {
+		return nil, nil
+	}
+	ret, err := Unmarshal[T](value)
+	return &ret, err
+}
diff --git a/starlark_import/unmarshal_test.go b/starlark_import/unmarshal_test.go
index ee7a9e3..bc0ea4c 100644
--- a/starlark_import/unmarshal_test.go
+++ b/starlark_import/unmarshal_test.go
@@ -30,7 +30,7 @@
 	return result["x"]
 }
 
-func TestUnmarshallConcreteType(t *testing.T) {
+func TestUnmarshalConcreteType(t *testing.T) {
 	x, err := Unmarshal[string](createStarlarkValue(t, `"foo"`))
 	if err != nil {
 		t.Error(err)
@@ -41,7 +41,7 @@
 	}
 }
 
-func TestUnmarshallConcreteTypeWithInterfaces(t *testing.T) {
+func TestUnmarshalConcreteTypeWithInterfaces(t *testing.T) {
 	x, err := Unmarshal[map[string]map[string]interface{}](createStarlarkValue(t,
 		`{"foo": {"foo2": "foo3"}, "bar": {"bar2": ["bar3"]}}`))
 	if err != nil {
@@ -57,7 +57,22 @@
 	}
 }
 
-func TestUnmarshall(t *testing.T) {
+func TestUnmarshalToStarlarkValue(t *testing.T) {
+	x, err := Unmarshal[map[string]starlark.Value](createStarlarkValue(t,
+		`{"foo": "Hi", "bar": None}`))
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if x["foo"].(starlark.String).GoString() != "Hi" {
+		t.Errorf("Expected \"Hi\", got: %q", x["foo"].(starlark.String).GoString())
+	}
+	if x["bar"].Type() != "NoneType" {
+		t.Errorf("Expected \"NoneType\", got: %q", x["bar"].Type())
+	}
+}
+
+func TestUnmarshal(t *testing.T) {
 	testCases := []struct {
 		input    string
 		expected interface{}
diff --git a/tests/genrule_sandbox_test.py b/tests/genrule_sandbox_test.py
index a9f0c9b..0cebc2a 100755
--- a/tests/genrule_sandbox_test.py
+++ b/tests/genrule_sandbox_test.py
@@ -17,34 +17,37 @@
 import argparse
 import collections
 import json
-import os.path
+import os
 import subprocess
+import sys
 import tempfile
 
-SRC_ROOT_DIR = os.path.abspath(__file__ + "/../../../..")
+def get_top() -> str:
+  path = '.'
+  while not os.path.isfile(os.path.join(path, 'build/soong/tests/genrule_sandbox_test.py')):
+    if os.path.abspath(path) == '/':
+      sys.exit('Could not find android source tree root.')
+    path = os.path.join(path, '..')
+  return os.path.abspath(path)
 
-
-def _module_graph_path(out_dir):
-  return os.path.join(SRC_ROOT_DIR, out_dir, "soong", "module-actions.json")
-
-
-def _build_with_soong(targets, target_product, out_dir, extra_env={}):
+def _build_with_soong(targets, target_product, *, keep_going = False, extra_env={}):
   env = {
+      **os.environ,
       "TARGET_PRODUCT": target_product,
       "TARGET_BUILD_VARIANT": "userdebug",
   }
-  env.update(os.environ)
   env.update(extra_env)
   args = [
       "build/soong/soong_ui.bash",
       "--make-mode",
       "--skip-soong-tests",
   ]
+  if keep_going:
+    args.append("-k")
   args.extend(targets)
   try:
-    out = subprocess.check_output(
+    subprocess.check_output(
         args,
-        cwd=SRC_ROOT_DIR,
         env=env,
     )
   except subprocess.CalledProcessError as e:
@@ -55,14 +58,13 @@
 
 
 def _find_outputs_for_modules(modules, out_dir, target_product):
-  module_path = os.path.join(
-      SRC_ROOT_DIR, out_dir, "soong", "module-actions.json"
-  )
+  module_path = os.path.join(out_dir, "soong", "module-actions.json")
 
   if not os.path.exists(module_path):
-    _build_with_soong(["json-module-graph"], target_product, out_dir)
+    _build_with_soong(["json-module-graph"], target_product)
 
-  action_graph = json.load(open(_module_graph_path(out_dir)))
+  with open(module_path) as f:
+    action_graph = json.load(f)
 
   module_to_outs = collections.defaultdict(set)
   for mod in action_graph:
@@ -74,50 +76,15 @@
   return module_to_outs
 
 
-def _store_outputs_to_tmp(output_files):
-  try:
-    tempdir = tempfile.TemporaryDirectory()
-    for f in output_files:
-      out = subprocess.check_output(
-          ["cp", "--parents", f, tempdir.name],
-          cwd=SRC_ROOT_DIR,
-      )
-    return tempdir
-  except subprocess.CalledProcessError as e:
-    print(e)
-    print(e.stdout)
-    print(e.stderr)
-
-
-def _diff_outs(file1, file2, show_diff):
-  output = None
-  base_args = ["diff"]
-  if not show_diff:
-    base_args.append("--brief")
-  try:
-    args = base_args + [file1, file2]
-    output = subprocess.check_output(
-        args,
-        cwd=SRC_ROOT_DIR,
-    )
-  except subprocess.CalledProcessError as e:
-    if e.returncode == 1:
-      if show_diff:
-        return output
-      return True
-  return None
-
-
-def _compare_outputs(module_to_outs, tempdir, show_diff):
+def _compare_outputs(module_to_outs, tempdir) -> dict[str, list[str]]:
   different_modules = collections.defaultdict(list)
   for module, outs in module_to_outs.items():
     for out in outs:
-      output = None
-      diff = _diff_outs(os.path.join(tempdir.name, out), out, show_diff)
-      if diff:
-        different_modules[module].append(diff)
+      try:
+        subprocess.check_output(["diff", os.path.join(tempdir, out), out])
+      except subprocess.CalledProcessError as e:
+        different_modules[module].append(e.stdout)
 
-  tempdir.cleanup()
   return different_modules
 
 
@@ -138,53 +105,56 @@
       "--show-diff",
       "-d",
       action="store_true",
-      required=False,
       help="whether to display differing files",
   )
   parser.add_argument(
       "--output-paths-only",
       "-o",
       action="store_true",
-      required=False,
       help="Whether to only return the output paths per module",
   )
   args = parser.parse_args()
+  os.chdir(get_top())
 
   out_dir = os.environ.get("OUT_DIR", "out")
-  target_product = args.target_product
-  modules = set(args.modules)
 
-  module_to_outs = _find_outputs_for_modules(modules, out_dir, target_product)
+  print("finding output files for the modules...")
+  module_to_outs = _find_outputs_for_modules(set(args.modules), out_dir, args.target_product)
   if not module_to_outs:
-    print("No outputs found")
-    exit(1)
+    sys.exit("No outputs found")
 
   if args.output_paths_only:
     for m, o in module_to_outs.items():
       print(f"{m} outputs: {o}")
-    exit(0)
+    sys.exit(0)
 
-  all_outs = set()
-  for outs in module_to_outs.values():
-    all_outs.update(outs)
-  print("build without sandboxing")
-  _build_with_soong(list(all_outs), target_product, out_dir)
-  tempdir = _store_outputs_to_tmp(all_outs)
-  print("build with sandboxing")
-  _build_with_soong(
-      list(all_outs),
-      target_product,
-      out_dir,
-      extra_env={"GENRULE_SANDBOXING": "true"},
-  )
-  diffs = _compare_outputs(module_to_outs, tempdir, args.show_diff)
-  if len(diffs) == 0:
-    print("All modules are correct")
-  elif args.show_diff:
-    for m, d in diffs.items():
-      print(f"Module {m} has diffs {d}")
-  else:
-    print(f"Modules {list(diffs.keys())} have diffs")
+  all_outs = list(set.union(*module_to_outs.values()))
+
+  print("building without sandboxing...")
+  _build_with_soong(all_outs, args.target_product)
+  with tempfile.TemporaryDirectory() as tempdir:
+    for f in all_outs:
+      subprocess.check_call(["cp", "--parents", f, tempdir])
+
+    print("building with sandboxing...")
+    _build_with_soong(
+        all_outs,
+        args.target_product,
+        # We've verified these build without sandboxing already, so do the sandboxing build
+        # with keep_going = True so that we can find all the genrules that fail to build with
+        # sandboxing.
+        keep_going = True,
+        extra_env={"GENRULE_SANDBOXING": "true"},
+    )
+
+    diffs = _compare_outputs(module_to_outs, tempdir)
+    if len(diffs) == 0:
+      print("All modules are correct")
+    elif args.show_diff:
+      for m, d in diffs.items():
+        print(f"Module {m} has diffs {d}")
+    else:
+      print(f"Modules {list(diffs.keys())} have diffs")
 
 
 if __name__ == "__main__":
diff --git a/tests/lib.sh b/tests/lib.sh
index 4aaf272..40b317b 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -147,6 +147,7 @@
   symlink_directory external/bazelbuild-rules_go
   symlink_directory external/bazelbuild-rules_license
   symlink_directory external/bazelbuild-kotlin-rules
+  symlink_directory external/bazelbuild-rules_java
 
   symlink_file WORKSPACE
   symlink_file BUILD
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 43a9f0f..6b9ff8b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -10,7 +10,7 @@
 "$TOP/build/soong/tests/persistent_bazel_test.sh"
 "$TOP/build/soong/tests/soong_test.sh"
 "$TOP/build/soong/tests/stale_metrics_files_test.sh"
-"$TOP/build/bazel/ci/rbc_regression_test.sh" aosp_arm64-userdebug
+"$TOP/prebuilts/build-tools/linux-x86/bin/py3-cmd" "$TOP/build/bazel/ci/rbc_dashboard.py" aosp_arm64-userdebug
 
 # The following tests build against the full source tree and don't rely on the
 # mock client.
@@ -22,7 +22,5 @@
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
 
-"$TOP/build/soong/tests/sbom_test.sh"
-
 "$TOP/build/bazel/ci/b_test.sh"
 
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 19987f2..2534b20 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -85,7 +85,13 @@
   lz4=$out_dir/host/linux-x86/bin/lz4
 
   declare -A diff_excludes
-  diff_excludes[vendor]="-I /vendor/lib64/libkeystore2_crypto.so"
+  diff_excludes[product]="\
+    -I /product/etc/aconfig_flags.textproto \
+    -I /product/etc/build_flags.json"
+  diff_excludes[vendor]="\
+    -I /vendor/lib64/libkeystore2_crypto.so \
+    -I /vendor/etc/aconfig_flags.textproto \
+    -I /vendor/etc/build_flags.json"
   diff_excludes[system]="\
     -I /bin \
     -I /bugreports \
@@ -105,12 +111,22 @@
     -I /odm/priv-app \
     -I /odm/usr \
     -I /sdcard \
+    -I /system/etc/aconfig_flags.textproto \
+    -I /system/etc/build_flags.json \
     -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 \
@@ -126,6 +142,9 @@
     -I /system/lib/vndk-sp-29 \
     -I /system/usr/icu \
     -I /vendor_dlkm/etc"
+  diff_excludes[system_ext]="\
+    -I /system_ext/etc/aconfig_flags.textproto \
+    -I /system_ext/etc/build_flags.json"
 
   # 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.
@@ -219,8 +238,104 @@
     diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
   done
 
+  verify_package_verification_code "$product_out/sbom.spdx"
+
   # Teardown
   cleanup "${out_dir}"
 }
 
-test_sbom_aosp_cf_x86_64_phone
\ No newline at end of file
+function verify_package_verification_code {
+  local sbom_file="$1"; shift
+
+  local -a file_checksums
+  local package_product_found=
+  while read -r line;
+  do
+    if grep -q 'PackageVerificationCode' <<<"$line"
+    then
+      package_product_found=true
+    fi
+    if [ -n "$package_product_found" ]
+    then
+      if grep -q 'FileChecksum' <<< "$line"
+      then
+        checksum=$(echo $line | sed 's/^.*: //')
+        file_checksums+=("$checksum")
+      fi
+    fi
+  done <<< "$(grep -E 'PackageVerificationCode|FileChecksum' $sbom_file)"
+  IFS=$'\n' file_checksums=($(sort <<<"${file_checksums[*]}")); unset IFS
+  IFS= expected_package_verification_code=$(printf "${file_checksums[*]}" | sha1sum | sed 's/[[:space:]]*-//'); unset IFS
+
+  actual_package_verification_code=$(grep PackageVerificationCode $sbom_file | sed 's/PackageVerificationCode: //g')
+  if [ $actual_package_verification_code = $expected_package_verification_code ]
+  then
+    echo "Package verification code is correct."
+  else
+    echo "Unexpected package verification code."
+    exit 1
+  fi
+}
+
+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.go b/third_party/zip/android.go
index f8e45c5..0f41f62 100644
--- a/third_party/zip/android.go
+++ b/third_party/zip/android.go
@@ -170,7 +170,7 @@
 func (w *Writer) CreateHeaderAndroid(fh *FileHeader) (io.Writer, error) {
 	writeDataDescriptor := fh.Method != Store
 	if writeDataDescriptor {
-		fh.Flags &= DataDescriptorFlag
+		fh.Flags |= DataDescriptorFlag
 	} else {
 		fh.Flags &= ^uint16(DataDescriptorFlag)
 	}
diff --git a/ui/build/config.go b/ui/build/config.go
index fb5f7dd..e5c9a50 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -374,6 +374,11 @@
 		if err := loadEnvConfig(ctx, ret, bc); err != nil {
 			ctx.Fatalln("Failed to parse env config files: %v", err)
 		}
+		if !ret.canSupportRBE() {
+			// Explicitly set USE_RBE env variable to false when we cannot run
+			// an RBE build to avoid ninja local execution pool issues.
+			ret.environ.Set("USE_RBE", "false")
+		}
 	}
 
 	if distDir, ok := ret.environ.Get("DIST_DIR"); ok {
@@ -1374,18 +1379,26 @@
 	return true
 }
 
+func (c *configImpl) canSupportRBE() bool {
+	// 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.
+	authType, _ := c.rbeAuth()
+	if !c.StubbyExists() && strings.Contains(authType, "use_google_prod_creds") {
+		return false
+	}
+	return true
+}
+
 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") {
+	if !c.canSupportRBE() {
 		return false
 	}
+
 	if v, ok := c.Environment().Get("USE_RBE"); ok {
 		v = strings.TrimSpace(v)
 		if v != "" && v != "false" {
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/zip/cmd/BUILD.bazel b/zip/cmd/BUILD.bazel
deleted file mode 100644
index e04a1e1..0000000
--- a/zip/cmd/BUILD.bazel
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# TODO(b/194644518): Switch to the source version when Bazel can build go
-# binaries.
-alias(
-    name = "soong_zip",
-    actual = "//prebuilts/build-tools:linux-x86/bin/soong_zip",
-)
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index a2ccc20..5231fae 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -173,6 +173,7 @@
 	cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
 	traceFile := flags.String("trace", "", "write trace to file")
 	sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
+	doNotWrite := flags.Bool("n", false, "Nothing is written to disk -- all other work happens")
 
 	flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
 	flags.Var(&listFiles{}, "l", "file containing list of files to zip")
@@ -236,6 +237,7 @@
 		StoreSymlinks:            *symlinks,
 		IgnoreMissingFiles:       *ignoreMissingFiles,
 		Sha256Checksum:           *sha256Checksum,
+		DoNotWrite:               *doNotWrite,
 	})
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index 5e1a104..30a2ee7 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -282,6 +282,7 @@
 	StoreSymlinks            bool
 	IgnoreMissingFiles       bool
 	Sha256Checksum           bool
+	DoNotWrite               bool
 
 	Stderr     io.Writer
 	Filesystem pathtools.FileSystem
@@ -400,7 +401,9 @@
 
 	var zipErr error
 
-	if !args.WriteIfChanged {
+	if args.DoNotWrite {
+		out = io.Discard
+	} else if !args.WriteIfChanged {
 		f, err := os.Create(args.OutputFilePath)
 		if err != nil {
 			return err
@@ -421,7 +424,7 @@
 		return zipErr
 	}
 
-	if args.WriteIfChanged {
+	if args.WriteIfChanged && !args.DoNotWrite {
 		err := pathtools.WriteFileIfChanged(args.OutputFilePath, buf.Bytes(), 0666)
 		if err != nil {
 			return err