Merge "Revert "Sort canned fs_config lines to facilitate comparison with Bazel-built APEXes""
diff --git a/OWNERS b/OWNERS
index e18eea1..964e27a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,7 +4,6 @@
 # AMER
 agespino@google.com
 alexmarquez@google.com
-asmundak@google.com
 ccross@android.com
 colefaust@google.com
 cparsons@google.com
@@ -27,6 +26,4 @@
 jingwen@google.com
 
 # EMEA
-hansson@google.com #{LAST_RESORT_SUGGESTION}
 lberki@google.com
-paulduffin@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/README.md b/README.md
index 7f18463..70311cb 100644
--- a/README.md
+++ b/README.md
@@ -105,31 +105,35 @@
 
 ### Types
 
-Variables and properties are strongly typed, variables dynamically based on the
-first assignment, and properties statically by the module type.  The supported
-types are:
+Variables and properties are strongly typed. Variables are dynamically typed
+based on the first assignment, and properties are statically typed by the
+module type.  The supported types are:
 * Bool (`true` or `false`)
 * Integers (`int`)
 * Strings (`"string"`)
 * Lists of strings (`["string1", "string2"]`)
 * Maps (`{key1: "value1", key2: ["value2"]}`)
 
-Maps may values of any type, including nested maps.  Lists and maps may have
-trailing commas after the last value.
+Maps may contain values of any type, including nested maps. Lists and maps may
+have trailing commas after the last value.
 
 Strings can contain double quotes using `\"`, for example `"cat \"a b\""`.
 
 ### Operators
 
-Strings, lists of strings, and maps can be appended using the `+` operator.
-Integers can be summed up using the `+` operator. Appending a map produces the
-union of keys in both maps, appending the values of any keys that are present
-in both maps.
+The `+` operator:
+* Sums integers.
+* Concatenates strings and lists.
+* Produces the union of maps.
+
+Concatenating maps produces a map whose keys are the union of the given maps'
+keys, and whose mapped values are the union of the given maps' corresponding
+mapped values.
 
 ### Defaults modules
 
-A defaults module can be used to repeat the same properties in multiple modules.
-For example:
+A `defaults` module can be used to repeat the same properties in multiple
+modules. For example:
 
 ```
 cc_defaults {
@@ -184,7 +188,7 @@
 
 ```
 package {
-    default_visibility: [":__subpackages"]
+    default_visibility: [":__subpackages__"]
 }
 ```
 
diff --git a/android/Android.bp b/android/Android.bp
index 29a88f2..641c438 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -111,6 +111,7 @@
         "depset_test.go",
         "deptag_test.go",
         "expand_test.go",
+        "filegroup_test.go",
         "fixture_test.go",
         "gen_notice_test.go",
         "license_kind_test.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index d466413..33655d0 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -125,6 +125,7 @@
 		"external/eigen":                         Bp2BuildDefaultTrueRecursively,
 		"external/erofs-utils":                   Bp2BuildDefaultTrueRecursively,
 		"external/error_prone":                   Bp2BuildDefaultTrueRecursively,
+		"external/escapevelocity":                Bp2BuildDefaultTrueRecursively,
 		"external/expat":                         Bp2BuildDefaultTrueRecursively,
 		"external/f2fs-tools":                    Bp2BuildDefaultTrue,
 		"external/flac":                          Bp2BuildDefaultTrueRecursively,
@@ -143,7 +144,10 @@
 		"external/javassist":                     Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                  Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                       Bp2BuildDefaultTrueRecursively,
+		"external/jsr305":                        Bp2BuildDefaultTrueRecursively,
+		"external/jsr330":                        Bp2BuildDefaultTrueRecursively,
 		"external/junit":                         Bp2BuildDefaultTrueRecursively,
+		"external/kotlinc":                       Bp2BuildDefaultTrueRecursively,
 		"external/libaom":                        Bp2BuildDefaultTrueRecursively,
 		"external/libavc":                        Bp2BuildDefaultTrueRecursively,
 		"external/libcap":                        Bp2BuildDefaultTrueRecursively,
@@ -195,7 +199,9 @@
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
 		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/tools/aapt2":                        Bp2BuildDefaultTrue,
+		"frameworks/base/tools/codegen":                      Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
+		"frameworks/hardware/interfaces/stats/aidl":          Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
@@ -211,72 +217,81 @@
 		"frameworks/native/services/batteryservice":          Bp2BuildDefaultTrue,
 		"frameworks/proto_logging/stats":                     Bp2BuildDefaultTrueRecursively,
 
-		"hardware/interfaces":                          Bp2BuildDefaultTrue,
-		"hardware/interfaces/audio/aidl":               Bp2BuildDefaultTrue,
-		"hardware/interfaces/common/aidl":              Bp2BuildDefaultTrue,
-		"hardware/interfaces/common/fmq/aidl":          Bp2BuildDefaultTrue,
-		"hardware/interfaces/configstore/1.0":          Bp2BuildDefaultTrue,
-		"hardware/interfaces/configstore/1.1":          Bp2BuildDefaultTrue,
-		"hardware/interfaces/configstore/utils":        Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/2.0":   Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/3.0":   Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/4.0":   Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/aidl":  Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/bufferqueue/1.0": Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/bufferqueue/2.0": Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/1.0":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/1.1":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/1.2":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/aidl":     Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/2.0":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/2.1":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/3.0":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/4.0":      Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/1.0":               Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/1.0/default":       Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/2.0":               Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/2.0/default":       Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/2.0/utils":         Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/health/2.1":               Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/aidl":              Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/utils":             Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/media/1.0":                Bp2BuildDefaultTrue,
-		"hardware/interfaces/media/bufferpool/2.0":     Bp2BuildDefaultTrue,
-		"hardware/interfaces/media/c2/1.0":             Bp2BuildDefaultTrue,
-		"hardware/interfaces/media/c2/1.1":             Bp2BuildDefaultTrue,
-		"hardware/interfaces/media/c2/1.2":             Bp2BuildDefaultTrue,
-		"hardware/interfaces/media/omx/1.0":            Bp2BuildDefaultTrue,
-		"hardware/interfaces/neuralnetworks/1.0":       Bp2BuildDefaultTrue,
-		"hardware/interfaces/neuralnetworks/1.1":       Bp2BuildDefaultTrue,
-		"hardware/interfaces/neuralnetworks/1.2":       Bp2BuildDefaultTrue,
-		"hardware/interfaces/neuralnetworks/1.3":       Bp2BuildDefaultTrue,
-		"hardware/interfaces/neuralnetworks/aidl":      Bp2BuildDefaultTrue,
+		"hardware/interfaces":                                     Bp2BuildDefaultTrue,
+		"hardware/interfaces/audio/aidl":                          Bp2BuildDefaultTrue,
+		"hardware/interfaces/audio/aidl/common":                   Bp2BuildDefaultTrue,
+		"hardware/interfaces/common/aidl":                         Bp2BuildDefaultTrue,
+		"hardware/interfaces/common/fmq/aidl":                     Bp2BuildDefaultTrue,
+		"hardware/interfaces/common/support":                      Bp2BuildDefaultTrue,
+		"hardware/interfaces/configstore/1.0":                     Bp2BuildDefaultTrue,
+		"hardware/interfaces/configstore/1.1":                     Bp2BuildDefaultTrue,
+		"hardware/interfaces/configstore/utils":                   Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/2.0":              Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/3.0":              Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/4.0":              Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/aidl":             Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/bufferqueue/1.0":            Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/bufferqueue/2.0":            Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/1.0":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/1.1":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/1.2":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/aidl":                Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/2.0":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/2.1":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/3.0":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/4.0":                 Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/1.0":                          Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/1.0/default":                  Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/2.0":                          Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/2.0/default":                  Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/2.0/utils":                    Bp2BuildDefaultTrueRecursively,
+		"hardware/interfaces/health/2.1":                          Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/aidl":                         Bp2BuildDefaultTrue,
+		"hardware/interfaces/health/utils":                        Bp2BuildDefaultTrueRecursively,
+		"hardware/interfaces/media/1.0":                           Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/bufferpool":                    Bp2BuildDefaultTrueRecursively,
+		"hardware/interfaces/media/bufferpool/aidl/default/tests": Bp2BuildDefaultFalseRecursively,
+		"hardware/interfaces/media/c2/1.0":                        Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/c2/1.1":                        Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/c2/1.2":                        Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/omx/1.0":                       Bp2BuildDefaultTrue,
+		"hardware/interfaces/neuralnetworks":                      Bp2BuildDefaultTrueRecursively,
+		"hardware/interfaces/neuralnetworks/aidl/vts":             Bp2BuildDefaultFalseRecursively,
+		"hardware/interfaces/neuralnetworks/1.0/vts":              Bp2BuildDefaultFalseRecursively,
+		"hardware/interfaces/neuralnetworks/1.1/vts":              Bp2BuildDefaultFalseRecursively,
+		"hardware/interfaces/neuralnetworks/1.2/vts":              Bp2BuildDefaultFalseRecursively,
+		"hardware/interfaces/neuralnetworks/1.3/vts":              Bp2BuildDefaultFalseRecursively,
+		"hardware/interfaces/neuralnetworks/1.4/vts":              Bp2BuildDefaultFalseRecursively,
 
 		"libnativehelper": Bp2BuildDefaultTrueRecursively,
 
-		"packages/apps/DevCamera":                          Bp2BuildDefaultTrue,
-		"packages/apps/HTMLViewer":                         Bp2BuildDefaultTrue,
-		"packages/apps/Protips":                            Bp2BuildDefaultTrue,
-		"packages/apps/SafetyRegulatoryInfo":               Bp2BuildDefaultTrue,
-		"packages/apps/WallpaperPicker":                    Bp2BuildDefaultTrue,
-		"packages/modules/NeuralNetworks/driver/cache":     Bp2BuildDefaultTrueRecursively,
-		"packages/modules/StatsD/lib/libstatssocket":       Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb":                             Bp2BuildDefaultTrue,
-		"packages/modules/adb/apex":                        Bp2BuildDefaultTrue,
-		"packages/modules/adb/crypto":                      Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/libs":                        Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_auth":                Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_connection":          Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/proto":                       Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/tls":                         Bp2BuildDefaultTrueRecursively,
-		"packages/providers/MediaProvider/tools/dialogs":   Bp2BuildDefaultFalse, // TODO(b/242834374)
-		"packages/screensavers/Basic":                      Bp2BuildDefaultTrue,
-		"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
+		"packages/apps/DevCamera":                            Bp2BuildDefaultTrue,
+		"packages/apps/HTMLViewer":                           Bp2BuildDefaultTrue,
+		"packages/apps/Protips":                              Bp2BuildDefaultTrue,
+		"packages/apps/SafetyRegulatoryInfo":                 Bp2BuildDefaultTrue,
+		"packages/apps/WallpaperPicker":                      Bp2BuildDefaultTrue,
+		"packages/modules/NeuralNetworks/driver/cache":       Bp2BuildDefaultTrueRecursively,
+		"packages/modules/StatsD/lib/libstatssocket":         Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb":                               Bp2BuildDefaultTrue,
+		"packages/modules/adb/apex":                          Bp2BuildDefaultTrue,
+		"packages/modules/adb/crypto":                        Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/libs":                          Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/pairing_auth":                  Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/pairing_connection":            Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/proto":                         Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/tls":                           Bp2BuildDefaultTrueRecursively,
+		"packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue,
+		"packages/modules/NeuralNetworks/apex":               Bp2BuildDefaultTrue,
+		"packages/providers/MediaProvider/tools/dialogs":     Bp2BuildDefaultFalse, // TODO(b/242834374)
+		"packages/screensavers/Basic":                        Bp2BuildDefaultTrue,
+		"packages/services/Car/tests/SampleRearViewCamera":   Bp2BuildDefaultFalse, // TODO(b/242834321)
 
 		"platform_testing/tests/example": Bp2BuildDefaultTrueRecursively,
 
 		"prebuilts/clang/host/linux-x86":           Bp2BuildDefaultTrueRecursively,
+		"prebuilts/gradle-plugin":                  Bp2BuildDefaultTrueRecursively,
 		"prebuilts/runtime/mainline/platform/sdk":  Bp2BuildDefaultTrueRecursively,
+		"prebuilts/sdk/current/androidx":           Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/extras/app-toolkit": Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/support":            Bp2BuildDefaultTrue,
 		"prebuilts/tools":                          Bp2BuildDefaultTrue,
@@ -345,7 +360,8 @@
 		"system/tools/sysprop":                                   Bp2BuildDefaultTrue,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
-		"tools/apksig": Bp2BuildDefaultTrue,
+		"tools/apksig":   Bp2BuildDefaultTrue,
+		"tools/metalava": Bp2BuildDefaultTrue,
 		"tools/platform-compat/java/android/compat":  Bp2BuildDefaultTrueRecursively,
 		"tools/tradefederation/prebuilts/filegroups": Bp2BuildDefaultTrueRecursively,
 	}
@@ -369,18 +385,16 @@
 		"external/bazelbuild-kotlin-rules":/* recursive = */ true,
 		"external/bazel-skylib":/* recursive = */ true,
 		"external/guava":/* recursive = */ true,
-		"external/jsr305":/* recursive = */ true,
 		"external/protobuf":/* recursive = */ false,
 		"external/python/absl-py":/* recursive = */ true,
 
 		// this BUILD file is globbed by //external/icu/icu4c/source:icu4c_test_data's "data/**/*".
 		"external/icu/icu4c/source/data/unidata/norm2":/* recursive = */ false,
 
-		"frameworks/base/tools/codegen":/* recursive = */ true,
 		"frameworks/ex/common":/* recursive = */ true,
 
+		// Building manually due to b/179889880: resource files cross package boundary
 		"packages/apps/Music":/* recursive = */ true,
-		"packages/apps/QuickSearchBox":/* recursive = */ true,
 
 		"prebuilts/abi-dumps/platform":/* recursive = */ true,
 		"prebuilts/abi-dumps/ndk":/* recursive = */ true,
@@ -390,7 +404,7 @@
 		"prebuilts/clang-tools":/* recursive = */ true,
 		"prebuilts/gcc":/* recursive = */ true,
 		"prebuilts/build-tools":/* recursive = */ true,
-		"prebuilts/jdk/jdk11":/* recursive = */ false,
+		"prebuilts/jdk/jdk17":/* recursive = */ true,
 		"prebuilts/misc":/* recursive = */ false, // not recursive because we need bp2build converted build files in prebuilts/misc/common/asm
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
@@ -400,6 +414,9 @@
 		// not recursive due to conflicting workspace paths in tools/atest/bazel/rules
 		"tools/asuite/atest":/* recursive = */ false,
 		"tools/asuite/atest/bazel/reporter":/* recursive = */ true,
+
+		// TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack
+		"external/rust/crates/rustc-demangle-capi":/* recursive = */ false,
 	}
 
 	Bp2buildModuleAlwaysConvertList = []string{
@@ -417,16 +434,11 @@
 		"com.android.media.swcodec-mediaswcodec.rc",
 		"com.android.media.swcodec.certificate",
 		"com.android.media.swcodec.key",
-		"com.android.neuralnetworks",
-		"com.android.neuralnetworks-androidManifest",
-		"com.android.neuralnetworks.certificate",
-		"com.android.neuralnetworks.key",
 		"flatbuffer_headers",
 		"framework-connectivity-protos",
 		"gemmlowp_headers",
 		"gl_headers",
 		"ipconnectivity-proto-src",
-		"libaidlcommonsupport",
 		"libandroid_runtime_lazy",
 		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
@@ -488,13 +500,6 @@
 		"mediaswcodec.policy",
 		"mediaswcodec.xml",
 		"neuralnetworks_types",
-		"neuralnetworks_utils_hal_aidl",
-		"neuralnetworks_utils_hal_common",
-		"neuralnetworks_utils_hal_service",
-		"neuralnetworks_utils_hal_1_0",
-		"neuralnetworks_utils_hal_1_1",
-		"neuralnetworks_utils_hal_1_2",
-		"neuralnetworks_utils_hal_1_3",
 		"libneuralnetworks_common",
 		// packagemanager_aidl_interface is created implicitly in packagemanager_aidl module
 		"packagemanager_aidl_interface",
@@ -669,16 +674,50 @@
 		// kotlin srcs in java libs
 		"CtsPkgInstallerConstants",
 		"kotlinx_atomicfu",
+
+		// kotlin srcs in java binary
+		"AnalyzerKt",
+		"trebuchet-core",
+
+		// kotlin srcs in android_library
+		"renderscript_toolkit",
+
+		//kotlin srcs in android_binary
+		"MusicKotlin",
+
+		// checked in current.txt for merged_txts
+		"non-updatable-current.txt",
+		"non-updatable-system-current.txt",
+		"non-updatable-module-lib-current.txt",
+		"non-updatable-system-server-current.txt",
+
+		// for api_fingerprint.txt generation
+		"api_fingerprint",
+
+		// allowlisting for kotlinx_coroutines
+		"kotlinx_coroutines",
+		"annotations",
+		"kotlinx-coroutines-android-annotation-stubs",
+
+		// for building com.android.neuralnetworks
+		"libimapper_stablec",
+		"libimapper_providerutils",
+
+		// min_sdk_version in android_app
+		"CtsShimUpgrade",
+		"fake-framework",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
 		"aidl_interface_headers",
+		"bpf",
+		"combined_apis",
 		"license",
 		"linker_config",
 		"java_import",
 		"java_import_host",
+		"java_sdk_library",
 		"sysprop_library",
-		"bpf",
 	}
 
 	// Add the names of modules that bp2build should never convert, if it is
@@ -689,15 +728,6 @@
 	// the "prebuilt_" prefix to the name, so that it's differentiable from
 	// the source versions within Soong's module graph.
 	Bp2buildModuleDoNotConvertList = []string{
-		// TODO(b/250876486): Created cc_aidl_library doesn't have static libs from parent cc module
-		"libgui_window_info_static",
-		"libgui",     // Depends on unconverted libgui_window_info_static
-		"libdisplay", // Depends on uncovnerted libgui
-		// Depends on unconverted libdisplay
-		"libdvr_static.google",
-		"libdvr.google",
-		"libvrsensor",
-		"dvr_api-test",
 		// Depends on unconverted libandroid, libgui
 		"dvr_buffer_queue-test",
 		"dvr_display-test",
@@ -826,6 +856,9 @@
 		"android.hardware.health-translate-java",
 
 		// cc_test related.
+		// b/274164834 "Could not open Configuration file test.cfg"
+		"svcenc", "svcdec",
+
 		// Failing host cc_tests
 		"memunreachable_unit_test",
 		"libprocinfo_test",
@@ -1316,6 +1349,9 @@
 
 		// uses glob in $(locations)
 		"libc_musl_sysroot",
+
+		// TODO(b/266459895): depends on libunwindstack
+		"libutils_test",
 	}
 
 	MixedBuildsDisabledList = []string{
@@ -1360,13 +1396,50 @@
 		"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{
+		// M5: tzdata launch
 		"com.android.tzdata",
 		"test1_com.android.tzdata",
+		// M7: adbd launch
+		"com.android.adbd",
+		"test_com.android.adbd",
+		"adbd_test",
+		"adb_crypto_test",
+		"adb_pairing_auth_test",
+		"adb_pairing_connection_test",
+		"adb_tls_connection_test",
+		// M9: mixed builds for mainline trains launch
+		"api_fingerprint",
 	}
 
 	// Staging-mode allowlist. Modules in this list are only built
@@ -1375,7 +1448,22 @@
 	// It is implicit that all modules in ProdMixedBuildsEnabledList will
 	// also be built - do not add them to this list.
 	StagingMixedBuildsEnabledList = []string{
-		"com.android.adbd",
-		"adbd_test",
+		"com.android.neuralnetworks",
 	}
+
+	// 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{}
+
+	// TODO(b/269342245): Enable the rest of the DCLA libs
+	// "libssl",
+	// "libstagefright_flacdec",
+	// "libutils",
 )
diff --git a/android/androidmk.go b/android/androidmk.go
index 6346401..aa411d1 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -501,6 +501,7 @@
 // generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
 type fillInEntriesContext interface {
 	ModuleDir(module blueprint.Module) string
+	ModuleSubDir(module blueprint.Module) string
 	Config() Config
 	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
 	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
@@ -528,7 +529,7 @@
 		fmt.Fprintf(&a.header, distString)
 	}
 
-	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)  # "+ctx.ModuleType(mod))
+	fmt.Fprintf(&a.header, "\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod))
 
 	// Collect make variable assignment entries.
 	a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
@@ -547,6 +548,7 @@
 	a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
 	a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
 	a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
+	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
 
 	// If the install rule was generated by Soong tell Make about it.
 	if len(base.katiInstalls) > 0 {
diff --git a/android/apex.go b/android/apex.go
index 3c945ae..87bff74 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -817,7 +817,7 @@
 	var flatContent strings.Builder
 
 	fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\n", ctx.ModuleName(), minSdkVersion)
-	for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
+	for _, key := range FirstUniqueStrings(SortedKeys(depInfos)) {
 		info := depInfos[key]
 		toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion)
 		if info.IsExternal {
@@ -845,7 +845,7 @@
 // ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
 type ModuleWithMinSdkVersionCheck interface {
 	Module
-	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+	MinSdkVersion(ctx EarlyModuleContext) ApiLevel
 	CheckMinSdkVersion(ctx ModuleContext)
 }
 
diff --git a/android/api_domain.go b/android/api_domain.go
index bdd4e6f..587ceae 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -32,17 +32,18 @@
 
 // TODO(b/246656800): Reconcile with android.SdkKind
 const (
-	PublicApi ApiSurface = iota
-	SystemApi
-	VendorApi
+	// API surface provided by platform and mainline modules to other mainline modules
+	ModuleLibApi ApiSurface = iota
+	PublicApi               // Aka NDK
+	VendorApi               // Aka LLNDK
 )
 
 func (a ApiSurface) String() string {
 	switch a {
+	case ModuleLibApi:
+		return "module-libapi"
 	case PublicApi:
 		return "publicapi"
-	case SystemApi:
-		return "systemapi"
 	case VendorApi:
 		return "vendorapi"
 	default:
diff --git a/android/api_levels.go b/android/api_levels.go
index bf7b317..0c0b2b4 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -55,6 +55,9 @@
 }
 
 func (this ApiLevel) FinalInt() int {
+	if this.IsInvalid() {
+		panic(fmt.Errorf("%v is not a recognized api_level\n", this))
+	}
 	if this.IsPreview() {
 		panic("Requested a final int from a non-final ApiLevel")
 	} else {
@@ -63,6 +66,9 @@
 }
 
 func (this ApiLevel) FinalOrFutureInt() int {
+	if this.IsInvalid() {
+		panic(fmt.Errorf("%v is not a recognized api_level\n", this))
+	}
 	if this.IsPreview() {
 		return FutureApiLevelInt
 	} else {
@@ -76,6 +82,9 @@
 // - preview codenames -> preview base (9000) + index
 // - otherwise -> cast to int
 func (this ApiLevel) FinalOrPreviewInt() int {
+	if this.IsInvalid() {
+		panic(fmt.Errorf("%v is not a recognized api_level\n", this))
+	}
 	if this.IsCurrent() {
 		return this.number
 	}
@@ -97,6 +106,11 @@
 	return this.isPreview
 }
 
+// Returns true if the raw api level string is invalid
+func (this ApiLevel) IsInvalid() bool {
+	return this.EqualTo(InvalidApiLevel)
+}
+
 // Returns true if this is the unfinalized "current" API level. This means
 // different things across Java and native. Java APIs do not use explicit
 // codenames, so all non-final codenames are grouped into "current". For native
@@ -113,6 +127,72 @@
 	return this.number == -1
 }
 
+// Returns true if an app is compiling against private apis.
+// e.g. if sdk_version = "" in Android.bp, then the ApiLevel of that "sdk" is at PrivateApiLevel.
+func (this ApiLevel) IsPrivate() bool {
+	return this.number == PrivateApiLevel.number
+}
+
+// EffectiveVersion converts an ApiLevel into the concrete ApiLevel that the module should use. For
+// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns
+// FutureApiLevel(10000).
+func (l ApiLevel) EffectiveVersion(ctx EarlyModuleContext) (ApiLevel, error) {
+	if l.EqualTo(InvalidApiLevel) {
+		return l, fmt.Errorf("invalid version in sdk_version %q", l.value)
+	}
+	if !l.IsPreview() {
+		return l, nil
+	}
+	ret := ctx.Config().DefaultAppTargetSdk(ctx)
+	if ret.IsPreview() {
+		return FutureApiLevel, nil
+	}
+	return ret, nil
+}
+
+// EffectiveVersionString converts an SdkSpec into the concrete version string that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns the codename (P, Q, R, etc.)
+func (l ApiLevel) EffectiveVersionString(ctx EarlyModuleContext) (string, error) {
+	if l.EqualTo(InvalidApiLevel) {
+		return l.value, fmt.Errorf("invalid version in sdk_version %q", l.value)
+	}
+	if !l.IsPreview() {
+		return l.String(), nil
+	}
+	// Determine the default sdk
+	ret := ctx.Config().DefaultAppTargetSdk(ctx)
+	if !ret.IsPreview() {
+		// If the default sdk has been finalized, return that
+		return ret.String(), nil
+	}
+	// There can be more than one active in-development sdks
+	// If an app is targeting an active sdk, but not the default one, return the requested active sdk.
+	// e.g.
+	// SETUP
+	// In-development: UpsideDownCake, VanillaIceCream
+	// Default: VanillaIceCream
+	// Android.bp
+	// min_sdk_version: `UpsideDownCake`
+	// RETURN
+	// UpsideDownCake and not VanillaIceCream
+	for _, preview := range ctx.Config().PreviewApiLevels() {
+		if l.String() == preview.String() {
+			return preview.String(), nil
+		}
+	}
+	// Otherwise return the default one
+	return ret.String(), nil
+}
+
+// Specified returns true if the module is targeting a recognzized api_level.
+// It returns false if either
+// 1. min_sdk_version is not an int or a recognized codename
+// 2. both min_sdk_version and sdk_version are empty. In this case, MinSdkVersion() defaults to SdkSpecPrivate.ApiLevel
+func (this ApiLevel) Specified() bool {
+	return !this.IsInvalid() && !this.IsPrivate()
+}
+
 // Returns -1 if the current API level is less than the argument, 0 if they
 // are equal, and 1 if it is greater than the argument.
 func (this ApiLevel) CompareTo(other ApiLevel) int {
@@ -166,6 +246,19 @@
 	isPreview: true,
 }
 
+// Sentinel ApiLevel to validate that an apiLevel is either an int or a recognized codename.
+var InvalidApiLevel = NewInvalidApiLevel("invalid")
+
+// Returns an apiLevel object at the same level as InvalidApiLevel.
+// The object contains the raw string provied in bp file, and can be used for error handling.
+func NewInvalidApiLevel(raw string) ApiLevel {
+	return ApiLevel{
+		value:     raw,
+		number:    -2, // One less than NoneApiLevel
+		isPreview: true,
+	}
+}
+
 // The first version that introduced 64-bit ABIs.
 var FirstLp64Version = uncheckedFinalApiLevel(21)
 
@@ -204,6 +297,16 @@
 	return strconv.Itoa(num)
 }
 
+// ApiLevelFrom converts the given string `raw` to an ApiLevel.
+// If `raw` is invalid (empty string, unrecognized codename etc.) it returns an invalid ApiLevel
+func ApiLevelFrom(ctx PathContext, raw string) ApiLevel {
+	ret, err := ApiLevelFromUser(ctx, raw)
+	if err != nil {
+		return NewInvalidApiLevel(raw)
+	}
+	return ret
+}
+
 // ApiLevelFromUser converts the given string `raw` to an ApiLevel, possibly returning an error.
 //
 // `raw` must be non-empty. Passing an empty string results in a panic.
@@ -225,6 +328,8 @@
 // ApiLevelFromUserWithConfig implements ApiLevelFromUser, see comments for
 // ApiLevelFromUser for more details.
 func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) {
+	// This logic is replicated in starlark, if changing logic here update starlark code too
+	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=42;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	if raw == "" {
 		panic("API level string must be non-empty")
 	}
@@ -302,31 +407,37 @@
 	return PathForOutput(ctx, "api_levels.json")
 }
 
+func getApiLevelsMapReleasedVersions() map[string]int {
+	return map[string]int{
+		"G":        9,
+		"I":        14,
+		"J":        16,
+		"J-MR1":    17,
+		"J-MR2":    18,
+		"K":        19,
+		"L":        21,
+		"L-MR1":    22,
+		"M":        23,
+		"N":        24,
+		"N-MR1":    25,
+		"O":        26,
+		"O-MR1":    27,
+		"P":        28,
+		"Q":        29,
+		"R":        30,
+		"S":        31,
+		"S-V2":     32,
+		"Tiramisu": 33,
+	}
+}
+
 var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap")
 
 func getFinalCodenamesMap(config Config) map[string]int {
+	// This logic is replicated in starlark, if changing logic here update starlark code too
+	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=30;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	return config.Once(finalCodenamesMapKey, func() interface{} {
-		apiLevelsMap := map[string]int{
-			"G":        9,
-			"I":        14,
-			"J":        16,
-			"J-MR1":    17,
-			"J-MR2":    18,
-			"K":        19,
-			"L":        21,
-			"L-MR1":    22,
-			"M":        23,
-			"N":        24,
-			"N-MR1":    25,
-			"O":        26,
-			"O-MR1":    27,
-			"P":        28,
-			"Q":        29,
-			"R":        30,
-			"S":        31,
-			"S-V2":     32,
-			"Tiramisu": 33,
-		}
+		apiLevelsMap := getApiLevelsMapReleasedVersions()
 
 		// TODO: Differentiate "current" and "future".
 		// The code base calls it FutureApiLevel, but the spelling is "current",
@@ -349,29 +460,12 @@
 
 var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
 
+// ApiLevelsMap has entries for preview API levels
 func GetApiLevelsMap(config Config) map[string]int {
+	// This logic is replicated in starlark, if changing logic here update starlark code too
+	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=23;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	return config.Once(apiLevelsMapKey, func() interface{} {
-		apiLevelsMap := map[string]int{
-			"G":        9,
-			"I":        14,
-			"J":        16,
-			"J-MR1":    17,
-			"J-MR2":    18,
-			"K":        19,
-			"L":        21,
-			"L-MR1":    22,
-			"M":        23,
-			"N":        24,
-			"N-MR1":    25,
-			"O":        26,
-			"O-MR1":    27,
-			"P":        28,
-			"Q":        29,
-			"R":        30,
-			"S":        31,
-			"S-V2":     32,
-			"Tiramisu": 33,
-		}
+		apiLevelsMap := getApiLevelsMapReleasedVersions()
 		for i, codename := range config.PlatformVersionActiveCodenames() {
 			apiLevelsMap[codename] = previewAPILevelBase + i
 		}
@@ -386,20 +480,11 @@
 	createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
 }
 
-func printApiLevelsStarlarkDict(config Config) string {
-	apiLevelsMap := GetApiLevelsMap(config)
-	valDict := make(map[string]string, len(apiLevelsMap))
-	for k, v := range apiLevelsMap {
-		valDict[k] = strconv.Itoa(v)
-	}
-	return starlark_fmt.PrintDict(valDict, 0)
-}
-
 func StarlarkApiLevelConfigs(config Config) string {
 	return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
-_api_levels = %s
+_api_levels_released_versions = %s
 
-api_levels = _api_levels
-`, printApiLevelsStarlarkDict(config),
+api_levels_released_versions = _api_levels_released_versions
+`, starlark_fmt.PrintStringIntDict(getApiLevelsMapReleasedVersions(), 0),
 	)
 }
diff --git a/android/arch.go b/android/arch.go
index 6acf9cf..4b4691b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1694,6 +1694,7 @@
 	return []archConfig{
 		{"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
 		{"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
+		{"riscv64", "", "", []string{"riscv64"}},
 		{"x86_64", "", "", []string{"x86_64"}},
 		{"x86", "", "", []string{"x86"}},
 	}
diff --git a/android/arch_list.go b/android/arch_list.go
index 578904c..ab644a4 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -26,6 +26,7 @@
 		"armv8-a-branchprot",
 		"armv8-2a",
 		"armv8-2a-dotprod",
+		"armv9-a",
 	},
 	X86: {
 		"amberlake",
@@ -71,6 +72,7 @@
 		"cortex-a8",
 		"cortex-a9",
 		"cortex-a15",
+		"cortex-a32",
 		"cortex-a53",
 		"cortex-a53.a57",
 		"cortex-a55",
@@ -148,6 +150,9 @@
 		"armv8-2a-dotprod": {
 			"dotprod",
 		},
+		"armv9-a": {
+			"dotprod",
+		},
 	},
 	X86: {
 		"amberlake": {
diff --git a/android/arch_test.go b/android/arch_test.go
index e445ec6..5021a67 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -401,7 +401,7 @@
 		{
 			name: "same arch host and host cross",
 			preparer: FixtureModifyConfig(func(config Config) {
-				modifyTestConfigForMusl(config)
+				ModifyTestConfigForMusl(config)
 				modifyTestConfigForMuslArm64HostCross(config)
 			}),
 			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
@@ -705,7 +705,7 @@
 		{
 			name:     "linux_musl",
 			goOS:     "linux",
-			preparer: FixtureModifyConfig(modifyTestConfigForMusl),
+			preparer: FixtureModifyConfig(ModifyTestConfigForMusl),
 			results: []result{
 				{
 					module:   "foo",
diff --git a/android/bazel.go b/android/bazel.go
index 10e9251..1646883 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -352,11 +352,16 @@
 // metrics reporting.
 func MixedBuildsEnabled(ctx BaseModuleContext) bool {
 	module := ctx.Module()
+	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
+	withinApex := !apexInfo.IsForPlatform()
 	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
 		ctx.Os() != Windows && // Windows toolchains are not currently supported.
+		ctx.Os() != LinuxBionic && // Linux Bionic toolchains are not currently supported.
+		ctx.Os() != LinuxMusl && // Linux musl toolchains are not currently supported (b/259266326).
+		ctx.Arch().ArchType != Riscv64 && // TODO(b/262192655) Riscv64 toolchains are not currently supported.
 		module.Enabled() &&
 		convertedToBazel(ctx, module) &&
-		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name())
+		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
 	ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
 	return mixedBuildEnabled
 }
@@ -526,3 +531,15 @@
 
 	return "", errors.New("Main-Class is not found.")
 }
+
+func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath {
+	validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base())
+	ctx.Build(pctx, BuildParams{
+		Rule:        CpNoPreserveSymlink,
+		Description: "run validations " + outputFilePath.Base(),
+		Output:      validatedOutputFilePath,
+		Input:       outputFilePath,
+		Validations: validations,
+	})
+	return validatedOutputFilePath
+}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 8d45041..44dc055 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -29,8 +29,10 @@
 	"android/soong/android/allowlists"
 	"android/soong/bazel/cquery"
 	"android/soong/shared"
+	"android/soong/starlark_fmt"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/metrics"
 
 	"android/soong/bazel"
 )
@@ -43,6 +45,30 @@
 		Description: "",
 		CommandDeps: []string{"${bazelBuildRunfilesTool}"},
 	}, "outDir")
+	allowedBazelEnvironmentVars = []string{
+		// clang-tidy
+		"ALLOW_LOCAL_TIDY_TRUE",
+		"DEFAULT_TIDY_HEADER_DIRS",
+		"TIDY_TIMEOUT",
+		"WITH_TIDY",
+		"WITH_TIDY_FLAGS",
+		"TIDY_EXTERNAL_VENDOR",
+
+		"SKIP_ABI_CHECKS",
+		"UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK",
+		"AUTO_ZERO_INITIALIZE",
+		"AUTO_PATTERN_INITIALIZE",
+		"AUTO_UNINITIALIZE",
+		"USE_CCACHE",
+		"LLVM_NEXT",
+		"ALLOW_UNKNOWN_WARNING_OPTION",
+
+		// Overrides the version in the apex_manifest.json. The version is unique for
+		// each branch (internal, aosp, mainline releases, dessert releases).  This
+		// enables modules built on an older branch to be installed against a newer
+		// device for development purposes.
+		"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION",
+	}
 )
 
 func init() {
@@ -83,12 +109,29 @@
 
 // Portion of cquery map key to describe target configuration.
 type configKey struct {
-	arch   string
-	osType OsType
+	arch    string
+	osType  OsType
+	apexKey ApexConfigKey
+}
+
+type ApexConfigKey struct {
+	WithinApex     bool
+	ApexSdkVersion string
+}
+
+func (c ApexConfigKey) String() string {
+	return fmt.Sprintf("%s_%s", withinApexToString(c.WithinApex), c.ApexSdkVersion)
+}
+
+func withinApexToString(withinApex bool) string {
+	if withinApex {
+		return "within_apex"
+	}
+	return ""
 }
 
 func (c configKey) String() string {
-	return fmt.Sprintf("%s::%s", c.arch, c.osType)
+	return fmt.Sprintf("%s::%s::%s", c.arch, c.osType, c.apexKey)
 }
 
 // Map key to describe bazel cquery requests.
@@ -110,6 +153,10 @@
 	return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
 }
 
+type invokeBazelContext interface {
+	GetEventHandler() *metrics.EventHandler
+}
+
 // BazelContext is a context object useful for interacting with Bazel during
 // the course of a build. Use of Bazel to evaluate part of the build graph
 // is referred to as a "mixed build". (Some modules are managed by Soong,
@@ -146,27 +193,29 @@
 	// Issues commands to Bazel to receive results for all cquery requests
 	// queued in the BazelContext. The ctx argument is optional and is only
 	// used for performance data collection
-	InvokeBazel(config Config, ctx *Context) error
+	InvokeBazel(config Config, ctx invokeBazelContext) error
 
 	// Returns true if Bazel handling is enabled for the module with the given name.
 	// Note that this only implies "bazel mixed build" allowlisting. The caller
 	// should independently verify the module is eligible for Bazel handling
 	// (for example, that it is MixedBuildBuildable).
-	IsModuleNameAllowed(moduleName string) bool
+	IsModuleNameAllowed(moduleName string, withinApex bool) bool
+
+	IsModuleDclaAllowed(moduleName string) bool
 
 	// Returns the bazel output base (the root directory for all bazel intermediate outputs).
 	OutputBase() string
 
 	// Returns build statements which should get registered to reflect Bazel's outputs.
-	BuildStatementsToRegister() []bazel.BuildStatement
+	BuildStatementsToRegister() []*bazel.BuildStatement
 
 	// Returns the depsets defined in Bazel's aquery response.
 	AqueryDepsets() []bazel.AqueryDepset
 }
 
 type bazelRunner interface {
-	createBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
-	issueBazelCommand(bazelCmd *exec.Cmd) (output string, errorMessage string, error error)
+	createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
+	issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
 }
 
 type bazelPaths struct {
@@ -183,14 +232,16 @@
 // and their results after the requests have been made.
 type mixedBuildBazelContext struct {
 	bazelRunner
-	paths        *bazelPaths
-	requests     map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
-	requestMutex sync.Mutex         // requests can be written in parallel
+	paths *bazelPaths
+	// cquery requests that have not yet been issued to Bazel. This list is maintained
+	// in a sorted state, and is guaranteed to have no duplicates.
+	requests     []cqueryKey
+	requestMutex sync.Mutex // requests can be written in parallel
 
 	results map[cqueryKey]string // Results of cquery requests after Bazel invocations
 
 	// Build statements which should get registered to reflect Bazel's outputs.
-	buildStatements []bazel.BuildStatement
+	buildStatements []*bazel.BuildStatement
 
 	// Depsets which should be used for Bazel's build statements.
 	depsets []bazel.AqueryDepset
@@ -203,6 +254,8 @@
 	bazelDisabledModules map[string]bool
 	// Per-module allowlist to opt modules in to bazel handling.
 	bazelEnabledModules map[string]bool
+	// DCLA modules are enabled when used in apex.
+	bazelDclaEnabledModules map[string]bool
 	// If true, modules are bazel-enabled by default, unless present in bazelDisabledModules.
 	modulesDefaultToBazel bool
 
@@ -226,49 +279,78 @@
 	LabelToPythonBinary map[string]string
 	LabelToApexInfo     map[string]cquery.ApexInfo
 	LabelToCcBinary     map[string]cquery.CcUnstrippedInfo
+
+	BazelRequests map[string]bool
 }
 
-func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
-	panic("unimplemented")
+func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+	key := BuildMockBazelContextRequestKey(label, requestType, cfgKey.arch, cfgKey.osType, cfgKey.apexKey)
+	if m.BazelRequests == nil {
+		m.BazelRequests = make(map[string]bool)
+	}
+	m.BazelRequests[key] = true
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
-	result, _ := m.LabelToOutputFiles[label]
+	result, ok := m.LabelToOutputFiles[label]
+	if !ok {
+		return []string{}, fmt.Errorf("no target with label %q in LabelToOutputFiles", label)
+	}
 	return result, nil
 }
 
-func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
-	result, _ := m.LabelToCcInfo[label]
+func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+	result, ok := m.LabelToCcInfo[label]
+	if !ok {
+		key := BuildMockBazelContextResultKey(label, cfgKey.arch, cfgKey.osType, cfgKey.apexKey)
+		result, ok = m.LabelToCcInfo[key]
+		if !ok {
+			return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label)
+		}
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
-	result, _ := m.LabelToPythonBinary[label]
+	result, ok := m.LabelToPythonBinary[label]
+	if !ok {
+		return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label)
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) {
-	result, _ := m.LabelToApexInfo[label]
+	result, ok := m.LabelToApexInfo[label]
+	if !ok {
+		return cquery.ApexInfo{}, fmt.Errorf("no target with label %q in LabelToApexInfo", label)
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) {
-	result, _ := m.LabelToCcBinary[label]
+	result, ok := m.LabelToCcBinary[label]
+	if !ok {
+		return cquery.CcUnstrippedInfo{}, fmt.Errorf("no target with label %q in LabelToCcBinary", label)
+	}
 	return result, nil
 }
 
-func (m MockBazelContext) InvokeBazel(_ Config, _ *Context) error {
+func (m MockBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) IsModuleNameAllowed(_ string) bool {
+func (m MockBazelContext) IsModuleNameAllowed(_ string, _ bool) bool {
+	return true
+}
+
+func (m MockBazelContext) IsModuleDclaAllowed(_ string) bool {
 	return true
 }
 
 func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
 
-func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
-	return []bazel.BuildStatement{}
+func (m MockBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
+	return []*bazel.BuildStatement{}
 }
 
 func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
@@ -277,11 +359,53 @@
 
 var _ BazelContext = MockBazelContext{}
 
+func BuildMockBazelContextRequestKey(label string, request cqueryRequest, arch string, osType OsType, apexKey ApexConfigKey) string {
+	cfgKey := configKey{
+		arch:    arch,
+		osType:  osType,
+		apexKey: apexKey,
+	}
+
+	return strings.Join([]string{label, request.Name(), cfgKey.String()}, "_")
+}
+
+func BuildMockBazelContextResultKey(label string, arch string, osType OsType, apexKey ApexConfigKey) string {
+	cfgKey := configKey{
+		arch:    arch,
+		osType:  osType,
+		apexKey: apexKey,
+	}
+
+	return strings.Join([]string{label, cfgKey.String()}, "_")
+}
+
 func (bazelCtx *mixedBuildBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
 	key := makeCqueryKey(label, requestType, cfgKey)
 	bazelCtx.requestMutex.Lock()
 	defer bazelCtx.requestMutex.Unlock()
-	bazelCtx.requests[key] = true
+
+	// Insert key into requests, maintaining the sort, and only if it's not duplicate.
+	keyString := key.String()
+	foundEqual := false
+	notLessThanKeyString := func(i int) bool {
+		s := bazelCtx.requests[i].String()
+		v := strings.Compare(s, keyString)
+		if v == 0 {
+			foundEqual = true
+		}
+		return v >= 0
+	}
+	targetIndex := sort.Search(len(bazelCtx.requests), notLessThanKeyString)
+	if foundEqual {
+		return
+	}
+
+	if targetIndex == len(bazelCtx.requests) {
+		bazelCtx.requests = append(bazelCtx.requests, key)
+	} else {
+		bazelCtx.requests = append(bazelCtx.requests[:targetIndex+1], bazelCtx.requests[targetIndex:]...)
+		bazelCtx.requests[targetIndex] = key
+	}
 }
 
 func (bazelCtx *mixedBuildBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
@@ -353,7 +477,7 @@
 	panic("implement me")
 }
 
-func (n noopBazelContext) InvokeBazel(_ Config, _ *Context) error {
+func (n noopBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
 	panic("unimplemented")
 }
 
@@ -361,26 +485,31 @@
 	return ""
 }
 
-func (n noopBazelContext) IsModuleNameAllowed(_ string) bool {
+func (n noopBazelContext) IsModuleNameAllowed(_ string, _ bool) bool {
 	return false
 }
 
-func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
-	return []bazel.BuildStatement{}
+func (n noopBazelContext) IsModuleDclaAllowed(_ string) bool {
+	return false
+}
+
+func (m noopBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
+	return []*bazel.BuildStatement{}
 }
 
 func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
 	return []bazel.AqueryDepset{}
 }
 
+func addToStringSet(set map[string]bool, items []string) {
+	for _, item := range items {
+		set[item] = true
+	}
+}
+
 func GetBazelEnabledAndDisabledModules(buildMode SoongBuildMode, forceEnabled map[string]struct{}) (map[string]bool, map[string]bool) {
 	disabledModules := map[string]bool{}
 	enabledModules := map[string]bool{}
-	addToStringSet := func(set map[string]bool, items []string) {
-		for _, item := range items {
-			set[item] = true
-		}
-	}
 
 	switch buildMode {
 	case BazelProdMode:
@@ -468,16 +597,24 @@
 	if c.HasDeviceProduct() {
 		targetProduct = c.DeviceProduct()
 	}
-
+	dclaMixedBuildsEnabledList := []string{}
+	if c.BuildMode == BazelProdMode {
+		dclaMixedBuildsEnabledList = allowlists.ProdDclaMixedBuildsEnabledList
+	} else if c.BuildMode == BazelStagingMode {
+		dclaMixedBuildsEnabledList = append(allowlists.ProdDclaMixedBuildsEnabledList,
+			allowlists.StagingDclaMixedBuildsEnabledList...)
+	}
+	dclaEnabledModules := map[string]bool{}
+	addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
 	return &mixedBuildBazelContext{
-		bazelRunner:           &builtinBazelRunner{},
-		paths:                 &paths,
-		requests:              make(map[cqueryKey]bool),
-		modulesDefaultToBazel: c.BuildMode == BazelDevMode,
-		bazelEnabledModules:   enabledModules,
-		bazelDisabledModules:  disabledModules,
-		targetProduct:         targetProduct,
-		targetBuildVariant:    targetBuildVariant,
+		bazelRunner:             &builtinBazelRunner{c.UseBazelProxy, absolutePath(c.outDir)},
+		paths:                   &paths,
+		modulesDefaultToBazel:   c.BuildMode == BazelDevMode,
+		bazelEnabledModules:     enabledModules,
+		bazelDisabledModules:    disabledModules,
+		bazelDclaEnabledModules: dclaEnabledModules,
+		targetProduct:           targetProduct,
+		targetBuildVariant:      targetBuildVariant,
 	}, nil
 }
 
@@ -485,16 +622,24 @@
 	return p.metricsDir
 }
 
-func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string) bool {
+func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string, withinApex bool) bool {
 	if context.bazelDisabledModules[moduleName] {
 		return false
 	}
 	if context.bazelEnabledModules[moduleName] {
 		return true
 	}
+	if withinApex && context.IsModuleDclaAllowed(moduleName) {
+		return true
+	}
+
 	return context.modulesDefaultToBazel
 }
 
+func (context *mixedBuildBazelContext) IsModuleDclaAllowed(moduleName string) bool {
+	return context.bazelDclaEnabledModules[moduleName]
+}
+
 func pwdPrefix() string {
 	// Darwin doesn't have /proc
 	if runtime.GOOS != "darwin" {
@@ -520,7 +665,7 @@
 	extraFlags []string
 }
 
-func (r *mockBazelRunner) createBazelCommand(_ *bazelPaths, _ bazel.RunName,
+func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.RunName,
 	command bazelCommand, extraFlags ...string) *exec.Cmd {
 	r.commands = append(r.commands, command)
 	r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
@@ -532,32 +677,57 @@
 	return cmd
 }
 
-func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
+func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) {
 	if command, ok := r.tokens[bazelCmd]; ok {
 		return r.bazelCommandResults[command], "", nil
 	}
 	return "", "", nil
 }
 
-type builtinBazelRunner struct{}
+type builtinBazelRunner struct {
+	useBazelProxy bool
+	outDir        string
+}
 
 // Issues the given bazel command with given build label and additional flags.
 // Returns (stdout, stderr, error). The first and second return values are strings
 // containing the stdout and stderr of the run command, and an error is returned if
 // the invocation returned an error code.
-func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
-	stderr := &bytes.Buffer{}
-	bazelCmd.Stderr = stderr
-	if output, err := bazelCmd.Output(); err != nil {
-		return "", string(stderr.Bytes()),
-			fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
-				err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
+func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
+	if r.useBazelProxy {
+		eventHandler.Begin("client_proxy")
+		defer eventHandler.End("client_proxy")
+		proxyClient := bazel.NewProxyClient(r.outDir)
+		// Omit the arg containing the Bazel binary, as that is handled by the proxy
+		// server.
+		bazelFlags := bazelCmd.Args[1:]
+		// TODO(b/270989498): Refactor these functions to not take exec.Cmd, as its
+		// not actually executed for client proxying.
+		resp, err := proxyClient.IssueCommand(bazel.CmdRequest{bazelFlags, bazelCmd.Env})
+
+		if err != nil {
+			return "", "", err
+		}
+		if len(resp.ErrorString) > 0 {
+			return "", "", fmt.Errorf(resp.ErrorString)
+		}
+		return resp.Stdout, resp.Stderr, nil
 	} else {
-		return string(output), string(stderr.Bytes()), nil
+		eventHandler.Begin("bazel command")
+		defer eventHandler.End("bazel command")
+		stderr := &bytes.Buffer{}
+		bazelCmd.Stderr = stderr
+		if output, err := bazelCmd.Output(); err != nil {
+			return "", string(stderr.Bytes()),
+				fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+					err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
+		} else {
+			return string(output), string(stderr.Bytes()), nil
+		}
 	}
 }
 
-func (r *builtinBazelRunner) createBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
+func (r *builtinBazelRunner) createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) *exec.Cmd {
 	cmdFlags := []string{
 		"--output_base=" + absolutePath(paths.outputBase),
@@ -566,15 +736,6 @@
 		// TODO(asmundak): is it needed in every build?
 		"--profile=" + shared.BazelMetricsFilename(paths, runName),
 
-		// Set default platforms to canonicalized values for mixed builds requests.
-		// If these are set in the bazelrc, they will have values that are
-		// non-canonicalized to @sourceroot labels, and thus be invalid when
-		// referenced from the buildroot.
-		//
-		// The actual platform values here may be overridden by configuration
-		// transitions from the buildroot.
-		fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"),
-
 		// We don't need to set --host_platforms because it's set in bazelrc files
 		// that the bazel shell script wrapper passes
 
@@ -601,6 +762,13 @@
 		// explicitly in BUILD files.
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
 	}
+	for _, envvar := range allowedBazelEnvironmentVars {
+		val := config.Getenv(envvar)
+		if val == "" {
+			continue
+		}
+		extraEnv = append(extraEnv, fmt.Sprintf("%s=%s", envvar, val))
+	}
 	bazelCmd.Env = append(os.Environ(), extraEnv...)
 
 	return bazelCmd
@@ -619,21 +787,37 @@
 #####################################################
 # This file is generated by soong_build. Do not edit.
 #####################################################
-
 def _config_node_transition_impl(settings, attr):
     if attr.os == "android" and attr.arch == "target":
         target = "{PRODUCT}-{VARIANT}"
     else:
         target = "{PRODUCT}-{VARIANT}_%s_%s" % (attr.os, attr.arch)
-    return {
+    apex_name = ""
+    if attr.within_apex:
+        # //build/bazel/rules/apex:apex_name has to be set to a non_empty value,
+        # otherwise //build/bazel/rules/apex:non_apex will be true and the
+        # "-D__ANDROID_APEX__" compiler flag will be missing. Apex_name is used
+        # in some validation on bazel side which don't really apply in mixed
+        # build because soong will do the work, so we just set it to a fixed
+        # value here.
+        apex_name = "dcla_apex"
+    outputs = {
         "//command_line_option:platforms": "@soong_injection//product_config_platforms/products/{PRODUCT}-{VARIANT}:%s" % target,
+        "@//build/bazel/rules/apex:within_apex": attr.within_apex,
+        "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version,
+        "@//build/bazel/rules/apex:apex_name": apex_name,
     }
 
+    return outputs
+
 _config_node_transition = transition(
     implementation = _config_node_transition_impl,
     inputs = [],
     outputs = [
         "//command_line_option:platforms",
+        "@//build/bazel/rules/apex:within_apex",
+        "@//build/bazel/rules/apex:min_sdk_version",
+        "@//build/bazel/rules/apex:apex_name",
     ],
 )
 
@@ -643,9 +827,11 @@
 config_node = rule(
     implementation = _passthrough_rule_impl,
     attrs = {
-        "arch" : attr.string(mandatory = True),
-        "os"   : attr.string(mandatory = True),
-        "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
+        "arch"    : attr.string(mandatory = True),
+        "os"      : attr.string(mandatory = True),
+        "within_apex" : attr.bool(default = False),
+        "apex_sdk_version" : attr.string(mandatory = True),
+        "deps"    : attr.label_list(cfg = _config_node_transition, allow_files = True),
         "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
     },
 )
@@ -704,6 +890,8 @@
 config_node(name = "%s",
     arch = "%s",
     os = "%s",
+    within_apex = %s,
+    apex_sdk_version = "%s",
     deps = [%s],
     testonly = True, # Unblocks testonly deps.
 )
@@ -712,24 +900,46 @@
 	configNodesSection := ""
 
 	labelsByConfig := map[string][]string{}
-	for val := range context.requests {
+
+	for _, val := range context.requests {
 		labelString := fmt.Sprintf("\"@%s\"", val.label)
 		configString := getConfigString(val)
 		labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
 	}
 
+	// Configs need to be sorted to maintain determinism of the BUILD file.
+	sortedConfigs := make([]string, 0, len(labelsByConfig))
+	for val := range labelsByConfig {
+		sortedConfigs = append(sortedConfigs, val)
+	}
+	sort.Slice(sortedConfigs, func(i, j int) bool { return sortedConfigs[i] < sortedConfigs[j] })
+
 	allLabels := []string{}
-	for configString, labels := range labelsByConfig {
+	for _, configString := range sortedConfigs {
+		labels := labelsByConfig[configString]
 		configTokens := strings.Split(configString, "|")
-		if len(configTokens) != 2 {
+		if len(configTokens) < 2 {
 			panic(fmt.Errorf("Unexpected config string format: %s", configString))
 		}
 		archString := configTokens[0]
 		osString := configTokens[1]
+		withinApex := "False"
+		apexSdkVerString := ""
 		targetString := fmt.Sprintf("%s_%s", osString, archString)
+		if len(configTokens) > 2 {
+			targetString += "_" + configTokens[2]
+			if configTokens[2] == withinApexToString(true) {
+				withinApex = "True"
+			}
+		}
+		if len(configTokens) > 3 {
+			targetString += "_" + configTokens[3]
+			apexSdkVerString = configTokens[3]
+		}
 		allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
 		labelsString := strings.Join(labels, ",\n            ")
-		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
+		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, withinApex, apexSdkVerString,
+			labelsString)
 	}
 
 	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n            ")))
@@ -750,7 +960,7 @@
 // request type.
 func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte {
 	requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
-	for val := range context.requests {
+	for _, val := range context.requests {
 		cqueryId := getCqueryId(val)
 		mapEntryString := fmt.Sprintf("%q : True", cqueryId)
 		requestTypeToCqueryIdEntries[val.requestType] =
@@ -790,31 +1000,6 @@
 	formatString := `
 # This file is generated by soong_build. Do not edit.
 
-# a drop-in replacement for json.encode(), not available in cquery environment
-# TODO(cparsons): bring json module in and remove this function
-def json_encode(input):
-  # Avoiding recursion by limiting
-  #  - a dict to contain anything except a dict
-  #  - a list to contain only primitives
-  def encode_primitive(p):
-    t = type(p)
-    if t == "string" or t == "int":
-      return repr(p)
-    fail("unsupported value '%s' of type '%s'" % (p, type(p)))
-
-  def encode_list(list):
-    return "[%s]" % ", ".join([encode_primitive(item) for item in list])
-
-  def encode_list_or_primitive(v):
-    return encode_list(v) if type(v) == "list" else encode_primitive(v)
-
-  if type(input) == "dict":
-    # TODO(juu): the result is read line by line so can't use '\n' yet
-    kv_pairs = [("%s: %s" % (encode_primitive(k), encode_list_or_primitive(v))) for (k, v) in input.items()]
-    return "{ %s }" % ", ".join(kv_pairs)
-  else:
-    return encode_list_or_primitive(input)
-
 {LABEL_REGISTRATION_MAP_SECTION}
 
 {FUNCTION_DEF_SECTION}
@@ -825,6 +1010,7 @@
   # Soong treats filegroups, but it may not be the case with manually-written
   # filegroup BUILD targets.
   buildoptions = build_options(target)
+
   if buildoptions == None:
     # File targets do not have buildoptions. File targets aren't associated with
     #  any specific platform architecture in mixed builds, so use the host.
@@ -841,15 +1027,26 @@
   if not platform_name.startswith("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}"):
     fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
   platform_name = platform_name.removeprefix("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}").removeprefix("_")
+  config_key = ""
   if not platform_name:
-    return "target|android"
+    config_key = "target|android"
   elif platform_name.startswith("android_"):
-    return platform_name.removeprefix("android_") + "|android"
+    config_key = platform_name.removeprefix("android_") + "|android"
   elif platform_name.startswith("linux_"):
-    return platform_name.removeprefix("linux_") + "|linux"
+    config_key = platform_name.removeprefix("linux_") + "|linux"
   else:
     fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
 
+  within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex")
+  apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version")
+
+  if within_apex:
+    config_key += "|within_apex"
+  if apex_sdk_version != None and len(apex_sdk_version) > 0:
+    config_key += "|" + apex_sdk_version
+
+  return config_key
+
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
@@ -906,11 +1103,10 @@
 
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
-func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx *Context) error {
-	if ctx != nil {
-		ctx.EventHandler.Begin("bazel")
-		defer ctx.EventHandler.End("bazel")
-	}
+func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
+	eventHandler := ctx.GetEventHandler()
+	eventHandler.Begin("bazel")
+	defer eventHandler.End("bazel")
 
 	if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
 		if err := os.MkdirAll(metricsDir, 0777); err != nil {
@@ -918,26 +1114,25 @@
 		}
 	}
 	context.results = make(map[cqueryKey]string)
-	if err := context.runCquery(ctx); err != nil {
+	if err := context.runCquery(config, ctx); err != nil {
 		return err
 	}
 	if err := context.runAquery(config, ctx); err != nil {
 		return err
 	}
-	if err := context.generateBazelSymlinks(ctx); err != nil {
+	if err := context.generateBazelSymlinks(config, ctx); err != nil {
 		return err
 	}
 
 	// Clear requests.
-	context.requests = map[cqueryKey]bool{}
+	context.requests = []cqueryKey{}
 	return nil
 }
 
-func (context *mixedBuildBazelContext) runCquery(ctx *Context) error {
-	if ctx != nil {
-		ctx.EventHandler.Begin("cquery")
-		defer ctx.EventHandler.End("cquery")
-	}
+func (context *mixedBuildBazelContext) runCquery(config Config, ctx invokeBazelContext) error {
+	eventHandler := ctx.GetEventHandler()
+	eventHandler.Begin("cquery")
+	defer eventHandler.End("cquery")
 	soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
 	mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
 	if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
@@ -946,23 +1141,27 @@
 			return err
 		}
 	}
-	if err := os.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
+	if err := writeFileBytesIfChanged(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
 		return err
 	}
-	if err := os.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
+	if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
 		return err
 	}
-	if err := os.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
+	if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
 		return err
 	}
 	cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
-	if err := os.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
+	if err := writeFileBytesIfChanged(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
 		return err
 	}
 
-	cqueryCommandWithFlag := context.createBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
-		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
-	cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag)
+	extraFlags := []string{"--output=starlark", "--starlark:file=" + absolutePath(cqueryFileRelpath)}
+	if Bool(config.productVariables.ClangCoverage) {
+		extraFlags = append(extraFlags, "--collect_code_coverage")
+	}
+
+	cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
+	cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
 	if cqueryErr != nil {
 		return cqueryErr
 	}
@@ -977,7 +1176,7 @@
 			cqueryResults[splitLine[0]] = splitLine[1]
 		}
 	}
-	for val := range context.requests {
+	for _, val := range context.requests {
 		if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
 			context.results[val] = cqueryResult
 		} else {
@@ -988,11 +1187,18 @@
 	return nil
 }
 
-func (context *mixedBuildBazelContext) runAquery(config Config, ctx *Context) error {
-	if ctx != nil {
-		ctx.EventHandler.Begin("aquery")
-		defer ctx.EventHandler.End("aquery")
+func writeFileBytesIfChanged(path string, contents []byte, perm os.FileMode) error {
+	oldContents, err := os.ReadFile(path)
+	if err != nil || !bytes.Equal(contents, oldContents) {
+		err = os.WriteFile(path, contents, perm)
 	}
+	return nil
+}
+
+func (context *mixedBuildBazelContext) runAquery(config Config, ctx invokeBazelContext) error {
+	eventHandler := ctx.GetEventHandler()
+	eventHandler.Begin("aquery")
+	defer eventHandler.End("aquery")
 	// Issue an aquery command to retrieve action information about the bazel build tree.
 	//
 	// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
@@ -1017,28 +1223,27 @@
 			extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
 		}
 	}
-	aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
-		extraFlags...))
+	aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
+		extraFlags...), eventHandler)
 	if err != nil {
 		return err
 	}
-	context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+	context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput), eventHandler)
 	return err
 }
 
-func (context *mixedBuildBazelContext) generateBazelSymlinks(ctx *Context) error {
-	if ctx != nil {
-		ctx.EventHandler.Begin("symlinks")
-		defer ctx.EventHandler.End("symlinks")
-	}
+func (context *mixedBuildBazelContext) generateBazelSymlinks(config Config, ctx invokeBazelContext) error {
+	eventHandler := ctx.GetEventHandler()
+	eventHandler.Begin("symlinks")
+	defer eventHandler.End("symlinks")
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd))
+	_, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler)
 	return err
 }
 
-func (context *mixedBuildBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+func (context *mixedBuildBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
 	return context.buildStatements
 }
 
@@ -1106,6 +1311,11 @@
 	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
 	bazelOutDir := path.Join(executionRoot, "bazel-out")
 	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
+		if buildStatement == nil {
+			continue
+		}
 		if len(buildStatement.Command) > 0 {
 			rule := NewRuleBuilder(pctx, ctx)
 			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
@@ -1120,10 +1330,11 @@
 		// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
 		// because this would cause circular dependency. So, until we move aquery processing
 		// to the 'android' package, we need to handle special cases here.
-		if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
+		switch buildStatement.Mnemonic {
+		case "FileWrite", "SourceSymlinkManifest":
 			out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
 			WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents)
-		} else if buildStatement.Mnemonic == "SymlinkTree" {
+		case "SymlinkTree":
 			// build-runfiles arguments are the manifest file and the target directory
 			// where it creates the symlink tree according to this manifest (and then
 			// writes the MANIFEST file to it).
@@ -1142,14 +1353,14 @@
 					"outDir": outDir,
 				},
 			})
-		} else {
+		default:
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
 	}
 }
 
 // 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) {
 	// executionRoot is the action cwd.
 	cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
 
@@ -1229,7 +1440,16 @@
 		// Use host OS, which is currently hardcoded to be linux.
 		osName = "linux"
 	}
-	return arch + "|" + osName
+	keyString := arch + "|" + osName
+	if key.configKey.apexKey.WithinApex {
+		keyString += "|" + withinApexToString(key.configKey.apexKey.WithinApex)
+	}
+
+	if len(key.configKey.apexKey.ApexSdkVersion) > 0 {
+		keyString += "|" + key.configKey.apexKey.ApexSdkVersion
+	}
+
+	return keyString
 }
 
 func GetConfigKey(ctx BaseModuleContext) configKey {
@@ -1240,6 +1460,29 @@
 	}
 }
 
+func GetConfigKeyApexVariant(ctx BaseModuleContext, apexKey *ApexConfigKey) configKey {
+	configKey := GetConfigKey(ctx)
+
+	if apexKey != nil {
+		configKey.apexKey = ApexConfigKey{
+			WithinApex:     apexKey.WithinApex,
+			ApexSdkVersion: apexKey.ApexSdkVersion,
+		}
+	}
+
+	return configKey
+}
+
 func bazelDepsetName(contentHash string) string {
 	return fmt.Sprintf("bazel_depset_%s", contentHash)
 }
+
+func EnvironmentVarsFile(config Config) string {
+	return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
+_env = %s
+
+env = _env
+`,
+		starlark_fmt.PrintStringList(allowedBazelEnvironmentVars, 0),
+	)
+}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 013e19c..c67d7fb 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -11,33 +11,57 @@
 	"android/soong/bazel/cquery"
 	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 
+	"github.com/google/blueprint/metrics"
 	"google.golang.org/protobuf/proto"
 )
 
 var testConfig = TestConfig("out", nil, "", nil)
 
+type testInvokeBazelContext struct{}
+
+func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
+	return &metrics.EventHandler{}
+}
+
 func TestRequestResultsAfterInvokeBazel(t *testing.T) {
-	label := "@//foo:bar"
-	cfg := configKey{"arm64_armv8-a", Android}
+	label_foo := "@//foo:foo"
+	label_bar := "@//foo:bar"
+	apexKey := ApexConfigKey{
+		WithinApex:     true,
+		ApexSdkVersion: "29",
+	}
+	cfg_foo := configKey{"arm64_armv8-a", Android, apexKey}
+	cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android}
+	cmd_results := []string{
+		`@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`,
+		`@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
+	}
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
+		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: strings.Join(cmd_results, "\n"),
 	})
-	bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
-	err := bazelContext.InvokeBazel(testConfig, nil)
+
+	bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
+	bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
+	err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
-	g, err := bazelContext.GetOutputFiles(label, cfg)
+	verifyCqueryResult(t, bazelContext, label_foo, cfg_foo, "out/foo/foo.txt")
+	verifyCqueryResult(t, bazelContext, label_bar, cfg_bar, "out/foo/bar.txt")
+}
+
+func verifyCqueryResult(t *testing.T, ctx *mixedBuildBazelContext, label string, cfg configKey, result string) {
+	g, err := ctx.GetOutputFiles(label, cfg)
 	if err != nil {
 		t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
-	} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
+	} else if w := []string{result}; !reflect.DeepEqual(w, g) {
 		t.Errorf("Expected output %s, got %s", w, g)
 	}
 }
 
 func TestInvokeBazelWritesBazelFiles(t *testing.T) {
 	bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
-	err := bazelContext.InvokeBazel(testConfig, nil)
+	err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
@@ -118,7 +142,7 @@
 		bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
 			bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
 
-		err = bazelContext.InvokeBazel(testConfig, nil)
+		err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
 		if err != nil {
 			t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
 		}
@@ -168,10 +192,86 @@
 	}
 }
 
+func TestBazelRequestsSorted(t *testing.T) {
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+	cfgKeyArm64Android := configKey{arch: "arm64_armv8-a", osType: Android}
+	cfgKeyArm64Linux := configKey{arch: "arm64_armv8-a", osType: Linux}
+	cfgKeyOtherAndroid := configKey{arch: "otherarch", osType: Android}
+
+	bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, cfgKeyArm64Linux)
+	bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyOtherAndroid)
+	bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, cfgKeyOtherAndroid)
+
+	if len(bazelContext.requests) != 7 {
+		t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
+	}
+
+	lastString := ""
+	for _, val := range bazelContext.requests {
+		thisString := val.String()
+		if thisString <= lastString {
+			t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString)
+		}
+		lastString = thisString
+	}
+}
+
+func TestIsModuleNameAllowed(t *testing.T) {
+	libDisabled := "lib_disabled"
+	libEnabled := "lib_enabled"
+	libDclaWithinApex := "lib_dcla_within_apex"
+	libDclaNonApex := "lib_dcla_non_apex"
+	libNotConverted := "lib_not_converted"
+
+	disabledModules := map[string]bool{
+		libDisabled: true,
+	}
+	enabledModules := map[string]bool{
+		libEnabled: true,
+	}
+	dclaEnabledModules := map[string]bool{
+		libDclaWithinApex: true,
+		libDclaNonApex:    true,
+	}
+
+	bazelContext := &mixedBuildBazelContext{
+		modulesDefaultToBazel:   false,
+		bazelEnabledModules:     enabledModules,
+		bazelDisabledModules:    disabledModules,
+		bazelDclaEnabledModules: dclaEnabledModules,
+	}
+
+	if bazelContext.IsModuleNameAllowed(libDisabled, true) {
+		t.Fatalf("%s shouldn't be allowed for mixed build", libDisabled)
+	}
+
+	if !bazelContext.IsModuleNameAllowed(libEnabled, true) {
+		t.Fatalf("%s should be allowed for mixed build", libEnabled)
+	}
+
+	if !bazelContext.IsModuleNameAllowed(libDclaWithinApex, true) {
+		t.Fatalf("%s should be allowed for mixed build", libDclaWithinApex)
+	}
+
+	if bazelContext.IsModuleNameAllowed(libDclaNonApex, false) {
+		t.Fatalf("%s shouldn't be allowed for mixed build", libDclaNonApex)
+	}
+
+	if bazelContext.IsModuleNameAllowed(libNotConverted, true) {
+		t.Fatalf("%s shouldn't be allowed for mixed build", libNotConverted)
+	}
+}
+
 func verifyExtraFlags(t *testing.T, config Config, expected string) string {
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
 
-	err := bazelContext.InvokeBazel(config, nil)
+	err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
@@ -204,7 +304,6 @@
 	return &mixedBuildBazelContext{
 		bazelRunner: runner,
 		paths:       &p,
-		requests:    map[cqueryKey]bool{},
 	}, p.soongOutDir
 }
 
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index e151521..bad7baf 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -461,11 +461,6 @@
 	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
 }
 
-// ModuleFromBazelLabel reverses the logic in bp2buildModuleLabel
-func ModuleFromBazelLabel(label string) string {
-	return strings.Split(label, ":")[1]
-}
-
 // BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
 type BazelOutPath struct {
 	OutputPath
diff --git a/android/config.go b/android/config.go
index a8b0a40..e0b661b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -52,6 +52,15 @@
 // FutureApiLevelInt is a placeholder constant for unreleased API levels.
 const FutureApiLevelInt = 10000
 
+// PrivateApiLevel represents the api level of SdkSpecPrivate (sdk_version: "")
+// This api_level exists to differentiate user-provided "" from "current" sdk_version
+// The differentiation is necessary to enable different validation rules for these two possible values.
+var PrivateApiLevel = ApiLevel{
+	value:     "current",             // The value is current since aidl expects `current` as the default (TestAidlFlagsWithMinSdkVersion)
+	number:    FutureApiLevelInt + 1, // This is used to differentiate it from FutureApiLevel
+	isPreview: true,
+}
+
 // FutureApiLevel represents unreleased API levels.
 var FutureApiLevel = ApiLevel{
 	value:     "current",
@@ -83,10 +92,14 @@
 	ModuleActionsFile   string
 	DocFile             string
 
+	MultitreeBuild bool
+
 	BazelMode                bool
 	BazelModeDev             bool
 	BazelModeStaging         bool
 	BazelForceEnabledModules string
+
+	UseBazelProxy bool
 }
 
 // Build modes that soong_build can run as.
@@ -227,6 +240,10 @@
 	Bp2buildPackageConfig          Bp2BuildConversionAllowlist
 	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
+	// If MultitreeBuild is true then this is one inner tree of a multitree
+	// build directed by the multitree orchestrator.
+	MultitreeBuild bool
+
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
 	// in tests when a path doesn't exist.
 	TestAllowNonExistentPaths bool
@@ -251,6 +268,10 @@
 	// specified modules. They are passed via the command-line flag
 	// "--bazel-force-enabled-modules"
 	bazelForceEnabledModules map[string]struct{}
+
+	// If true, for any requests to Bazel, communicate with a Bazel proxy using
+	// unix sockets, instead of spawning Bazel as a subprocess.
+	UseBazelProxy bool
 }
 
 type deviceConfig struct {
@@ -397,11 +418,13 @@
 arch_variant_product_var_constraints = _arch_variant_product_var_constraints
 `,
 	}
-	err = os.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644)
+	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 = os.WriteFile(filepath.Join(dir, "BUILD"), []byte(bazel.GeneratedBazelFileWarning), 0644)
+	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "BUILD"),
+		[]byte(bazel.GeneratedBazelFileWarning), 0644)
 	if err != nil {
 		return fmt.Errorf("Could not write BUILD config file %s", err)
 	}
@@ -440,6 +463,9 @@
 		mixedBuildDisabledModules: make(map[string]struct{}),
 		mixedBuildEnabledModules:  make(map[string]struct{}),
 		bazelForceEnabledModules:  make(map[string]struct{}),
+
+		MultitreeBuild: cmdArgs.MultitreeBuild,
+		UseBazelProxy:  cmdArgs.UseBazelProxy,
 	}
 
 	config.deviceConfig = &deviceConfig{
@@ -521,27 +547,33 @@
 		config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	}
 
-	if cmdArgs.SymlinkForestMarker != "" {
-		config.BuildMode = SymlinkForest
-	} else if cmdArgs.Bp2buildMarker != "" {
-		config.BuildMode = Bp2build
-	} else if cmdArgs.BazelQueryViewDir != "" {
-		config.BuildMode = GenerateQueryView
-	} else if cmdArgs.BazelApiBp2buildDir != "" {
-		config.BuildMode = ApiBp2build
-	} else if cmdArgs.ModuleGraphFile != "" {
-		config.BuildMode = GenerateModuleGraph
-	} else if cmdArgs.DocFile != "" {
-		config.BuildMode = GenerateDocFile
-	} else if cmdArgs.BazelModeDev {
-		config.BuildMode = BazelDevMode
-	} else if cmdArgs.BazelMode {
-		config.BuildMode = BazelProdMode
-	} else if cmdArgs.BazelModeStaging {
-		config.BuildMode = BazelStagingMode
-	} else {
-		config.BuildMode = AnalysisNoBazel
+	setBuildMode := func(arg string, mode SoongBuildMode) {
+		if arg != "" {
+			if config.BuildMode != AnalysisNoBazel {
+				fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
+				os.Exit(1)
+			}
+			config.BuildMode = mode
+		}
 	}
+	setBazelMode := func(arg bool, argName string, mode SoongBuildMode) {
+		if arg {
+			if config.BuildMode != AnalysisNoBazel {
+				fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", argName)
+				os.Exit(1)
+			}
+			config.BuildMode = mode
+		}
+	}
+	setBuildMode(cmdArgs.SymlinkForestMarker, SymlinkForest)
+	setBuildMode(cmdArgs.Bp2buildMarker, Bp2build)
+	setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView)
+	setBuildMode(cmdArgs.BazelApiBp2buildDir, ApiBp2build)
+	setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
+	setBuildMode(cmdArgs.DocFile, GenerateDocFile)
+	setBazelMode(cmdArgs.BazelModeDev, "--bazel-mode-dev", BazelDevMode)
+	setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
+	setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
 
 	config.BazelContext, err = NewBazelContext(config)
 	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
@@ -719,10 +751,6 @@
 	return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
 }
 
-func (c *config) TargetsJava17() bool {
-	return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_17")
-}
-
 // EnvDeps returns the environment variables this build depends on. The first
 // call to this function blocks future reads from the environment.
 func (c *config) EnvDeps() map[string]string {
@@ -871,6 +899,8 @@
 // DefaultAppTargetSdk returns the API level that platform apps are targeting.
 // This converts a codename to the exact ApiLevel it represents.
 func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel {
+	// This logic is replicated in starlark, if changing logic here update starlark code too
+	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=72;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	if Bool(c.productVariables.Platform_sdk_final) {
 		return c.PlatformSdkVersion()
 	}
@@ -1355,6 +1385,11 @@
 		}
 	}
 	if coverage && len(c.config.productVariables.NativeCoverageExcludePaths) > 0 {
+		// Workaround coverage boot failure.
+		// http://b/269981180
+		if strings.HasPrefix(path, "external/protobuf") {
+			coverage = false
+		}
 		if HasAnyPrefix(path, c.config.productVariables.NativeCoverageExcludePaths) {
 			coverage = false
 		}
@@ -1494,6 +1529,10 @@
 	return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
 }
 
+func (c *config) ApexTrimEnabled() bool {
+	return Bool(c.productVariables.TrimmedApex)
+}
+
 func (c *config) EnforceSystemCertificate() bool {
 	return Bool(c.productVariables.EnforceSystemCertificate)
 }
@@ -1741,6 +1780,10 @@
 	return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow
 }
 
+func (c *deviceConfig) BuildBrokenUsesSoongPython2Modules() bool {
+	return c.config.productVariables.BuildBrokenUsesSoongPython2Modules
+}
+
 func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool {
 	return c.config.productVariables.BuildDebugfsRestrictionsEnabled
 }
@@ -1819,3 +1862,14 @@
 		c.mixedBuildDisabledModules[moduleName] = struct{}{}
 	}
 }
+
+// ApiSurfaces directory returns the source path inside the api_surfaces repo
+// (relative to workspace root).
+func (c *config) ApiSurfacesDir(s ApiSurface, version string) string {
+	return filepath.Join(
+		"build",
+		"bazel",
+		"api_surfaces",
+		s.String(),
+		version)
+}
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
index 2beeb51..830890d 100644
--- a/android/config_bp2build.go
+++ b/android/config_bp2build.go
@@ -95,6 +95,15 @@
 	return ev.pctx.VariableConfigMethod(name, method)
 }
 
+func (ev ExportedVariables) ExportStringStaticVariableWithEnvOverride(name, envVar, defaultVal string) {
+	ev.ExportVariableConfigMethod(name, func(config Config) string {
+		if override := config.Getenv(envVar); override != "" {
+			return override
+		}
+		return defaultVal
+	})
+}
+
 // ExportSourcePathVariable declares a static "source path" variable and exports
 // it to Bazel's toolchain.
 func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) {
diff --git a/android/defaults.go b/android/defaults.go
index 7906e94..31d6014 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -15,8 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"fmt"
 	"reflect"
 
 	"github.com/google/blueprint"
@@ -55,7 +53,7 @@
 	d.hook = hook
 }
 
-func (d *DefaultableModuleBase) callHookIfAvailable(ctx DefaultableHookContext) {
+func (d *DefaultableModuleBase) CallHookIfAvailable(ctx DefaultableHookContext) {
 	if d.hook != nil {
 		d.hook(ctx)
 	}
@@ -69,11 +67,9 @@
 	// Set the property structures into which defaults will be added.
 	setProperties(props []interface{}, variableProperties interface{})
 
-	// Apply defaults from the supplied DefaultsModule to the property structures supplied to
+	// Apply defaults from the supplied Defaults to the property structures supplied to
 	// setProperties(...).
-	applyDefaults(TopDownMutatorContext, []DefaultsModule)
-
-	applySingleDefaultsWithTracker(EarlyModuleContext, DefaultsModule, defaultsTrackerFunc)
+	applyDefaults(TopDownMutatorContext, []Defaults)
 
 	// Set the hook to be called after any defaults have been applied.
 	//
@@ -82,7 +78,7 @@
 	SetDefaultableHook(hook DefaultableHook)
 
 	// Call the hook if specified.
-	callHookIfAvailable(context DefaultableHookContext)
+	CallHookIfAvailable(context DefaultableHookContext)
 }
 
 type DefaultableModule interface {
@@ -119,23 +115,9 @@
 	Defaults_visibility []string
 }
 
-// AdditionalDefaultsProperties contains properties of defaults modules which
-// can have other defaults applied.
-type AdditionalDefaultsProperties struct {
-
-	// The list of properties set by the default whose values must not be changed by any module that
-	// applies these defaults. It is an error if a property is not supported by the defaults module or
-	// has not been set to a non-zero value. If this contains "*" then that must be the only entry in
-	// which case all properties that are set on this defaults will be protected (except the
-	// protected_properties and visibility.
-	Protected_properties []string
-}
-
 type DefaultsModuleBase struct {
 	DefaultableModuleBase
 
-	defaultsProperties AdditionalDefaultsProperties
-
 	// Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
 	// target. This is primarily useful for modules that were architecture specific and instead are
 	// handled in Bazel as a select().
@@ -169,18 +151,6 @@
 	// DefaultsModuleBase will type-assert to the Defaults interface.
 	isDefaults() bool
 
-	// additionalDefaultableProperties returns additional properties provided by the defaults which
-	// can themselves have defaults applied.
-	additionalDefaultableProperties() []interface{}
-
-	// protectedProperties returns the names of the properties whose values cannot be changed by a
-	// module that applies these defaults.
-	protectedProperties() []string
-
-	// setProtectedProperties sets the names of the properties whose values cannot be changed by a
-	// module that applies these defaults.
-	setProtectedProperties(protectedProperties []string)
-
 	// Get the structures containing the properties for which defaults can be provided.
 	properties() []interface{}
 
@@ -197,18 +167,6 @@
 	Bazelable
 }
 
-func (d *DefaultsModuleBase) additionalDefaultableProperties() []interface{} {
-	return []interface{}{&d.defaultsProperties}
-}
-
-func (d *DefaultsModuleBase) protectedProperties() []string {
-	return d.defaultsProperties.Protected_properties
-}
-
-func (d *DefaultsModuleBase) setProtectedProperties(protectedProperties []string) {
-	d.defaultsProperties.Protected_properties = protectedProperties
-}
-
 func (d *DefaultsModuleBase) properties() []interface{} {
 	return d.defaultableProperties
 }
@@ -232,10 +190,6 @@
 		&ApexProperties{},
 		&distProperties{})
 
-	// Additional properties of defaults modules that can themselves have
-	// defaults applied.
-	module.AddProperties(module.additionalDefaultableProperties()...)
-
 	// Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
 	InitBazelModule(module)
 	initAndroidModuleBase(module)
@@ -263,58 +217,6 @@
 
 	// The applicable licenses property for defaults is 'licenses'.
 	setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
-
-	AddLoadHook(module, func(ctx LoadHookContext) {
-
-		protectedProperties := module.protectedProperties()
-		if len(protectedProperties) == 0 {
-			return
-		}
-
-		propertiesAvailable := map[string]struct{}{}
-		propertiesSet := map[string]struct{}{}
-
-		// A defaults tracker which will keep track of which properties have been set on this module.
-		collector := func(defaults DefaultsModule, property string, dstValue interface{}, srcValue interface{}) bool {
-			value := reflect.ValueOf(dstValue)
-			propertiesAvailable[property] = struct{}{}
-			if !value.IsZero() {
-				propertiesSet[property] = struct{}{}
-			}
-			// Skip all the properties so that there are no changes to the defaults.
-			return false
-		}
-
-		// Try and apply this module's defaults to itself, so that the properties can be collected but
-		// skip all the properties so it doesn't actually do anything.
-		module.applySingleDefaultsWithTracker(ctx, module, collector)
-
-		if InList("*", protectedProperties) {
-			if len(protectedProperties) != 1 {
-				ctx.PropertyErrorf("protected_properties", `if specified then "*" must be the only property listed`)
-				return
-			}
-
-			// Do not automatically protect the protected_properties property.
-			delete(propertiesSet, "protected_properties")
-
-			// Or the visibility property.
-			delete(propertiesSet, "visibility")
-
-			// Replace the "*" with the names of all the properties that have been set.
-			protectedProperties = SortedStringKeys(propertiesSet)
-			module.setProtectedProperties(protectedProperties)
-		} else {
-			for _, property := range protectedProperties {
-				if _, ok := propertiesAvailable[property]; !ok {
-					ctx.PropertyErrorf(property, "property is not supported by this module type %q",
-						ctx.ModuleType())
-				} else if _, ok := propertiesSet[property]; !ok {
-					ctx.PropertyErrorf(property, "is not set; protected properties must be explicitly set")
-				}
-			}
-		}
-	})
 }
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
@@ -366,204 +268,35 @@
 	b.setNamespacedVariableProps(dst)
 }
 
-// defaultValueInfo contains information about each default value that applies to a protected
-// property.
-type defaultValueInfo struct {
-	// The DefaultsModule providing the value, which may be defined on that module or applied as a
-	// default from other modules.
-	module Module
-
-	// The default value, as returned by getComparableValue
-	defaultValue reflect.Value
-}
-
-// protectedPropertyInfo contains information about each property that has to be protected when
-// applying defaults.
-type protectedPropertyInfo struct {
-	// True if the property was set on the module to which defaults are applied, this is an error.
-	propertySet bool
-
-	// The original value of the property on the module, as returned by getComparableValue.
-	originalValue reflect.Value
-
-	// A list of defaults for the property that are being applied.
-	defaultValues []defaultValueInfo
-}
-
-// getComparableValue takes a reflect.Value that may be a pointer to another value and returns a
-// reflect.Value to the underlying data or the original if was not a pointer or was nil. The
-// returned values can then be compared for equality.
-func getComparableValue(value reflect.Value) reflect.Value {
-	if value.IsZero() {
-		return value
-	}
-	for value.Kind() == reflect.Ptr {
-		value = value.Elem()
-	}
-	return value
-}
-
 func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
-	defaultsList []DefaultsModule) {
-
-	// Collate information on all the properties protected by each of the default modules applied
-	// to this module.
-	allProtectedProperties := map[string]*protectedPropertyInfo{}
-	for _, defaults := range defaultsList {
-		for _, property := range defaults.protectedProperties() {
-			info := allProtectedProperties[property]
-			if info == nil {
-				info = &protectedPropertyInfo{}
-				allProtectedProperties[property] = info
-			}
-		}
-	}
-
-	// If there are any protected properties then collate information about attempts to change them.
-	var protectedPropertyInfoCollector defaultsTrackerFunc
-	if len(allProtectedProperties) > 0 {
-		protectedPropertyInfoCollector = func(defaults DefaultsModule, property string,
-			dstValue interface{}, srcValue interface{}) bool {
-
-			// If the property is not protected then return immediately.
-			info := allProtectedProperties[property]
-			if info == nil {
-				return true
-			}
-
-			currentValue := reflect.ValueOf(dstValue)
-			if info.defaultValues == nil {
-				info.propertySet = !currentValue.IsZero()
-				info.originalValue = getComparableValue(currentValue)
-			}
-
-			defaultValue := reflect.ValueOf(srcValue)
-			if !defaultValue.IsZero() {
-				info.defaultValues = append(info.defaultValues,
-					defaultValueInfo{defaults, getComparableValue(defaultValue)})
-			}
-
-			return true
-		}
-	}
+	defaultsList []Defaults) {
 
 	for _, defaults := range defaultsList {
 		if ctx.Config().BuildMode == Bp2build {
 			applyNamespacedVariableDefaults(defaults, ctx)
 		}
-
-		defaultable.applySingleDefaultsWithTracker(ctx, defaults, protectedPropertyInfoCollector)
-	}
-
-	// Check the status of any protected properties.
-	for property, info := range allProtectedProperties {
-		if len(info.defaultValues) == 0 {
-			// No defaults were applied to the protected properties. Possibly because this module type
-			// does not support any of them.
-			continue
-		}
-
-		// Check to make sure that there are no conflicts between the defaults.
-		conflictingDefaults := false
-		previousDefaultValue := reflect.ValueOf(false)
-		for _, defaultInfo := range info.defaultValues {
-			defaultValue := defaultInfo.defaultValue
-			if previousDefaultValue.IsZero() {
-				previousDefaultValue = defaultValue
-			} else if !reflect.DeepEqual(previousDefaultValue.Interface(), defaultValue.Interface()) {
-				conflictingDefaults = true
-				break
-			}
-		}
-
-		if conflictingDefaults {
-			var buf bytes.Buffer
-			for _, defaultInfo := range info.defaultValues {
-				buf.WriteString(fmt.Sprintf("\n    defaults module %q provides value %#v",
-					ctx.OtherModuleName(defaultInfo.module), defaultInfo.defaultValue))
-			}
-			result := buf.String()
-			ctx.ModuleErrorf("has conflicting default values for protected property %q:%s", property, result)
-			continue
-		}
-
-		// Now check to see whether there the current module tried to override/append to the defaults.
-		if info.propertySet {
-			originalValue := info.originalValue
-			// Just compare against the first defaults.
-			defaultValue := info.defaultValues[0].defaultValue
-			defaults := info.defaultValues[0].module
-
-			if originalValue.Kind() == reflect.Slice {
-				ctx.ModuleErrorf("attempts to append %q to protected property %q's value of %q defined in module %q",
-					originalValue,
-					property,
-					defaultValue,
-					ctx.OtherModuleName(defaults))
+		for _, prop := range defaultable.defaultableProperties {
+			if prop == defaultable.defaultableVariableProperties {
+				defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
 			} else {
-				same := reflect.DeepEqual(originalValue.Interface(), defaultValue.Interface())
-				message := ""
-				if same {
-					message = fmt.Sprintf(" with a matching value (%#v) so this property can simply be removed.", originalValue)
-				} else {
-					message = fmt.Sprintf(" with a different value (override %#v with %#v) so removing the property may necessitate other changes.", defaultValue, originalValue)
-				}
-				ctx.ModuleErrorf("attempts to override protected property %q defined in module %q%s",
-					property,
-					ctx.OtherModuleName(defaults), message)
+				defaultable.applyDefaultProperties(ctx, defaults, prop)
 			}
 		}
 	}
 }
 
-func (defaultable *DefaultableModuleBase) applySingleDefaultsWithTracker(ctx EarlyModuleContext, defaults DefaultsModule, tracker defaultsTrackerFunc) {
-	for _, prop := range defaultable.defaultableProperties {
-		var err error
-		if prop == defaultable.defaultableVariableProperties {
-			err = defaultable.applyDefaultVariableProperties(defaults, prop, tracker)
-		} else {
-			err = defaultable.applyDefaultProperties(defaults, prop, tracker)
-		}
-		if err != nil {
-			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-				ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-			} else {
-				panic(err)
-			}
-		}
-	}
-}
-
-// defaultsTrackerFunc is the type of a function that can be used to track how defaults are applied.
-type defaultsTrackerFunc func(defaults DefaultsModule, property string,
-	dstValue interface{}, srcValue interface{}) bool
-
-// filterForTracker wraps a defaultsTrackerFunc in a proptools.ExtendPropertyFilterFunc
-func filterForTracker(defaults DefaultsModule, tracker defaultsTrackerFunc) proptools.ExtendPropertyFilterFunc {
-	if tracker == nil {
-		return nil
-	}
-	return func(property string,
-		dstField, srcField reflect.StructField,
-		dstValue, srcValue interface{}) (bool, error) {
-
-		apply := tracker(defaults, property, dstValue, srcValue)
-		return apply, nil
-	}
-}
-
 // Product variable properties need special handling, the type of the filtered product variable
 // property struct may not be identical between the defaults module and the defaultable module.
 // Use PrependMatchingProperties to apply whichever properties match.
-func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(defaults DefaultsModule,
-	defaultableProp interface{}, tracker defaultsTrackerFunc) error {
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
+	defaults Defaults, defaultableProp interface{}) {
 	if defaultableProp == nil {
-		return nil
+		return
 	}
 
 	defaultsProp := defaults.productVariableProperties()
 	if defaultsProp == nil {
-		return nil
+		return
 	}
 
 	dst := []interface{}{
@@ -573,26 +306,31 @@
 		proptools.CloneEmptyProperties(reflect.ValueOf(defaultsProp)).Interface(),
 	}
 
-	filter := filterForTracker(defaults, tracker)
-
-	return proptools.PrependMatchingProperties(dst, defaultsProp, filter)
+	err := proptools.PrependMatchingProperties(dst, defaultsProp, nil)
+	if err != nil {
+		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+		} else {
+			panic(err)
+		}
+	}
 }
 
-func (defaultable *DefaultableModuleBase) applyDefaultProperties(defaults DefaultsModule,
-	defaultableProp interface{}, checker defaultsTrackerFunc) error {
-
-	filter := filterForTracker(defaults, checker)
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
+	defaults Defaults, defaultableProp interface{}) {
 
 	for _, def := range defaults.properties() {
 		if proptools.TypeEqual(defaultableProp, def) {
-			err := proptools.PrependProperties(defaultableProp, def, filter)
+			err := proptools.PrependProperties(defaultableProp, def, nil)
 			if err != nil {
-				return err
+				if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+					ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+				} else {
+					panic(err)
+				}
 			}
 		}
 	}
-
-	return nil
 }
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
@@ -609,12 +347,12 @@
 func defaultsMutator(ctx TopDownMutatorContext) {
 	if defaultable, ok := ctx.Module().(Defaultable); ok {
 		if len(defaultable.defaults().Defaults) > 0 {
-			var defaultsList []DefaultsModule
+			var defaultsList []Defaults
 			seen := make(map[Defaults]bool)
 
 			ctx.WalkDeps(func(module, parent Module) bool {
 				if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
-					if defaults, ok := module.(DefaultsModule); ok {
+					if defaults, ok := module.(Defaults); ok {
 						if !seen[defaults] {
 							seen[defaults] = true
 							defaultsList = append(defaultsList, defaults)
@@ -630,6 +368,6 @@
 			defaultable.applyDefaults(ctx, defaultsList)
 		}
 
-		defaultable.callHookIfAvailable(ctx)
+		defaultable.CallHookIfAvailable(ctx)
 	}
 }
diff --git a/android/defaults_test.go b/android/defaults_test.go
index d80f40c..a7542ab 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -19,14 +19,7 @@
 )
 
 type defaultsTestProperties struct {
-	Foo    []string
-	Bar    []string
-	Nested struct {
-		Fizz *bool
-	}
-	Other struct {
-		Buzz *string
-	}
+	Foo []string
 }
 
 type defaultsTestModule struct {
@@ -137,167 +130,3 @@
 	// TODO: missing transitive defaults is currently not handled
 	_ = missingTransitiveDefaults
 }
-
-func TestProtectedProperties_ProtectedPropertyNotSet(t *testing.T) {
-	bp := `
-		defaults {
-			name: "transitive",
-			protected_properties: ["foo"],
-		}
-	`
-
-	GroupFixturePreparers(
-		prepareForDefaultsTest,
-		FixtureWithRootAndroidBp(bp),
-	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
-		"module \"transitive\": foo: is not set; protected properties must be explicitly set")).
-		RunTest(t)
-}
-
-func TestProtectedProperties_ProtectedPropertyNotLeaf(t *testing.T) {
-	bp := `
-		defaults {
-			name: "transitive",
-			protected_properties: ["nested"],
-			nested: {
-				fizz: true,
-			},
-		}
-	`
-
-	GroupFixturePreparers(
-		prepareForDefaultsTest,
-		FixtureWithRootAndroidBp(bp),
-	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
-		`\Qmodule "transitive": nested: property is not supported by this module type "defaults"\E`)).
-		RunTest(t)
-}
-
-// TestProtectedProperties_ApplyDefaults makes sure that the protected_properties property has
-// defaults applied.
-func TestProtectedProperties_HasDefaultsApplied(t *testing.T) {
-
-	bp := `
-		defaults {
-			name: "transitive",
-			protected_properties: ["foo"],
-			foo: ["transitive"],
-		}
-
-		defaults {
-			name: "defaults",
-			defaults: ["transitive"],
-			protected_properties: ["bar"],
-			bar: ["defaults"],
-		}
-	`
-
-	result := GroupFixturePreparers(
-		prepareForDefaultsTest,
-		FixtureWithRootAndroidBp(bp),
-	).RunTest(t)
-
-	defaults := result.Module("defaults", "").(DefaultsModule)
-	AssertDeepEquals(t, "defaults protected properties", []string{"foo", "bar"}, defaults.protectedProperties())
-}
-
-// TestProtectedProperties_ProtectAllProperties makes sure that protected_properties: ["*"] protects
-// all properties.
-func TestProtectedProperties_ProtectAllProperties(t *testing.T) {
-
-	bp := `
-		defaults {
-			name: "transitive",
-			protected_properties: ["other.buzz"],
-			other: {
-				buzz: "transitive",
-			},
-		}
-
-		defaults {
-			name: "defaults",
-			defaults: ["transitive"],
-			visibility: ["//visibility:private"],
-			protected_properties: ["*"],
-			foo: ["other"],
-			bar: ["defaults"],
-			nested: {
-				fizz: true,
-			}
-		}
-	`
-
-	result := GroupFixturePreparers(
-		prepareForDefaultsTest,
-		FixtureWithRootAndroidBp(bp),
-	).RunTest(t)
-
-	defaults := result.Module("defaults", "").(DefaultsModule)
-	AssertDeepEquals(t, "defaults protected properties", []string{"other.buzz", "bar", "foo", "nested.fizz"},
-		defaults.protectedProperties())
-}
-
-func TestProtectedProperties_DetectedOverride(t *testing.T) {
-	bp := `
-		defaults {
-			name: "defaults",
-			protected_properties: ["foo", "nested.fizz"],
-			foo: ["defaults"],
-			nested: {
-				fizz: true,
-			},
-		}
-
-		test {
-			name: "foo",
-			defaults: ["defaults"],
-			foo: ["module"],
-			nested: {
-				fizz: false,
-			},
-		}
-	`
-
-	GroupFixturePreparers(
-		prepareForDefaultsTest,
-		FixtureWithRootAndroidBp(bp),
-	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(
-		[]string{
-			`\Qmodule "foo": attempts to append ["module"] to protected property "foo"'s value of ["defaults"] defined in module "defaults"\E`,
-			`\Qmodule "foo": attempts to override protected property "nested.fizz" defined in module "defaults" with a different value (override true with false) so removing the property may necessitate other changes.\E`,
-		})).RunTest(t)
-}
-
-func TestProtectedProperties_DefaultsConflict(t *testing.T) {
-	bp := `
-		defaults {
-			name: "defaults1",
-			protected_properties: ["other.buzz"],
-			other: {
-				buzz: "value",
-			},
-		}
-
-		defaults {
-			name: "defaults2",
-			protected_properties: ["other.buzz"],
-			other: {
-				buzz: "another",
-			},
-		}
-
-		test {
-			name: "foo",
-			defaults: ["defaults1", "defaults2"],
-		}
-	`
-
-	GroupFixturePreparers(
-		prepareForDefaultsTest,
-		FixtureWithRootAndroidBp(bp),
-	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
-		`\Qmodule "foo": has conflicting default values for protected property "other.buzz":
-    defaults module "defaults1" provides value "value"
-    defaults module "defaults2" provides value "another"\E`,
-	)).RunTest(t)
-}
diff --git a/android/defs.go b/android/defs.go
index 6e5bb05..18eed2d 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -58,6 +58,14 @@
 		},
 		"cpFlags", "extraCmds")
 
+	// A copy rule that doesn't preserve symlinks.
+	CpNoPreserveSymlink = pctx.AndroidStaticRule("CpNoPreserveSymlink",
+		blueprint.RuleParams{
+			Command:     "rm -f $out && cp $cpFlags $in $out$extraCmds",
+			Description: "cp $out",
+		},
+		"cpFlags", "extraCmds")
+
 	// A copy rule that only updates the output if it changed.
 	CpIfChanged = pctx.AndroidStaticRule("CpIfChanged",
 		blueprint.RuleParams{
diff --git a/android/filegroup.go b/android/filegroup.go
index d21d146..38de855 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -26,13 +26,18 @@
 )
 
 func init() {
-	RegisterModuleType("filegroup", FileGroupFactory)
+	RegisterFilegroupBuildComponents(InitRegistrationContext)
 }
 
 var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
-	ctx.RegisterModuleType("filegroup", FileGroupFactory)
+	RegisterFilegroupBuildComponents(ctx)
 })
 
+func RegisterFilegroupBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("filegroup", FileGroupFactory)
+	ctx.RegisterModuleType("filegroup_defaults", FileGroupDefaultsFactory)
+}
+
 var convertedProtoLibrarySuffix = "_bp2build_converted"
 
 // IsFilegroup checks that a module is a filegroup type
@@ -78,6 +83,12 @@
 	Strip_import_prefix *string
 }
 
+// api srcs can be contained in filegroups.
+// this should be generated in api_bp2build workspace as well.
+func (fg *fileGroup) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
+	fg.ConvertWithBp2build(ctx)
+}
+
 // ConvertWithBp2build performs bp2build conversion of filegroup
 func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
@@ -107,6 +118,7 @@
 	// If the module has a mixed bag of AIDL and non-AIDL files, split the filegroup manually
 	// and then convert
 	if fg.ShouldConvertToAidlLibrary(ctx) {
+		tags := []string{"apex_available=//apex_available:anyapex"}
 		attrs := &bazelAidlLibraryAttributes{
 			Srcs:                srcs,
 			Strip_import_prefix: fg.properties.Path,
@@ -117,17 +129,25 @@
 			Bzl_load_location: "//build/bazel/rules/aidl:library.bzl",
 		}
 
-		ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
+		ctx.CreateBazelTargetModule(
+			props,
+			CommonAttributes{
+				Name: fg.Name(),
+				Tags: bazel.MakeStringListAttribute(tags),
+			},
+			attrs)
 	} else {
 		if fg.ShouldConvertToProtoLibrary(ctx) {
-			// TODO(b/246997908): we can remove this tag if we could figure out a
-			// solution for this bug.
 			attrs := &ProtoAttrs{
 				Srcs:                srcs,
 				Strip_import_prefix: fg.properties.Path,
 			}
 
-			tags := []string{"manual"}
+			tags := []string{
+				"apex_available=//apex_available:anyapex",
+				// TODO(b/246997908): we can remove this tag if we could figure out a solution for this bug.
+				"manual",
+			}
 			ctx.CreateBazelTargetModule(
 				bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
 				CommonAttributes{
@@ -172,6 +192,7 @@
 type fileGroup struct {
 	ModuleBase
 	BazelModuleBase
+	DefaultableModuleBase
 	FileGroupAsLibrary
 	properties fileGroupProperties
 	srcs       Paths
@@ -189,6 +210,7 @@
 	module.AddProperties(&module.properties)
 	InitAndroidModule(module)
 	InitBazelModule(module)
+	InitDefaultableModule(module)
 	return module
 }
 
@@ -232,7 +254,7 @@
 	bazelCtx.QueueBazelRequest(
 		fg.GetBazelLabel(ctx, fg),
 		cquery.GetOutputFiles,
-		configKey{Common.String(), CommonOS})
+		configKey{arch: Common.String(), osType: CommonOS})
 }
 
 func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
@@ -252,7 +274,7 @@
 		relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path)
 	}
 
-	filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{Common.String(), CommonOS})
+	filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{arch: Common.String(), osType: CommonOS})
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
@@ -320,3 +342,17 @@
 	}
 	return nil, false
 }
+
+// Defaults
+type FileGroupDefaults struct {
+	ModuleBase
+	DefaultsModuleBase
+}
+
+func FileGroupDefaultsFactory() Module {
+	module := &FileGroupDefaults{}
+	module.AddProperties(&fileGroupProperties{})
+	InitDefaultsModule(module)
+
+	return module
+}
diff --git a/android/filegroup_test.go b/android/filegroup_test.go
index 8292d5e..893da57 100644
--- a/android/filegroup_test.go
+++ b/android/filegroup_test.go
@@ -58,3 +58,24 @@
 		AssertStringEquals(t, "src full path", expectedOutputfile, fg.srcs[0].String())
 	}
 }
+
+func TestFilegroupDefaults(t *testing.T) {
+	bp := FixtureAddTextFile("p/Android.bp", `
+		filegroup_defaults {
+			name: "defaults",
+			visibility: ["//x"],
+		}
+		filegroup {
+			name: "foo",
+			defaults: ["defaults"],
+			visibility: ["//y"],
+		}
+	`)
+	result := GroupFixturePreparers(
+		PrepareForTestWithFilegroup,
+		PrepareForTestWithDefaults,
+		PrepareForTestWithVisibility,
+		bp).RunTest(t)
+	rules := effectiveVisibilityRules(result.Config, qualifiedModuleName{pkg: "p", name: "foo"})
+	AssertDeepEquals(t, "visibility", []string{"//x", "//y"}, rules.Strings())
+}
diff --git a/android/fixture.go b/android/fixture.go
index c2b16f6..dbc3bc5 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"runtime"
 	"strings"
 	"testing"
 )
@@ -379,6 +380,12 @@
 	})
 }
 
+var PrepareForSkipTestOnMac = newSimpleFixturePreparer(func(fixture *fixture) {
+	if runtime.GOOS != "linux" {
+		fixture.t.Skip("Test is only supported on linux.")
+	}
+})
+
 // PrepareForDebug_DO_NOT_SUBMIT puts the fixture into debug which will cause it to output its
 // state before running the test.
 //
diff --git a/android/module.go b/android/module.go
index 681f724..76fe8dc 100644
--- a/android/module.go
+++ b/android/module.go
@@ -37,26 +37,64 @@
 	DeviceStaticLibrary = "static_library"
 )
 
+// BuildParameters describes the set of potential parameters to build a Ninja rule.
+// In general, these correspond to a Ninja concept.
 type BuildParams struct {
-	Rule            blueprint.Rule
-	Deps            blueprint.Deps
-	Depfile         WritablePath
-	Description     string
-	Output          WritablePath
-	Outputs         WritablePaths
-	SymlinkOutput   WritablePath
-	SymlinkOutputs  WritablePaths
-	ImplicitOutput  WritablePath
+	// A Ninja Rule that will be written to the Ninja file. This allows factoring out common code
+	// among multiple modules to reduce repetition in the Ninja file of action requirements. A rule
+	// can contain variables that should be provided in Args.
+	Rule blueprint.Rule
+	// Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles
+	// are used.
+	Deps blueprint.Deps
+	// Depfile is a writeable path that allows correct incremental builds when the inputs have not
+	// been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax.
+	Depfile WritablePath
+	// A description of the build action.
+	Description string
+	// Output is an output file of the action. When using this field, references to $out in the Ninja
+	// command will refer to this file.
+	Output WritablePath
+	// Outputs is a slice of output file of the action. When using this field, references to $out in
+	// the Ninja command will refer to these files.
+	Outputs WritablePaths
+	// SymlinkOutput is an output file specifically that is a symlink.
+	SymlinkOutput WritablePath
+	// SymlinkOutputs is a slice of output files specifically that is a symlink.
+	SymlinkOutputs WritablePaths
+	// ImplicitOutput is an output file generated by the action. Note: references to `$out` in the
+	// Ninja command will NOT include references to this file.
+	ImplicitOutput WritablePath
+	// ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out`
+	// in the Ninja command will NOT include references to these files.
 	ImplicitOutputs WritablePaths
-	Input           Path
-	Inputs          Paths
-	Implicit        Path
-	Implicits       Paths
-	OrderOnly       Paths
-	Validation      Path
-	Validations     Paths
-	Default         bool
-	Args            map[string]string
+	// Input is an input file to the Ninja action. When using this field, references to $in in the
+	// Ninja command will refer to this file.
+	Input Path
+	// Inputs is a slice of input files to the Ninja action. When using this field, references to $in
+	// in the Ninja command will refer to these files.
+	Inputs Paths
+	// Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command
+	// will NOT include references to this file.
+	Implicit Path
+	// Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja
+	// command will NOT include references to these files.
+	Implicits Paths
+	// OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is
+	// not rebuilt until they are built, but changes in order-only dependencies alone do not cause the
+	// output to be rebuilt.
+	OrderOnly Paths
+	// Validation is an output path for a validation action. Validation outputs imply lower
+	// non-blocking priority to building non-validation outputs.
+	Validation Path
+	// Validations is a slice of output path for a validation action. Validation outputs imply lower
+	// non-blocking priority to building non-validation outputs.
+	Validations Paths
+	// Whether to skip outputting a default target statement which will be built by Ninja when no
+	// targets are specified on Ninja's command line.
+	Default bool
+	// Args is a key value mapping for replacements of variables within the Rule
+	Args map[string]string
 }
 
 type ModuleBuildParams BuildParams
@@ -502,6 +540,7 @@
 	InstallInRoot() bool
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
+	PartitionTag(DeviceConfig) string
 	HideFromMake()
 	IsHideFromMake() bool
 	IsSkipInstall() bool
@@ -1333,7 +1372,7 @@
 // Check product variables for `enabled: true` flag override.
 // Returns a list of the constraint_value targets who enable this override.
 func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label {
-	productVariableProps := ProductVariableProperties(ctx)
+	productVariableProps := ProductVariableProperties(ctx, ctx.Module())
 	productConfigEnablingTargets := []bazel.Label{}
 	const propName = "Enabled"
 	if productConfigProps, exists := productVariableProps[propName]; exists {
@@ -3674,7 +3713,7 @@
 	// Ensure ancestor directories are in dirMap
 	// Make directories build their direct subdirectories
 	// Returns a slice of all directories and a slice of top-level directories.
-	dirs := SortedStringKeys(dirMap)
+	dirs := SortedKeys(dirMap)
 	for _, dir := range dirs {
 		dir := parentDir(dir)
 		for dir != "." && dir != "/" {
@@ -3685,7 +3724,7 @@
 			dir = parentDir(dir)
 		}
 	}
-	dirs = SortedStringKeys(dirMap)
+	dirs = SortedKeys(dirMap)
 	var topDirs []string
 	for _, dir := range dirs {
 		p := parentDir(dir)
@@ -3695,7 +3734,7 @@
 			topDirs = append(topDirs, dir)
 		}
 	}
-	return SortedStringKeys(dirMap), topDirs
+	return SortedKeys(dirMap), topDirs
 }
 
 func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
@@ -3781,7 +3820,7 @@
 	}
 
 	// Wrap those into host|host-cross|target phony rules
-	for _, class := range SortedStringKeys(osClass) {
+	for _, class := range SortedKeys(osClass) {
 		ctx.Phony(class, osClass[class]...)
 	}
 }
diff --git a/android/mutator.go b/android/mutator.go
index 4e55609..676f8a5 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -268,6 +268,11 @@
 	// platforms, as dictated by a given bool attribute: the target will not be buildable in
 	// any platform for which this bool attribute is false.
 	CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}, bazel.BoolAttribute)
+
+	// CreateBazelTargetAliasInDir creates an alias definition in `dir` directory.
+	// This function can be used to create alias definitions in a directory that is different
+	// from the directory of the visited Soong module.
+	CreateBazelTargetAliasInDir(dir string, name string, actual bazel.Label)
 }
 
 type topDownMutatorContext struct {
@@ -705,28 +710,61 @@
 	t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty)
 }
 
+var (
+	bazelAliasModuleProperties = bazel.BazelTargetModuleProperties{
+		Rule_class: "alias",
+	}
+)
+
+type bazelAliasAttributes struct {
+	Actual *bazel.LabelAttribute
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetAliasInDir(
+	dir string,
+	name string,
+	actual bazel.Label) {
+	mod := t.Module()
+	attrs := &bazelAliasAttributes{
+		Actual: bazel.MakeLabelAttribute(actual.Label),
+	}
+	info := bp2buildInfo{
+		Dir:             dir,
+		BazelProps:      bazelAliasModuleProperties,
+		CommonAttrs:     CommonAttributes{Name: name},
+		ConstraintAttrs: constraintAttributes{},
+		Attrs:           attrs,
+	}
+	mod.base().addBp2buildInfo(info)
+}
+
 // ApexAvailableTags converts the apex_available property value of an ApexModule
 // module and returns it as a list of keyed tags.
 func ApexAvailableTags(mod Module) bazel.StringListAttribute {
 	attr := bazel.StringListAttribute{}
-	tags := []string{}
 	// Transform specific attributes into tags.
 	if am, ok := mod.(ApexModule); ok {
 		// TODO(b/218841706): hidl_interface has the apex_available prop, but it's
 		// defined directly as a prop and not via ApexModule, so this doesn't
 		// pick those props up.
-		// TODO(b/260694842): This does not pick up aidl_interface.backend.ndk.apex_available.
-		for _, a := range am.apexModuleBase().ApexAvailable() {
-			tags = append(tags, "apex_available="+a)
-		}
-	}
-	if len(tags) > 0 {
-		// This avoids creating a tags attr with an empty list if there are no tags.
-		attr.Value = tags
+		attr.Value = ConvertApexAvailableToTags(am.apexModuleBase().ApexAvailable())
 	}
 	return attr
 }
 
+func ConvertApexAvailableToTags(apexAvailable []string) []string {
+	if len(apexAvailable) == 0 {
+		// We need nil specifically to make bp2build not add the tags property at all,
+		// instead of adding it with an empty list
+		return nil
+	}
+	result := make([]string, 0, len(apexAvailable))
+	for _, a := range apexAvailable {
+		result = append(result, "apex_available="+a)
+	}
+	return result
+}
+
 func (t *topDownMutatorContext) createBazelTargetModule(
 	bazelProps bazel.BazelTargetModuleProperties,
 	commonAttrs CommonAttributes,
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 21eebd2..dbdfa33 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 	"strings"
 	"testing"
 
@@ -267,3 +268,22 @@
 		FixtureWithRootAndroidBp(`test {name: "foo"}`),
 	).RunTest(t)
 }
+
+func TestConvertApexAvailableToTags(t *testing.T) {
+	input := []string{
+		"com.android.adbd",
+		"//apex_available:platform",
+	}
+	actual := ConvertApexAvailableToTags(input)
+	expected := []string{
+		"apex_available=com.android.adbd",
+		"apex_available=//apex_available:platform",
+	}
+	if !reflect.DeepEqual(actual, expected) {
+		t.Errorf("Expected: %v, actual: %v", expected, actual)
+	}
+
+	if ConvertApexAvailableToTags(nil) != nil {
+		t.Errorf("Expected providing nil to return nil")
+	}
+}
diff --git a/android/neverallow.go b/android/neverallow.go
index ad9880a..2139c3c 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -165,6 +165,7 @@
 	javaDeviceForHostProjectsAllowedList := []string{
 		"development/build",
 		"external/guava",
+		"external/kotlinx.coroutines",
 		"external/robolectric-shadows",
 		"external/robolectric",
 		"frameworks/layoutlib",
@@ -542,7 +543,7 @@
 		s = append(s, fmt.Sprintf("properties matching: %s", r.props))
 	}
 	if len(r.directDeps) > 0 {
-		s = append(s, fmt.Sprintf("dep(s): %q", SortedStringKeys(r.directDeps)))
+		s = append(s, fmt.Sprintf("dep(s): %q", SortedKeys(r.directDeps)))
 	}
 	if len(r.osClasses) > 0 {
 		s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
diff --git a/android/packaging.go b/android/packaging.go
index ecd84a2..4a9b591 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -240,7 +240,7 @@
 // entries into the specified directory.
 func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
 	seenDir := make(map[string]bool)
-	for _, k := range SortedStringKeys(specs) {
+	for _, k := range SortedKeys(specs) {
 		ps := specs[k]
 		destPath := dir.Join(ctx, ps.relPathInPackage).String()
 		destDir := filepath.Dir(destPath)
diff --git a/android/paths.go b/android/paths.go
index 0fc39df..eaa6a8d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -1710,10 +1709,10 @@
 func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, debug bool,
 	pathComponents ...string) InstallPath {
 
-	var partionPaths []string
+	var partitionPaths []string
 
 	if os.Class == Device {
-		partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
+		partitionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
 	} else {
 		osName := os.String()
 		if os == Linux {
@@ -1735,21 +1734,21 @@
 		if os.Class == Host && (arch == X86_64 || arch == Common) {
 			archName = "x86"
 		}
-		partionPaths = []string{"host", osName + "-" + archName, partition}
+		partitionPaths = []string{"host", osName + "-" + archName, partition}
 	}
 	if debug {
-		partionPaths = append([]string{"debug"}, partionPaths...)
+		partitionPaths = append([]string{"debug"}, partitionPaths...)
 	}
 
-	partionPath, err := validatePath(partionPaths...)
+	partitionPath, err := validatePath(partitionPaths...)
 	if err != nil {
 		reportPathError(ctx, err)
 	}
 
 	base := InstallPath{
-		basePath:     basePath{partionPath, ""},
+		basePath:     basePath{partitionPath, ""},
 		soongOutDir:  ctx.Config().soongOutDir,
-		partitionDir: partionPath,
+		partitionDir: partitionPath,
 		partition:    partition,
 	}
 
@@ -1868,10 +1867,14 @@
 	return ret
 }
 
-// validateSafePath validates a path that we trust (may contain ninja variables).
-// Ensures that each path component does not attempt to leave its component.
-func validateSafePath(pathComponents ...string) (string, error) {
+// validatePathInternal ensures that a path does not leave its component, and
+// optionally doesn't contain Ninja variables.
+func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (string, error) {
 	for _, path := range pathComponents {
+		if !allowNinjaVariables && strings.Contains(path, "$") {
+			return "", fmt.Errorf("Path contains invalid character($): %s", path)
+		}
+
 		path := filepath.Clean(path)
 		if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
 			return "", fmt.Errorf("Path is outside directory: %s", path)
@@ -1883,16 +1886,18 @@
 	return filepath.Join(pathComponents...), nil
 }
 
+// validateSafePath validates a path that we trust (may contain ninja
+// variables).  Ensures that each path component does not attempt to leave its
+// component. Returns a joined version of each path component.
+func validateSafePath(pathComponents ...string) (string, error) {
+	return validatePathInternal(true, pathComponents...)
+}
+
 // validatePath validates that a path does not include ninja variables, and that
 // each path component does not attempt to leave its component. Returns a joined
 // version of each path component.
 func validatePath(pathComponents ...string) (string, error) {
-	for _, path := range pathComponents {
-		if strings.Contains(path, "$") {
-			return "", fmt.Errorf("Path contains invalid character($): %s", path)
-		}
-	}
-	return validateSafePath(pathComponents...)
+	return validatePathInternal(false, pathComponents...)
 }
 
 func PathForPhony(ctx PathContext, phony string) WritablePath {
@@ -2093,13 +2098,16 @@
 
 // Writes a file to the output directory.  Attempting to write directly to the output directory
 // will fail due to the sandbox of the soong_build process.
+// Only writes the file if the file doesn't exist or if it has different contents, to prevent
+// updating the timestamp if no changes would be made. (This is better for incremental
+// performance.)
 func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
 	absPath := absolutePath(path.String())
 	err := os.MkdirAll(filepath.Dir(absPath), 0777)
 	if err != nil {
 		return err
 	}
-	return ioutil.WriteFile(absPath, data, perm)
+	return pathtools.WriteFileIfChanged(absPath, data, perm)
 }
 
 func RemoveAllOutputDir(path WritablePath) error {
diff --git a/android/phony.go b/android/phony.go
index 0adbb55..814a9e3 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -48,7 +48,7 @@
 
 func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
 	p.phonyMap = getPhonyMap(ctx.Config())
-	p.phonyList = SortedStringKeys(p.phonyMap)
+	p.phonyList = SortedKeys(p.phonyMap)
 	for _, phony := range p.phonyList {
 		p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
 	}
diff --git a/android/sdk.go b/android/sdk.go
index 8b23d63..63e0bbe 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -25,7 +25,7 @@
 
 // minApiLevelForSdkSnapshot provides access to the min_sdk_version for MinApiLevelForSdkSnapshot
 type minApiLevelForSdkSnapshot interface {
-	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+	MinSdkVersion(ctx EarlyModuleContext) ApiLevel
 }
 
 // MinApiLevelForSdkSnapshot returns the ApiLevel of the min_sdk_version of the supplied module.
@@ -34,7 +34,7 @@
 func MinApiLevelForSdkSnapshot(ctx EarlyModuleContext, module Module) ApiLevel {
 	minApiLevel := NoneApiLevel
 	if m, ok := module.(minApiLevelForSdkSnapshot); ok {
-		minApiLevel = m.MinSdkVersion(ctx).ApiLevel
+		minApiLevel = m.MinSdkVersion(ctx)
 	}
 	if minApiLevel == NoneApiLevel {
 		// The default min API level is 1.
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 8953eae..7ace638 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -25,15 +25,15 @@
 	SdkVersion(ctx EarlyModuleContext) SdkSpec
 	// SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
 	SystemModules() string
-	// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
+	// MinSdkVersion returns ApiLevel that corresponds to the min_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
-	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+	MinSdkVersion(ctx EarlyModuleContext) ApiLevel
 	// ReplaceMaxSdkVersionPlaceholder returns SdkSpec to replace the maxSdkVersion property of permission and
 	// uses-permission tags if it is set.
 	ReplaceMaxSdkVersionPlaceholder(ctx EarlyModuleContext) SdkSpec
-	// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
+	// TargetSdkVersion returns the ApiLevel that corresponds to the target_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
-	TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
+	TargetSdkVersion(ctx EarlyModuleContext) ApiLevel
 }
 
 // SdkKind represents a particular category of an SDK spec like public, system, test, etc.
@@ -84,6 +84,40 @@
 	}
 }
 
+// JavaLibraryName returns the soong module containing the Java APIs of that API surface.
+func (k SdkKind) JavaLibraryName(c Config) string {
+	name := k.defaultJavaLibraryName()
+	return JavaLibraryNameFromText(c, name)
+}
+
+// JavaLibraryNameFromText returns the name of .txt equivalent of a java_library, but does
+// not check if either module exists.
+// TODO: Return .txt (single-tree or multi-tree equivalents) based on config
+func JavaLibraryNameFromText(c Config, name string) string {
+	// This returns the default for now.
+	// TODO: Implement this
+	return name
+}
+
+func (k SdkKind) defaultJavaLibraryName() string {
+	switch k {
+	case SdkPublic:
+		return "android_stubs_current"
+	case SdkSystem:
+		return "android_system_stubs_current"
+	case SdkTest:
+		return "android_test_stubs_current"
+	case SdkCore:
+		return "core.current.stubs"
+	case SdkModule:
+		return "android_module_lib_stubs_current"
+	case SdkSystemServer:
+		return "android_system_server_stubs_current"
+	default:
+		panic(fmt.Errorf("APIs of API surface %v cannot be provided by a single Soong module\n", k))
+	}
+}
+
 // SdkSpec represents the kind and the version of an SDK for a module to build against
 type SdkSpec struct {
 	Kind     SdkKind
@@ -187,14 +221,7 @@
 	if ctx.DeviceSpecific() || ctx.SocSpecific() {
 		s = s.ForVendorPartition(ctx)
 	}
-	if !s.ApiLevel.IsPreview() {
-		return s.ApiLevel, nil
-	}
-	ret := ctx.Config().DefaultAppTargetSdk(ctx)
-	if ret.IsPreview() {
-		return FutureApiLevel, nil
-	}
-	return ret, nil
+	return s.ApiLevel.EffectiveVersion(ctx)
 }
 
 // EffectiveVersionString converts an SdkSpec into the concrete version string that the module
@@ -208,15 +235,12 @@
 	if ctx.DeviceSpecific() || ctx.SocSpecific() {
 		s = s.ForVendorPartition(ctx)
 	}
-	if !s.ApiLevel.IsPreview() {
-		return s.ApiLevel.String(), nil
-	}
-	return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
+	return s.ApiLevel.EffectiveVersionString(ctx)
 }
 
 var (
 	SdkSpecNone         = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
-	SdkSpecPrivate      = SdkSpec{SdkPrivate, FutureApiLevel, ""}
+	SdkSpecPrivate      = SdkSpec{SdkPrivate, PrivateApiLevel, ""}
 	SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, FutureApiLevel, "core_platform"}
 )
 
@@ -239,7 +263,7 @@
 
 		var kindString string
 		if sep == 0 {
-			return SdkSpec{SdkInvalid, NoneApiLevel, str}
+			return SdkSpec{SdkInvalid, NewInvalidApiLevel(str), str}
 		} else if sep == -1 {
 			kindString = ""
 		} else {
@@ -267,7 +291,7 @@
 
 		apiLevel, err := ApiLevelFromUserWithConfig(config, versionString)
 		if err != nil {
-			return SdkSpec{SdkInvalid, apiLevel, str}
+			return SdkSpec{SdkInvalid, NewInvalidApiLevel(versionString), str}
 		}
 		return SdkSpec{kind, apiLevel, str}
 	}
@@ -294,3 +318,18 @@
 	}
 	return true
 }
+
+func init() {
+	RegisterMakeVarsProvider(pctx, javaSdkMakeVars)
+}
+
+// Export the name of the soong modules representing the various Java API surfaces.
+func javaSdkMakeVars(ctx MakeVarsContext) {
+	ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.JavaLibraryName(ctx.Config()))
+	ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.JavaLibraryName(ctx.Config()))
+	ctx.Strict("ANDROID_TEST_STUBS", SdkTest.JavaLibraryName(ctx.Config()))
+	ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.JavaLibraryName(ctx.Config()))
+	ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.JavaLibraryName(ctx.Config()))
+	// TODO (jihoonkang): Create a .txt equivalent for core.current.stubs
+	ctx.Strict("ANDROID_CORE_STUBS", SdkCore.JavaLibraryName(ctx.Config()))
+}
diff --git a/android/sdk_version_test.go b/android/sdk_version_test.go
index ec81782..ea99c4d 100644
--- a/android/sdk_version_test.go
+++ b/android/sdk_version_test.go
@@ -37,11 +37,11 @@
 		},
 		{
 			input:    "_",
-			expected: "invalid_(no version)",
+			expected: "invalid__",
 		},
 		{
 			input:    "_31",
-			expected: "invalid_(no version)",
+			expected: "invalid__31",
 		},
 		{
 			input:    "system_R",
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 9f5440d..5fa6012 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -396,7 +396,7 @@
 		}
 
 		if ctx.Config().BuildMode == Bp2build {
-			ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
+			ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(mtDef)
 		}
 
 		globalModuleTypes := ctx.moduleFactories()
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index ed4888d..23c8afa 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -240,12 +240,7 @@
 // string vars, bool vars and value vars created by every
 // soong_config_module_type in this build.
 type Bp2BuildSoongConfigDefinitions struct {
-	// varCache contains a cache of string variables namespace + property
-	// The same variable may be used in multiple module types (for example, if need support
-	// for cc_default and java_default), only need to process once
-	varCache map[string]bool
-
-	StringVars map[string][]string
+	StringVars map[string]map[string]bool
 	BoolVars   map[string]bool
 	ValueVars  map[string]bool
 }
@@ -255,7 +250,7 @@
 // SoongConfigVariablesForBp2build extracts information from a
 // SoongConfigDefinition that bp2build needs to generate constraint settings and
 // values for, in order to migrate soong_config_module_type usages to Bazel.
-func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition) {
+func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef *SoongConfigDefinition) {
 	// In bp2build mode, this method is called concurrently in goroutines from
 	// loadhooks while parsing soong_config_module_type, so add a mutex to
 	// prevent concurrent map writes. See b/207572723
@@ -263,7 +258,7 @@
 	defer bp2buildSoongConfigVarsLock.Unlock()
 
 	if defs.StringVars == nil {
-		defs.StringVars = make(map[string][]string)
+		defs.StringVars = make(map[string]map[string]bool)
 	}
 	if defs.BoolVars == nil {
 		defs.BoolVars = make(map[string]bool)
@@ -271,24 +266,29 @@
 	if defs.ValueVars == nil {
 		defs.ValueVars = make(map[string]bool)
 	}
-	if defs.varCache == nil {
-		defs.varCache = make(map[string]bool)
-	}
+	// varCache contains a cache of string variables namespace + property
+	// The same variable may be used in multiple module types (for example, if need support
+	// for cc_default and java_default), only need to process once
+	varCache := map[string]bool{}
+
 	for _, moduleType := range mtDef.ModuleTypes {
 		for _, v := range moduleType.Variables {
 			key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
 
 			// The same variable may be used in multiple module types (for example, if need support
 			// for cc_default and java_default), only need to process once
-			if _, keyInCache := defs.varCache[key]; keyInCache {
+			if _, keyInCache := varCache[key]; keyInCache {
 				continue
 			} else {
-				defs.varCache[key] = true
+				varCache[key] = true
 			}
 
 			if strVar, ok := v.(*stringVariable); ok {
+				if _, ok := defs.StringVars[key]; !ok {
+					defs.StringVars[key] = make(map[string]bool, len(strVar.values))
+				}
 				for _, value := range strVar.values {
-					defs.StringVars[key] = append(defs.StringVars[key], value)
+					defs.StringVars[key][value] = true
 				}
 			} else if _, ok := v.(*boolVariable); ok {
 				defs.BoolVars[key] = true
@@ -329,8 +329,13 @@
 	ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
 	ret += "\n\n"
 
+	stringVars := make(map[string][]string, len(defs.StringVars))
+	for k, v := range defs.StringVars {
+		stringVars[k] = sortedStringKeys(v)
+	}
+
 	ret += "soong_config_string_variables = "
-	ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
+	ret += starlark_fmt.PrintStringListDict(stringVars, 0)
 
 	return ret
 }
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index d5d87ef..a5fa349 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -414,6 +414,110 @@
 	}
 }
 
+func Test_Bp2BuildSoongConfigDefinitionsAddVars(t *testing.T) {
+	testCases := []struct {
+		desc     string
+		defs     []*SoongConfigDefinition
+		expected Bp2BuildSoongConfigDefinitions
+	}{
+		{
+			desc: "non-overlapping",
+			defs: []*SoongConfigDefinition{
+				&SoongConfigDefinition{
+					ModuleTypes: map[string]*ModuleType{
+						"a": &ModuleType{
+							ConfigNamespace: "foo",
+							Variables: []soongConfigVariable{
+								&stringVariable{
+									baseVariable: baseVariable{"string_var"},
+									values:       []string{"a", "b", "c"},
+								},
+							},
+						},
+					},
+				},
+				&SoongConfigDefinition{
+					ModuleTypes: map[string]*ModuleType{
+						"b": &ModuleType{
+							ConfigNamespace: "foo",
+							Variables: []soongConfigVariable{
+								&stringVariable{
+									baseVariable: baseVariable{"string_var"},
+									values:       []string{"a", "b", "c"},
+								},
+								&boolVariable{baseVariable: baseVariable{"bool_var"}},
+								&valueVariable{baseVariable: baseVariable{"variable_var"}},
+							},
+						},
+					},
+				},
+			},
+			expected: Bp2BuildSoongConfigDefinitions{
+				StringVars: map[string]map[string]bool{
+					"foo__string_var": map[string]bool{"a": true, "b": true, "c": true},
+				},
+				BoolVars:  map[string]bool{"foo__bool_var": true},
+				ValueVars: map[string]bool{"foo__variable_var": true},
+			},
+		},
+		{
+			desc: "overlapping",
+			defs: []*SoongConfigDefinition{
+				&SoongConfigDefinition{
+					ModuleTypes: map[string]*ModuleType{
+						"a": &ModuleType{
+							ConfigNamespace: "foo",
+							Variables: []soongConfigVariable{
+								&stringVariable{
+									baseVariable: baseVariable{"string_var"},
+									values:       []string{"a", "b", "c"},
+								},
+							},
+						},
+					},
+				},
+				&SoongConfigDefinition{
+					ModuleTypes: map[string]*ModuleType{
+						"b": &ModuleType{
+							ConfigNamespace: "foo",
+							Variables: []soongConfigVariable{
+								&stringVariable{
+									baseVariable: baseVariable{"string_var"},
+									values:       []string{"b", "c", "d"},
+								},
+								&boolVariable{baseVariable: baseVariable{"bool_var"}},
+								&valueVariable{baseVariable: baseVariable{"variable_var"}},
+							},
+						},
+					},
+				},
+			},
+			expected: Bp2BuildSoongConfigDefinitions{
+				StringVars: map[string]map[string]bool{
+					"foo__string_var": map[string]bool{"a": true, "b": true, "c": true, "d": true},
+				},
+				BoolVars:  map[string]bool{"foo__bool_var": true},
+				ValueVars: map[string]bool{"foo__variable_var": true},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			actual := &Bp2BuildSoongConfigDefinitions{}
+			for _, d := range tc.defs {
+				func(def *SoongConfigDefinition) {
+					actual.AddVars(def)
+				}(d)
+			}
+			if !reflect.DeepEqual(*actual, tc.expected) {
+				t.Errorf("Expected %#v, got %#v", tc.expected, *actual)
+			}
+		})
+	}
+
+}
+
 func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
 	testCases := []struct {
 		desc     string
@@ -456,11 +560,11 @@
 soong_config_string_variables = {}`}, {
 			desc: "only string vars",
 			defs: Bp2BuildSoongConfigDefinitions{
-				StringVars: map[string][]string{
-					"string_var": []string{
-						"choice1",
-						"choice2",
-						"choice3",
+				StringVars: map[string]map[string]bool{
+					"string_var": map[string]bool{
+						"choice1": true,
+						"choice2": true,
+						"choice3": true,
 					},
 				},
 			},
@@ -484,15 +588,15 @@
 					"value_var_one": true,
 					"value_var_two": true,
 				},
-				StringVars: map[string][]string{
-					"string_var_one": []string{
-						"choice1",
-						"choice2",
-						"choice3",
+				StringVars: map[string]map[string]bool{
+					"string_var_one": map[string]bool{
+						"choice1": true,
+						"choice2": true,
+						"choice3": true,
 					},
-					"string_var_two": []string{
-						"foo",
-						"bar",
+					"string_var_two": map[string]bool{
+						"foo": true,
+						"bar": true,
 					},
 				},
 			},
@@ -512,8 +616,8 @@
         "choice3",
     ],
     "string_var_two": [
-        "foo",
         "bar",
+        "foo",
     ],
 }`},
 	}
diff --git a/android/test_asserts.go b/android/test_asserts.go
index 064f656..4143f15 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -160,6 +160,7 @@
 // the value of the expected bool. If the expectation does not hold it reports an error prefixed with
 // the supplied message and including a reason for why it failed.
 func AssertStringListContainsEquals(t *testing.T, message string, list []string, s string, expected bool) {
+	t.Helper()
 	if expected {
 		AssertStringListContains(t, message, list, s)
 	} else {
diff --git a/android/test_config.go b/android/test_config.go
index 70c319a..07ca33d 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -109,7 +109,8 @@
 	config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
 }
 
-func modifyTestConfigForMusl(config Config) {
+// ModifyTestConfigForMusl takes a Config returned by TestConfig and changes the host targets from glibc to musl.
+func ModifyTestConfigForMusl(config Config) {
 	delete(config.Targets, config.BuildOS)
 	config.productVariables.HostMusl = boolPtr(true)
 	determineBuildOS(config.config)
diff --git a/android/test_suites.go b/android/test_suites.go
index 55e1da7..b570b23 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -57,7 +57,7 @@
 
 func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
 	var installedPaths InstallPaths
-	for _, module := range SortedStringKeys(files) {
+	for _, module := range SortedKeys(files) {
 		installedPaths = append(installedPaths, files[module]...)
 	}
 	testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false)
diff --git a/android/testing.go b/android/testing.go
index 29af71f..fc39a9c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1125,6 +1125,11 @@
 	config.katiEnabled = true
 }
 
+func SetTrimmedApexEnabledForTests(config Config) {
+	config.productVariables.TrimmedApex = new(bool)
+	*config.productVariables.TrimmedApex = true
+}
+
 func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
 	t.Helper()
 	var p AndroidMkEntriesProvider
@@ -1145,7 +1150,7 @@
 	var p AndroidMkDataProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkDataProvider); !ok {
-		t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+		t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name())
 	}
 	data := p.AndroidMk()
 	data.fillInData(ctx, mod)
diff --git a/android/util.go b/android/util.go
index a0f7160..38e0a4d 100644
--- a/android/util.go
+++ b/android/util.go
@@ -29,6 +29,15 @@
 	return append([]string(nil), s...)
 }
 
+// Concat returns a new slice concatenated from the two input slices. It does not change the input
+// slices.
+func Concat[T any](s1, s2 []T) []T {
+	res := make([]T, 0, len(s1)+len(s2))
+	res = append(res, s1...)
+	res = append(res, s2...)
+	return res
+}
+
 // JoinWithPrefix prepends the prefix to each string in the list and
 // returns them joined together with " " as separator.
 func JoinWithPrefix(strs []string, prefix string) string {
@@ -53,40 +62,33 @@
 	return buf.String()
 }
 
-// JoinWithSuffix appends the suffix to each string in the list and
-// returns them joined together with given separator.
-func JoinWithSuffix(strs []string, suffix string, separator string) string {
-	if len(strs) == 0 {
-		return ""
-	}
-
-	var buf strings.Builder
-	buf.WriteString(strs[0])
-	buf.WriteString(suffix)
-	for i := 1; i < len(strs); i++ {
-		buf.WriteString(separator)
-		buf.WriteString(strs[i])
-		buf.WriteString(suffix)
-	}
-	return buf.String()
+// SortedStringKeys returns the keys of the given map in the ascending order.
+//
+// Deprecated: Use SortedKeys instead.
+func SortedStringKeys[V any](m map[string]V) []string {
+	return SortedKeys(m)
 }
 
-// SorterStringKeys returns the keys of the given string-keyed map in the ascending order.
-func SortedStringKeys(m interface{}) []string {
-	v := reflect.ValueOf(m)
-	if v.Kind() != reflect.Map {
-		panic(fmt.Sprintf("%#v is not a map", m))
-	}
-	if v.Len() == 0 {
+type Ordered interface {
+	~string |
+		~float32 | ~float64 |
+		~int | ~int8 | ~int16 | ~int32 | ~int64 |
+		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
+}
+
+// SortedKeys returns the keys of the given map in the ascending order.
+func SortedKeys[T Ordered, V any](m map[T]V) []T {
+	if len(m) == 0 {
 		return nil
 	}
-	iter := v.MapRange()
-	s := make([]string, 0, v.Len())
-	for iter.Next() {
-		s = append(s, iter.Key().String())
+	ret := make([]T, 0, len(m))
+	for k := range m {
+		ret = append(ret, k)
 	}
-	sort.Strings(s)
-	return s
+	sort.Slice(ret, func(i, j int) bool {
+		return ret[i] < ret[j]
+	})
+	return ret
 }
 
 // stringValues returns the values of the given string-valued map in randomized map order.
@@ -136,6 +138,38 @@
 	return IndexList(s, list) != -1
 }
 
+func setFromList[T comparable](l []T) map[T]bool {
+	m := make(map[T]bool, len(l))
+	for _, t := range l {
+		m[t] = true
+	}
+	return m
+}
+
+// ListSetDifference checks if the two lists contain the same elements. It returns
+// a boolean which is true if there is a difference, and then returns lists of elements
+// that are in l1 but not l2, and l2 but not l1.
+func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
+	listsDiffer := false
+	diff1 := []T{}
+	diff2 := []T{}
+	m1 := setFromList(l1)
+	m2 := setFromList(l2)
+	for t := range m1 {
+		if _, ok := m2[t]; !ok {
+			diff1 = append(diff1, t)
+			listsDiffer = true
+		}
+	}
+	for t := range m2 {
+		if _, ok := m1[t]; !ok {
+			diff2 = append(diff2, t)
+			listsDiffer = true
+		}
+	}
+	return listsDiffer, diff1, diff2
+}
+
 // Returns true if the given string s is prefixed with any string in the given prefix list.
 func HasAnyPrefix(s string, prefixList []string) bool {
 	for _, prefix := range prefixList {
diff --git a/android/util_test.go b/android/util_test.go
index 9b9253b..5584b38 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -641,42 +641,34 @@
 	}
 }
 
-func TestSortedStringKeys(t *testing.T) {
-	testCases := []struct {
-		name     string
-		in       interface{}
-		expected []string
-	}{
-		{
-			name:     "nil",
-			in:       map[string]string(nil),
-			expected: nil,
-		},
-		{
-			name:     "empty",
-			in:       map[string]string{},
-			expected: nil,
-		},
-		{
-			name:     "simple",
-			in:       map[string]string{"a": "foo", "b": "bar"},
-			expected: []string{"a", "b"},
-		},
-		{
-			name:     "interface values",
-			in:       map[string]interface{}{"a": nil, "b": nil},
-			expected: []string{"a", "b"},
-		},
-	}
+func testSortedKeysHelper[K Ordered, V any](t *testing.T, name string, input map[K]V, expected []K) {
+	t.Helper()
+	t.Run(name, func(t *testing.T) {
+		actual := SortedKeys(input)
+		if !reflect.DeepEqual(actual, expected) {
+			t.Errorf("expected %v, got %v", expected, actual)
+		}
+	})
+}
 
-	for _, tt := range testCases {
-		t.Run(tt.name, func(t *testing.T) {
-			got := SortedStringKeys(tt.in)
-			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
-				t.Errorf("wanted %q, got %q", w, g)
-			}
-		})
-	}
+func TestSortedKeys(t *testing.T) {
+	testSortedKeysHelper(t, "simple", map[string]string{
+		"b": "bar",
+		"a": "foo",
+	}, []string{
+		"a",
+		"b",
+	})
+	testSortedKeysHelper(t, "ints", map[int]interface{}{
+		10: nil,
+		5:  nil,
+	}, []int{
+		5,
+		10,
+	})
+
+	testSortedKeysHelper(t, "nil", map[string]string(nil), nil)
+	testSortedKeysHelper(t, "empty", map[string]string{}, nil)
 }
 
 func TestSortedStringValues(t *testing.T) {
diff --git a/android/variable.go b/android/variable.go
index e838b7c..8c5c0bc 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -64,6 +64,12 @@
 			Enabled *bool `android:"arch_variant"`
 		} `android:"arch_variant"`
 
+		// similar to `Unbundled_build`, but `Always_use_prebuilt_sdks` means that it uses prebuilt
+		// sdk specifically.
+		Always_use_prebuilt_sdks struct {
+			Enabled *bool `android:"arch_variant"`
+		} `android:"arch_variant"`
+
 		Malloc_not_svelte struct {
 			Cflags              []string `android:"arch_variant"`
 			Shared_libs         []string `android:"arch_variant"`
@@ -380,6 +386,7 @@
 
 	Ndk_abis *bool `json:",omitempty"`
 
+	TrimmedApex                  *bool `json:",omitempty"`
 	Flatten_apex                 *bool `json:",omitempty"`
 	ForceApexSymlinkOptimization *bool `json:",omitempty"`
 	CompressedApex               *bool `json:",omitempty"`
@@ -435,6 +442,7 @@
 	BuildBrokenDepfile                 *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
+	BuildBrokenUsesSoongPython2Modules bool     `json:",omitempty"`
 	BuildBrokenVendorPropertyNamespace bool     `json:",omitempty"`
 	BuildBrokenInputDirModules         []string `json:",omitempty"`
 
@@ -502,6 +510,7 @@
 		Malloc_zero_contents:         boolPtr(true),
 		Malloc_pattern_fill_contents: boolPtr(false),
 		Safestack:                    boolPtr(false),
+		TrimmedApex:                  boolPtr(false),
 
 		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
 		ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
@@ -655,9 +664,8 @@
 type ProductConfigProperties map[string]map[ProductConfigProperty]interface{}
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
-// have been set for the module in the given context.
-func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProperties {
-	module := ctx.Module()
+// have been set for the given module.
+func ProductVariableProperties(ctx ArchVariantContext, module Module) ProductConfigProperties {
 	moduleBase := module.base()
 
 	productConfigProperties := ProductConfigProperties{}
diff --git a/android/visibility.go b/android/visibility.go
index b209599..5955133 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -155,7 +155,11 @@
 }
 
 func isAncestor(p1 string, p2 string) bool {
-	return strings.HasPrefix(p2+"/", p1+"/")
+	// Equivalent to strings.HasPrefix(p2+"/", p1+"/"), but without the string copies
+	// The check for a trailing slash is so that we don't consider sibling
+	// directories with common prefixes to be ancestors, e.g. "fooo/bar" should not be
+	// a descendant of "foo".
+	return strings.HasPrefix(p2, p1) && (len(p2) == len(p1) || p2[len(p1)] == '/')
 }
 
 func (r subpackagesRule) String() string {
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index f646742..61058df 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -166,10 +166,9 @@
 		}
 	} else {
 		llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
-		llvmLib64 := config.ClangPath(ctx, "lib64/libc++.so.1")
-		llvmLib := config.ClangPath(ctx, "lib/libc++.so.1")
+		llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so.1")
 		for _, strip := range s.properties.Strip_files {
-			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib64).ImplicitTool(llvmLib)
+			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
 			if !ctx.Windows() {
 				cmd.Flag("-x")
 			}
diff --git a/apex/Android.bp b/apex/Android.bp
index 018d030..7ffca0e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -29,6 +29,7 @@
         "bp2build.go",
         "deapexer.go",
         "key.go",
+        "metadata.go",
         "prebuilt.go",
         "testing.go",
         "vndk.go",
@@ -37,6 +38,7 @@
         "apex_test.go",
         "bootclasspath_fragment_test.go",
         "classpath_element_test.go",
+        "metadata_test.go",
         "platform_bootclasspath_test.go",
         "systemserver_classpath_fragment_test.go",
         "vndk_test.go",
diff --git a/apex/OWNERS b/apex/OWNERS
deleted file mode 100644
index 8e4ba5c..0000000
--- a/apex/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file * = jiyong@google.com
diff --git a/apex/androidmk.go b/apex/androidmk.go
index b76f6bd..684833d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -23,8 +23,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/java"
-
-	"github.com/google/blueprint/proptools"
+	"android/soong/rust"
 )
 
 func (a *apexBundle) AndroidMk() android.AndroidMkData {
@@ -64,21 +63,26 @@
 }
 
 // Return the full module name for a dependency module, which appends the apex module name unless re-using a system lib.
-func (a *apexBundle) fullModuleName(apexBundleName string, fi *apexFile) string {
-	linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
-
+func (a *apexBundle) fullModuleName(apexBundleName string, linkToSystemLib bool, fi *apexFile) string {
 	if linkToSystemLib {
 		return fi.androidMkModuleName
 	}
 	return fi.androidMkModuleName + "." + apexBundleName + a.suffix
 }
 
-func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
+// androidMkForFiles generates Make definitions for the contents of an
+// apexBundle (apexBundle#filesInfo).  The filesInfo structure can either be
+// populated by Soong for unconverted APEXes, or Bazel in mixed mode. Use
+// apexFile#isBazelPrebuilt to differentiate.
+func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir string,
 	apexAndroidMkData android.AndroidMkData) []string {
 
-	// apexBundleName comes from the 'name' property; apexName comes from 'apex_name' property.
+	// apexBundleName comes from the 'name' property or soong module.
+	// apexName comes from 'name' property of apex_manifest.
 	// An apex is installed to /system/apex/<apexBundleName> and is activated at /apex/<apexName>
 	// In many cases, the two names are the same, but could be different in general.
+	// However, symbol files for apex files are installed under /apex/<apexBundleName> to avoid
+	// conflicts between two apexes with the same apexName.
 
 	moduleNames := []string{}
 	apexType := a.properties.ApexType
@@ -89,31 +93,11 @@
 		return moduleNames
 	}
 
-	// b/162366062. Prevent GKI APEXes to emit make rules to avoid conflicts.
-	if strings.HasPrefix(apexName, "com.android.gki.") && apexType != flattenedApex {
-		return moduleNames
-	}
-
-	// b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
-	// APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
-	// as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
-	// for the overriding VNDK APEXes.
-	symbolFilesNotNeeded := a.vndkApex && len(a.overridableProperties.Overrides) > 0
-	if symbolFilesNotNeeded && apexType != flattenedApex {
-		return moduleNames
-	}
-
-	// Avoid creating duplicate build rules for multi-installed APEXes.
-	if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
-		return moduleNames
-	}
-
 	seenDataOutPaths := make(map[string]bool)
 
 	for _, fi := range a.filesInfo {
 		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
-
-		moduleName := a.fullModuleName(apexBundleName, &fi)
+		moduleName := a.fullModuleName(apexBundleName, linkToSystemLib, &fi)
 
 		// This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
 		// arch-specific otherwise we will end up installing both ABIs even when only
@@ -141,18 +125,19 @@
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 		}
 		fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
+
 		if fi.module != nil && fi.module.Owner() != "" {
 			fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", fi.module.Owner())
 		}
-		// /apex/<apex_name>/{lib|framework|...}
-		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+		// /apex/<apexBundleName>/{lib|framework|...}
+		pathForSymbol := filepath.Join("$(PRODUCT_OUT)", "apex", apexBundleName, fi.installDir)
 		var modulePath string
 		if apexType == flattenedApex {
-			// /system/apex/<name>/{lib|framework|...}
+			// /system/apex/<apexBundleName>/{lib|framework|...}
 			modulePath = filepath.Join(a.installDir.String(), apexBundleName, fi.installDir)
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
-			if a.primaryApexType && !symbolFilesNotNeeded {
-				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
+			if a.primaryApexType {
+				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathForSymbol)
 			}
 			android.AndroidMkEmitAssignList(w, "LOCAL_MODULE_SYMLINKS", fi.symlinks)
 			newDataPaths := []android.DataPath{}
@@ -165,8 +150,8 @@
 			}
 			android.AndroidMkEmitAssignList(w, "LOCAL_TEST_DATA", android.AndroidMkDataPaths(newDataPaths))
 		} else {
-			modulePath = pathWhenActivated
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+			modulePath = pathForSymbol
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
 
 			// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
 			// We don't need to have notice file for the individual modules in it. Otherwise,
@@ -178,6 +163,7 @@
 		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
 		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
 		if fi.module != nil {
+			// This apexFile's module comes from Soong
 			archStr := fi.module.Target().Arch.ArchType.String()
 			host := false
 			switch fi.module.Target().Os.Class {
@@ -205,6 +191,9 @@
 				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
 				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
 			}
+		} else if fi.isBazelPrebuilt && fi.arch != "" {
+			// This apexFile comes from Bazel
+			fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", fi.arch)
 		}
 		if fi.jacocoReportClassesFile != nil {
 			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
@@ -248,13 +237,21 @@
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
 		case nativeSharedLib, nativeExecutable, nativeTest:
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
-			if ccMod, ok := fi.module.(*cc.Module); ok {
-				if ccMod.UnstrippedOutputFile() != nil {
-					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
-				}
-				ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
-				if ccMod.CoverageOutputFile().Valid() {
-					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
+			if fi.isBazelPrebuilt {
+				fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", fi.unstrippedBuiltFile)
+			} else {
+				if ccMod, ok := fi.module.(*cc.Module); ok {
+					if ccMod.UnstrippedOutputFile() != nil {
+						fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
+					}
+					ccMod.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
+					if ccMod.CoverageOutputFile().Valid() {
+						fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
+					}
+				} else if rustMod, ok := fi.module.(*rust.Module); ok {
+					if rustMod.UnstrippedOutputFile() != nil {
+						fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", rustMod.UnstrippedOutputFile().String())
+					}
 				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk")
@@ -309,7 +306,7 @@
 		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
 		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
 	}
-	android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.requiredDeps, required)
+	android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
 	android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
 	android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
 }
@@ -320,8 +317,7 @@
 			moduleNames := []string{}
 			apexType := a.properties.ApexType
 			if a.installable() {
-				apexName := proptools.StringDefault(a.properties.Apex_name, name)
-				moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
+				moduleNames = a.androidMkForFiles(w, name, moduleDir, data)
 			}
 
 			if apexType == flattenedApex {
diff --git a/apex/apex.go b/apex/apex.go
index 9485a4b..1f700c6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -50,7 +50,7 @@
 	ctx.RegisterModuleType("apex", BundleFactory)
 	ctx.RegisterModuleType("apex_test", TestApexBundleFactory)
 	ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
-	ctx.RegisterModuleType("apex_defaults", defaultsFactory)
+	ctx.RegisterModuleType("apex_defaults", DefaultsFactory)
 	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	ctx.RegisterModuleType("override_apex", OverrideApexFactory)
 	ctx.RegisterModuleType("apex_set", apexSetFactory)
@@ -80,6 +80,7 @@
 	ctx.BottomUp("apex", apexMutator).Parallel()
 	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
 	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
 	// Register after apex_info mutator so that it can use ApexVariationName
 	ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
 }
@@ -93,15 +94,15 @@
 	// a default one is automatically generated.
 	AndroidManifest *string `android:"path"`
 
-	// Canonical name of this APEX bundle. Used to determine the path to the activated APEX on
-	// device (/apex/<apex_name>). If unspecified, follows the name property.
-	Apex_name *string
-
 	// Determines the file contexts file for setting the security contexts to files in this APEX
 	// bundle. For platform APEXes, this should points to a file under /system/sepolicy Default:
 	// /system/sepolicy/apex/<module_name>_file_contexts.
 	File_contexts *string `android:"path"`
 
+	// By default, file_contexts is amended by force-labelling / and /apex_manifest.pb as system_file
+	// to avoid mistakes. When set as true, no force-labelling.
+	Use_file_contexts_as_is *bool
+
 	// Path to the canned fs config file for customizing file's uid/gid/mod/capabilities. The
 	// format is /<path_or_glob> <uid> <gid> <mode> [capabilities=0x<cap>], where path_or_glob is a
 	// path or glob pattern for a file or set of files, uid/gid are numerial values of user ID
@@ -114,6 +115,18 @@
 
 	Multilib apexMultilibProperties
 
+	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
+	Rros []string
+
+	// List of bootclasspath fragments that are embedded inside this APEX bundle.
+	Bootclasspath_fragments []string
+
+	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
+	Systemserverclasspath_fragments []string
+
+	// List of java libraries that are embedded inside this APEX bundle.
+	Java_libs []string
+
 	// List of sh binaries that are embedded inside this APEX bundle.
 	Sh_binaries []string
 
@@ -123,10 +136,6 @@
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
-	// The minimum SDK version that this APEX must support at minimum. This is usually set to
-	// the SDK version that the APEX was first introduced.
-	Min_sdk_version *string
-
 	// Whether this APEX is considered updatable or not. When set to true, this will enforce
 	// additional rules for making sure that the APEX is truly updatable. To be updatable,
 	// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -152,16 +161,6 @@
 	// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
 	Use_vndk_as_stable *bool
 
-	// Whether this is multi-installed APEX should skip installing symbol files.
-	// Multi-installed APEXes share the same apex_name and are installed at the same time.
-	// Default is false.
-	//
-	// Should be set to true for all multi-installed APEXes except the singular
-	// default version within the multi-installed group.
-	// Only the default version can install symbol files in $(PRODUCT_OUT}/apex,
-	// or else conflicting build rules may be created.
-	Multi_install_skip_symbol_files *bool
-
 	// The type of APEX to build. Controls what the APEX payload is. Either 'image', 'zip' or
 	// 'both'. When set to image, contents are stored in a filesystem image inside a zip
 	// container. When set to zip, contents are stored in a zip container directly. This type is
@@ -343,21 +342,9 @@
 	// List of prebuilt files that are embedded inside this APEX bundle.
 	Prebuilts []string
 
-	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
-	Rros []string
-
 	// List of BPF programs inside this APEX bundle.
 	Bpfs []string
 
-	// List of bootclasspath fragments that are embedded inside this APEX bundle.
-	Bootclasspath_fragments []string
-
-	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
-	Systemserverclasspath_fragments []string
-
-	// List of java libraries that are embedded inside this APEX bundle.
-	Java_libs []string
-
 	// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
 	// Soong). This does not completely prevent installation of the overridden binaries, but if
 	// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -389,6 +376,13 @@
 	// conditions, e.g., target device needs to support APEX compression, are also fulfilled.
 	// Default: false.
 	Compressible *bool
+
+	// Trim against a specific Dynamic Common Lib APEX
+	Trim_against *string
+
+	// The minimum SDK version that this APEX must support at minimum. This is usually set to
+	// the SDK version that the APEX was first introduced.
+	Min_sdk_version *string
 }
 
 type apexBundle struct {
@@ -439,8 +433,8 @@
 	// GenerateAndroidBuildActions.
 	filesInfo []apexFile
 
-	// List of other module names that should be installed when this APEX gets installed.
-	requiredDeps []string
+	// List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
+	makeModulesToInstall []string
 
 	///////////////////////////////////////////////////////////////////////////////////////////
 	// Outputs (final and intermediates)
@@ -488,8 +482,6 @@
 	// Optional list of lint report zip files for apexes that contain java or app modules
 	lintReports android.Paths
 
-	prebuiltFileToDelete string
-
 	isCompressed bool
 
 	// Path of API coverage generate file
@@ -517,6 +509,21 @@
 	shBinary
 )
 
+var (
+	classes = map[string]apexFileClass{
+		"app":              app,
+		"appSet":           appSet,
+		"etc":              etc,
+		"goBinary":         goBinary,
+		"javaSharedLib":    javaSharedLib,
+		"nativeExecutable": nativeExecutable,
+		"nativeSharedLib":  nativeSharedLib,
+		"nativeTest":       nativeTest,
+		"pyBinary":         pyBinary,
+		"shBinary":         shBinary,
+	}
+)
+
 // apexFile represents a file in an APEX bundle. This is created during the first half of
 // GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half
 // of the function, this is used to create commands that copies the files into a staging directory,
@@ -526,6 +533,7 @@
 	// buildFile is put in the installDir inside the APEX.
 	builtFile  android.Path
 	installDir string
+	partition  string
 	customStem string
 	symlinks   []string // additional symlinks
 
@@ -550,6 +558,10 @@
 
 	multilib string
 
+	isBazelPrebuilt     bool
+	unstrippedBuiltFile android.Path
+	arch                string
+
 	// TODO(jiyong): remove this
 	module android.Module
 }
@@ -565,6 +577,7 @@
 	}
 	if module != nil {
 		ret.moduleDir = ctx.OtherModuleDir(module)
+		ret.partition = module.PartitionTag(ctx.DeviceConfig())
 		ret.requiredModuleNames = module.RequiredModuleNames()
 		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
 		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
@@ -675,6 +688,7 @@
 	androidAppTag   = &dependencyTag{name: "androidApp", payload: true}
 	bpfTag          = &dependencyTag{name: "bpf", payload: true}
 	certificateTag  = &dependencyTag{name: "certificate"}
+	dclaTag         = &dependencyTag{name: "dcla"}
 	executableTag   = &dependencyTag{name: "executable", payload: true}
 	fsTag           = &dependencyTag{name: "filesystem", payload: true}
 	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
@@ -856,6 +870,10 @@
 
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...)
+	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
 }
@@ -869,10 +887,6 @@
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
-	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
-	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.overridableProperties.Bootclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.overridableProperties.Systemserverclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.overridableProperties.Java_libs...)
 	if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
 		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
 		// regardless of the TARGET_PREFER_* setting. See b/144532908
@@ -908,6 +922,33 @@
 	}
 }
 
+func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
+	if !mctx.Config().ApexTrimEnabled() {
+		return
+	}
+	if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
+		commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+		mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
+	} else if o, ok := mctx.Module().(*OverrideApex); ok {
+		for _, p := range o.GetProperties() {
+			properties, ok := p.(*overridableProperties)
+			if !ok {
+				continue
+			}
+			if properties.Trim_against != nil {
+				commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+				mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
+			}
+		}
+	}
+}
+
+type DCLAInfo struct {
+	ProvidedLibs []string
+}
+
+var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
+
 type ApexBundleInfo struct {
 	Contents *android.ApexContents
 }
@@ -952,6 +993,9 @@
 		if !useVndk {
 			mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
 		}
+		if a.minSdkVersionValue(mctx) != "" {
+			mctx.PropertyErrorf("use_vndk_as_stable", "not supported when min_sdk_version is set")
+		}
 		mctx.VisitDirectDepsWithTag(sharedLibTag, func(dep android.Module) {
 			if c, ok := dep.(*cc.Module); ok && c.IsVndk() {
 				mctx.PropertyErrorf("use_vndk_as_stable", "Trying to include a VNDK library(%s) while use_vndk_as_stable is true.", dep.Name())
@@ -1017,7 +1061,7 @@
 	// This is the main part of this mutator. Mark the collected dependencies that they need to
 	// be built for this apexBundle.
 
-	apexVariationName := proptools.StringDefault(a.properties.Apex_name, mctx.ModuleName()) // could be com.android.foo
+	apexVariationName := mctx.ModuleName() // could be com.android.foo
 	a.properties.ApexVariationName = apexVariationName
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
@@ -1035,6 +1079,12 @@
 		child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
 		return true
 	})
+
+	if a.dynamic_common_lib_apex() {
+		mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+			ProvidedLibs: a.properties.Native_shared_libs,
+		})
+	}
 }
 
 type ApexInfoMutator interface {
@@ -1531,6 +1581,19 @@
 	return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
 }
 
+// See the list of libs to trim
+func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
+	dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
+	if len(dclaModules) > 1 {
+		panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
+	}
+	if len(dclaModules) > 0 {
+		DCLAInfo := ctx.OtherModuleProvider(dclaModules[0], DCLAInfoProvider).(DCLAInfo)
+		return DCLAInfo.ProvidedLibs
+	}
+	return []string{}
+}
+
 // These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
 // members) can be sanitized, either forcibly, or by the global configuration. For some of the
 // sanitizers, extra dependencies can be forcibly added as well.
@@ -1657,7 +1720,7 @@
 	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
 }
 
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile {
 	dirInApex := "bin"
 	fileToCopy := py.HostToolPath().Path()
 	return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
@@ -1669,6 +1732,7 @@
 	// NB: Since go binaries are static we don't need the module for anything here, which is
 	// good since the go tool is a blueprint.Module not an android.Module like we would
 	// normally use.
+	//
 	return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
 }
 
@@ -1732,6 +1796,18 @@
 	return af
 }
 
+func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, module javaModule) *apexFile {
+	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+		if profilePathOnHost := dexpreopter.OutputProfilePathOnHost(); profilePathOnHost != nil {
+			dirInApex := "javalib"
+			af := newApexFile(ctx, profilePathOnHost, module.BaseModuleName()+"-profile", dirInApex, etc, nil)
+			af.customStem = module.Stem() + ".jar.prof"
+			return &af
+		}
+	}
+	return nil
+}
+
 // androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
 // the same way.
 type androidApp interface {
@@ -1866,7 +1942,7 @@
 var _ android.MixedBuildBuildable = (*apexBundle)(nil)
 
 func (a *apexBundle) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
-	return ctx.ModuleType() == "apex" && a.properties.ApexType == imageApex
+	return a.properties.ApexType == imageApex
 }
 
 func (a *apexBundle) QueueBazelCall(ctx android.BaseModuleContext) {
@@ -1915,6 +1991,11 @@
 	}
 	a.outputFile = a.outputApexFile
 
+	if len(outputs.TidyFiles) > 0 {
+		tidyFiles := android.PathsForBazelOut(ctx, outputs.TidyFiles)
+		a.outputFile = android.AttachValidationActions(ctx, a.outputFile, tidyFiles)
+	}
+
 	// TODO(b/257829940): These are used by the apex_keys_text singleton; would probably be a clearer
 	// interface if these were set in a provider rather than the module itself
 	a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyInfo[0])
@@ -1922,11 +2003,9 @@
 	a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
 	a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
 
-	// Ensure ApexInfo.RequiresLibs are installed as part of a bundle build
-	for _, bazelLabel := range outputs.RequiresLibs {
-		// convert Bazel label back to Soong module name
-		a.requiredDeps = append(a.requiredDeps, android.ModuleFromBazelLabel(bazelLabel))
-	}
+	// Ensure ApexMkInfo.install_to_system make module names are installed as
+	// part of a bundled build.
+	a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...)
 
 	apexType := a.properties.ApexType
 	switch apexType {
@@ -1944,21 +2023,45 @@
 		a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
 			a.compatSymlinks.Paths()...)
 	default:
-		panic(fmt.Errorf("unexpected apex_type for the ProcessBazelQuery: %v", a.properties.ApexType))
+		panic(fmt.Errorf("internal error: unexpected apex_type for the ProcessBazelQueryResponse: %v", a.properties.ApexType))
 	}
 
-	/*
-			TODO(asmundak): compared to building an APEX with Soong, building it with Bazel does not
-			return filesInfo and requiredDeps fields (in the Soong build the latter is updated).
-			Fix this, as these fields are subsequently used in apex/androidmk.go and in apex/builder/go
-			To find out what Soong build puts there, run:
-			vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)}
-			ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
-		      return a.depVisitor(&vctx, ctx, child, parent)
-		    })
-			vctx.normalizeFileInfo()
-	*/
+	// filesInfo in mixed mode must retrieve all information about the apex's
+	// contents completely from the Starlark providers. It should never rely on
+	// Android.bp information, as they might not exist for fully migrated
+	// dependencies.
+	//
+	// Prevent accidental writes to filesInfo in the earlier parts Soong by
+	// asserting it to be nil.
+	if a.filesInfo != nil {
+		panic(
+			fmt.Errorf("internal error: filesInfo must be nil for an apex handled by Bazel. " +
+				"Did something else set filesInfo before this line of code?"))
+	}
+	for _, f := range outputs.PayloadFilesInfo {
+		fileInfo := apexFile{
+			isBazelPrebuilt: true,
 
+			builtFile:           android.PathForBazelOut(ctx, f["built_file"]),
+			unstrippedBuiltFile: android.PathForBazelOut(ctx, f["unstripped_built_file"]),
+			androidMkModuleName: f["make_module_name"],
+			installDir:          f["install_dir"],
+			class:               classes[f["class"]],
+			customStem:          f["basename"],
+			moduleDir:           f["package"],
+		}
+
+		arch := f["arch"]
+		fileInfo.arch = arch
+		if len(arch) > 0 {
+			fileInfo.multilib = "lib32"
+			if strings.HasSuffix(arch, "64") {
+				fileInfo.multilib = "lib64"
+			}
+		}
+
+		a.filesInfo = append(a.filesInfo, fileInfo)
+	}
 }
 
 func (a *apexBundle) setCompression(ctx android.ModuleContext) {
@@ -2025,7 +2128,7 @@
 			a.primaryApexType = true
 
 			if ctx.Config().InstallExtraFlattenedApexes() {
-				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+				a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix)
 			}
 		}
 	case zipApex:
@@ -2147,7 +2250,7 @@
 			case *cc.Module:
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
 				return true // track transitive dependencies
-			case *python.Module:
+			case *python.PythonBinaryModule:
 				if ch.HostToolPath().Valid() {
 					vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
 				}
@@ -2177,7 +2280,7 @@
 
 			vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
 			for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
-				a.requiredDeps = append(a.requiredDeps, makeModuleName)
+				a.makeModulesToInstall = append(a.makeModulesToInstall, makeModuleName)
 			}
 			return true
 		case sscpfTag:
@@ -2295,12 +2398,6 @@
 			} else {
 				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
 			}
-		case android.PrebuiltDepTag:
-			// If the prebuilt is force disabled, remember to delete the prebuilt file
-			// that might have been installed in the previous builds
-			if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
-				a.prebuiltFileToDelete = prebuilt.InstallFilename()
-			}
 		}
 		return false
 	}
@@ -2340,11 +2437,14 @@
 				//
 				// Always include if we are a host-apex however since those won't have any
 				// system libraries.
-				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
+				//
+				// Skip the dependency in unbundled builds where the device image is not
+				// being built.
+				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
 					// we need a module name for Make
 					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
-					if !android.InList(name, a.requiredDeps) {
-						a.requiredDeps = append(a.requiredDeps, name)
+					if !android.InList(name, a.makeModulesToInstall) {
+						a.makeModulesToInstall = append(a.makeModulesToInstall, name)
 					}
 				}
 				vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
@@ -2431,6 +2531,9 @@
 		case *java.Library, *java.SdkLibrary:
 			af := apexFileForJavaModule(ctx, child.(javaModule))
 			vctx.filesInfo = append(vctx.filesInfo, af)
+			if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
+				vctx.filesInfo = append(vctx.filesInfo, *profileAf)
+			}
 			return true // track transitive dependencies
 		default:
 			ctx.PropertyErrorf("systemserverclasspath_fragments",
@@ -2479,7 +2582,6 @@
 	}
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 2) traverse the dependency tree to collect apexFile structs from them.
-
 	// Collect the module directory for IDE info in java/jdeps.go.
 	a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
 
@@ -2677,14 +2779,9 @@
 }
 
 // apex_defaults provides defaultable properties to other apex modules.
-func defaultsFactory() android.Module {
-	return DefaultsFactory()
-}
-
-func DefaultsFactory(props ...interface{}) android.Module {
+func DefaultsFactory() android.Module {
 	module := &Defaults{}
 
-	module.AddProperties(props...)
 	module.AddProperties(
 		&apexBundleProperties{},
 		&apexTargetBundleProperties{},
@@ -2837,7 +2934,7 @@
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
-	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
 	if minApiLevel.IsNone() {
 		return ""
 	}
@@ -2852,12 +2949,8 @@
 }
 
 // Returns apex's min_sdk_version SdkSpec, honoring overrides
-func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return android.SdkSpec{
-		Kind:     android.SdkNone,
-		ApiLevel: a.minSdkVersion(ctx),
-		Raw:      a.minSdkVersionValue(ctx),
-	}
+func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return a.minSdkVersion(ctx)
 }
 
 // Returns apex's min_sdk_version ApiLevel, honoring overrides
@@ -2931,8 +3024,8 @@
 		if a.UsePlatformApis() {
 			ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
 		}
-		if a.SocSpecific() || a.DeviceSpecific() {
-			ctx.PropertyErrorf("updatable", "vendor APEXes are not updatable")
+		if proptools.Bool(a.properties.Use_vndk_as_stable) {
+			ctx.PropertyErrorf("use_vndk_as_stable", "updatable APEXes can't use external VNDK libs")
 		}
 		if a.FutureUpdatable() {
 			ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`")
@@ -3062,9 +3155,9 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Java_libs...)
-	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Bootclasspath_fragments...)
-	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Systemserverclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
 	dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
 }
 
@@ -3487,12 +3580,12 @@
 		fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.File_contexts))
 	}
 
-	productVariableProps := android.ProductVariableProperties(ctx)
+	productVariableProps := android.ProductVariableProperties(ctx, a)
 	// TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
 	// given it's coming via config, we probably don't want to put it in here.
 	var minSdkVersion bazel.StringAttribute
-	if a.properties.Min_sdk_version != nil {
-		minSdkVersion.SetValue(*a.properties.Min_sdk_version)
+	if a.overridableProperties.Min_sdk_version != nil {
+		minSdkVersion.SetValue(*a.overridableProperties.Min_sdk_version)
 	}
 	if props, ok := productVariableProps[minSdkVersionPropName]; ok {
 		for c, p := range props {
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 6faed70..1581949 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -55,17 +55,17 @@
 			   touch ${out};
 			else
 				echo -e "\n******************************";
-				echo "ERROR: go/apex-allowed-deps-error";
+				echo "ERROR: go/apex-allowed-deps-error contains more information";
 				echo "******************************";
 				echo "Detected changes to allowed dependencies in updatable modules.";
 				echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
 				echo -e "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)\n";
 				echo "When submitting the generated CL, you must include the following information";
 				echo "in the commit message if you are adding a new dependency:";
-				echo "Apex-Size-Increase:";
-				echo "Previous-Platform-Support:";
-				echo "Aosp-First:";
-				echo "Test-Info:";
+				echo "Apex-Size-Increase: Expected binary size increase for affected APEXes (or the size of the .jar / .so file of the new library)";
+				echo "Previous-Platform-Support: Are the maintainers of the new dependency committed to supporting previous platform releases?";
+				echo "Aosp-First: Is the new dependency being developed AOSP-first or internal?";
+				echo "Test-Info: What’s the testing strategy for the new dependency? Does it have its own tests, and are you adding integration tests? How/when are the tests run?";
 				echo "You do not need OWNERS approval to submit the change, but mainline-modularization@";
 				echo "will periodically review additions and may require changes.";
 				echo -e "******************************\n";
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 33fce7c..c9665a4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -784,6 +784,43 @@
 	}
 }
 
+func TestFileContexts(t *testing.T) {
+	for _, useFileContextsAsIs := range []bool{true, false} {
+		prop := ""
+		if useFileContextsAsIs {
+			prop = "use_file_contexts_as_is: true,\n"
+		}
+		ctx := testApex(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				file_contexts: "file_contexts",
+				updatable: false,
+				vendor: true,
+				`+prop+`
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+		`, withFiles(map[string][]byte{
+			"file_contexts": nil,
+		}))
+
+		rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("file_contexts")
+		forceLabellingCommand := "apex_manifest\\\\.pb u:object_r:system_file:s0"
+		if useFileContextsAsIs {
+			android.AssertStringDoesNotContain(t, "should force-label",
+				rule.RuleParams.Command, forceLabellingCommand)
+		} else {
+			android.AssertStringDoesContain(t, "shouldn't force-label",
+				rule.RuleParams.Command, forceLabellingCommand)
+		}
+	}
+}
+
 func TestBasicZipApex(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -1890,13 +1927,13 @@
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
-func TestApexMinSdkVersion_crtobjectInVendorApex(t *testing.T) {
+func TestApexMinSdkVersion_InVendorApex(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
-			updatable: false,
+			updatable: true,
 			vendor: true,
 			min_sdk_version: "29",
 		}
@@ -1909,20 +1946,34 @@
 
 		cc_library {
 			name: "mylib",
+			srcs: ["mylib.cpp"],
 			vendor_available: true,
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "myapex" ],
 			min_sdk_version: "29",
+			shared_libs: ["libbar"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+			llndk: { symbol_file: "libbar.map.txt" },
 		}
 	`)
 
 	vendorVariant := "android_vendor.29_arm64_armv8-a"
 
-	// First check that the correct variant of crtbegin_so is used.
-	ldRule := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29").Rule("ld")
-	crtBegin := names(ldRule.Args["crtBegin"])
-	ensureListContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
+	mylib := ctx.ModuleForTests("mylib", vendorVariant+"_shared_myapex")
+
+	// Ensure that mylib links with "current" LLNDK
+	libFlags := names(mylib.Rule("ld").Args["libFlags"])
+	ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared_current/libbar.so")
+
+	// Ensure that mylib is targeting 29
+	ccRule := ctx.ModuleForTests("mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o")
+	ensureContains(t, ccRule.Args["cFlags"], "-target aarch64-linux-android29")
+
+	// Ensure that the correct variant of crtbegin_so is used.
+	crtBegin := mylib.Rule("ld").Args["crtBegin"]
+	ensureContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
 
 	// Ensure that the crtbegin_so used by the APEX is targeting 29
 	cflags := ctx.ModuleForTests("crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"]
@@ -2128,6 +2179,34 @@
 			min_sdk_version: "30",
 		}
 	`)
+
+	// Skip check for modules compiling against core API surface
+	testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["libfoo"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "libfoo",
+			srcs: ["Foo.java"],
+			apex_available: [
+				"myapex",
+			],
+			// Compile against core API surface
+			sdk_version: "core_current",
+			min_sdk_version: "30",
+		}
+	`)
+
 }
 
 func TestApexMinSdkVersion_Okay(t *testing.T) {
@@ -3329,17 +3408,14 @@
 	// non-APEX variant does not have __ANDROID_APEX__ defined
 	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
 
-	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=10000")
 
-	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=29")
 
 	// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
 	// each variant defines additional macros to distinguish which apex variant it is built for
@@ -3348,19 +3424,17 @@
 	mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
-	// recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+	// recovery variant does not set __ANDROID_APEX__
 	mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
 
 	// non-APEX variant does not have __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
-	// recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+	// recovery variant does not set __ANDROID_APEX__
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
 }
 
 func TestHeaderLibsDependency(t *testing.T) {
@@ -3489,14 +3563,14 @@
 	return ret
 }
 
-func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+func assertFileListEquals(t *testing.T, expectedFiles []string, actualFiles []fileInApex) {
 	t.Helper()
 	var failed bool
 	var surplus []string
 	filesMatched := make(map[string]bool)
-	for _, file := range getFiles(t, ctx, moduleName, variant) {
+	for _, file := range actualFiles {
 		matchFound := false
-		for _, expected := range files {
+		for _, expected := range expectedFiles {
 			if file.match(expected) {
 				matchFound = true
 				filesMatched[expected] = true
@@ -3514,9 +3588,9 @@
 		failed = true
 	}
 
-	if len(files) > len(filesMatched) {
+	if len(expectedFiles) > len(filesMatched) {
 		var missing []string
-		for _, expected := range files {
+		for _, expected := range expectedFiles {
 			if !filesMatched[expected] {
 				missing = append(missing, expected)
 			}
@@ -3530,6 +3604,32 @@
 	}
 }
 
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+	assertFileListEquals(t, files, getFiles(t, ctx, moduleName, variant))
+}
+
+func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
+	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Rule("deapexer")
+	outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
+	if deapexer.Output != nil {
+		outputs = append(outputs, deapexer.Output.String())
+	}
+	for _, output := range deapexer.ImplicitOutputs {
+		outputs = append(outputs, output.String())
+	}
+	actualFiles := make([]fileInApex, 0, len(outputs))
+	for _, output := range outputs {
+		dir := "/deapexer/"
+		pos := strings.LastIndex(output, dir)
+		if pos == -1 {
+			t.Fatal("Unknown deapexer output ", output)
+		}
+		path := output[pos+len(dir):]
+		actualFiles = append(actualFiles, fileInApex{path: path, src: "", isLink: false})
+	}
+	assertFileListEquals(t, files, actualFiles)
+}
+
 func TestVndkApexCurrent(t *testing.T) {
 	commonFiles := []string{
 		"lib/libc++.so",
@@ -3811,11 +3911,9 @@
 		}`+vndkLibrariesTxtFiles("28", "current"))
 
 	assertApexName := func(expected, moduleName string) {
-		bundle := ctx.ModuleForTests(moduleName, "android_common_image").Module().(*apexBundle)
-		actual := proptools.String(bundle.properties.Apex_name)
-		if !reflect.DeepEqual(actual, expected) {
-			t.Errorf("Got '%v', expected '%v'", actual, expected)
-		}
+		module := ctx.ModuleForTests(moduleName, "android_common_image")
+		apexManifestRule := module.Rule("apexManifestRule")
+		ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected)
 	}
 
 	assertApexName("com.android.vndk.v29", "com.android.vndk.current")
@@ -4112,57 +4210,11 @@
 	ensureListEmpty(t, requireNativeLibs)
 }
 
-func TestApexName(t *testing.T) {
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			apex_name: "com.android.myapex",
-			native_shared_libs: ["mylib"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [
-				"//apex_available:platform",
-				"myapex",
-			],
-		}
-	`)
-
-	module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
-	apexManifestRule := module.Rule("apexManifestRule")
-	ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
-	apexRule := module.Rule("apexRule")
-	ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
-
-	apexBundle := module.Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
-	name := apexBundle.BaseModuleName()
-	prefix := "TARGET_"
-	var builder strings.Builder
-	data.Custom(&builder, name, prefix, "", data)
-	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
-}
-
 func TestOverrideApexManifestDefaultVersion(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			apex_name: "com.android.myapex",
 			native_shared_libs: ["mylib"],
 			updatable: false,
 		}
@@ -4187,7 +4239,7 @@
 		"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
 	}))
 
-	module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
 	apexManifestRule := module.Rule("apexManifestRule")
 	ensureContains(t, apexManifestRule.Args["default_version"], "1234")
 }
@@ -4771,6 +4823,9 @@
 	android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String())
 	android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"])
 	android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.apex", rule.Args["install_path"])
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, testingModule.Module())[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "prebuilt_apex", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func TestPrebuiltMissingSrc(t *testing.T) {
@@ -5721,7 +5776,7 @@
 		}),
 	)
 	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	ensureListContains(t, ab.requiredDeps, "myapex.flattened")
+	ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened")
 	mk := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
@@ -6321,9 +6376,6 @@
 			apps: ["app"],
 			bpfs: ["bpf"],
 			prebuilts: ["myetc"],
-			bootclasspath_fragments: ["mybootclasspath_fragment"],
-			systemserverclasspath_fragments: ["mysystemserverclasspath_fragment"],
-			java_libs: ["myjava_library"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -6334,9 +6386,6 @@
 			apps: ["override_app"],
 			bpfs: ["overrideBpf"],
 			prebuilts: ["override_myetc"],
-			bootclasspath_fragments: ["override_bootclasspath_fragment"],
-			systemserverclasspath_fragments: ["override_systemserverclasspath_fragment"],
-			java_libs: ["override_java_library"],
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
@@ -6395,78 +6444,6 @@
 			name: "override_myetc",
 			src: "override_myprebuilt",
 		}
-
-		java_library {
-			name: "bcplib",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-			permitted_packages: ["bcp.lib"],
-		}
-
-		bootclasspath_fragment {
-			name: "mybootclasspath_fragment",
-			contents: ["bcplib"],
-			apex_available: ["myapex"],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-
-		java_library {
-			name: "override_bcplib",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-			permitted_packages: ["override.bcp.lib"],
-		}
-
-		bootclasspath_fragment {
-			name: "override_bootclasspath_fragment",
-			contents: ["override_bcplib"],
-			apex_available: ["myapex"],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-
-		java_library {
-			name: "systemserverlib",
-			srcs: ["a.java"],
-			apex_available: ["myapex"],
-		}
-
-		systemserverclasspath_fragment {
-			name: "mysystemserverclasspath_fragment",
-			standalone_contents: ["systemserverlib"],
-			apex_available: ["myapex"],
-		}
-
-		java_library {
-			name: "override_systemserverlib",
-			srcs: ["a.java"],
-			apex_available: ["myapex"],
-		}
-
-		systemserverclasspath_fragment {
-			name: "override_systemserverclasspath_fragment",
-			standalone_contents: ["override_systemserverlib"],
-			apex_available: ["myapex"],
-		}
-
-		java_library {
-			name: "myjava_library",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-		}
-
-		java_library {
-			name: "override_java_library",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6501,13 +6478,6 @@
 		t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent)
 	}
 
-	android.AssertArrayString(t, "Bootclasspath_fragments does not match",
-		[]string{"override_bootclasspath_fragment"}, apexBundle.overridableProperties.Bootclasspath_fragments)
-	android.AssertArrayString(t, "Systemserverclasspath_fragments does not match",
-		[]string{"override_systemserverclasspath_fragment"}, apexBundle.overridableProperties.Systemserverclasspath_fragments)
-	android.AssertArrayString(t, "Java_libs does not match",
-		[]string{"override_java_library"}, apexBundle.overridableProperties.Java_libs)
-
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
 	ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey")
@@ -6522,18 +6492,12 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_bcplib.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_java_library.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
 
@@ -7149,7 +7113,10 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["myotherlib"],
+			shared_libs: [
+				"myotherlib",
+				"myotherlib_ext",
+			],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [
@@ -7173,6 +7140,20 @@
 			min_sdk_version: "current",
 		}
 
+		cc_library {
+			name: "myotherlib_ext",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			system_ext_specific: true,
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"myapex.updatable",
+				"//apex_available:platform",
+			],
+			min_sdk_version: "current",
+		}
+
 		java_library {
 			name: "myjar",
 			srcs: ["foo/bar/MyClass.java"],
@@ -7213,12 +7194,15 @@
 		t.Errorf("%q is not found", file)
 	}
 
-	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
+	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string, target string) {
 		for _, f := range files {
 			if f.path == file {
 				if !f.isLink {
 					t.Errorf("%q is not a symlink", file)
 				}
+				if f.src != target {
+					t.Errorf("expected symlink target to be %q, got %q", target, f.src)
+				}
 				return
 			}
 		}
@@ -7232,23 +7216,27 @@
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
 	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib_ext.so")
 
 	files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
 	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib_ext.so")
 
 	// For bundled build, symlink to the system for the non-updatable APEXes only
 	ctx = testApex(t, bp)
 	files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
-	ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+	ensureSymlinkExists(t, files, "lib64/myotherlib.so", "/system/lib64/myotherlib.so")             // this is symlink
+	ensureSymlinkExists(t, files, "lib64/myotherlib_ext.so", "/system_ext/lib64/myotherlib_ext.so") // this is symlink
 
 	files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
-	ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file
+	ensureRealfileExists(t, files, "lib64/myotherlib.so")     // this is a real file
+	ensureRealfileExists(t, files, "lib64/myotherlib_ext.so") // this is a real file
 }
 
 func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
@@ -7886,12 +7874,13 @@
 	`)
 }
 
-func TestUpdatable_cannot_be_vendor_apex(t *testing.T) {
-	testApexError(t, `"myapex" .*: updatable: vendor APEXes are not updatable`, `
+func Test_use_vndk_as_stable_shouldnt_be_used_for_updatable_vendor_apexes(t *testing.T) {
+	testApexError(t, `"myapex" .*: use_vndk_as_stable: updatable APEXes can't use external VNDK libs`, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			updatable: true,
+			use_vndk_as_stable: true,
 			soc_specific: true,
 		}
 
@@ -7903,6 +7892,42 @@
 	`)
 }
 
+func Test_use_vndk_as_stable_shouldnt_be_used_with_min_sdk_version(t *testing.T) {
+	testApexError(t, `"myapex" .*: use_vndk_as_stable: not supported when min_sdk_version is set`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+			min_sdk_version: "29",
+			use_vndk_as_stable: true,
+			vendor: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
+func Test_use_vndk_as_stable_shouldnt_be_used_for_non_vendor_apexes(t *testing.T) {
+	testApexError(t, `"myapex" .*: use_vndk_as_stable: not supported for system/system_ext APEXes`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+			use_vndk_as_stable: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
 func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
 	testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
 		apex {
@@ -9302,7 +9327,7 @@
 	`)
 
 	bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+	bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo")
 	data := android.AndroidMkDataForTest(t, ctx, bundle)
 	var builder strings.Builder
 	data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
@@ -9310,7 +9335,7 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
 
 	flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-	flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+	flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo")
 	flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
 	var flattenedBuilder strings.Builder
 	flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
@@ -9554,7 +9579,7 @@
 func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
 	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
 	for _, dep := range deps {
-		android.AssertStringListContains(t, "", a.requiredDeps, dep)
+		android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
 	}
 }
 
@@ -9562,191 +9587,192 @@
 func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
 	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
 	for _, dep := range deps {
-		android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+		android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
 	}
 }
 
-func TestApexStrictUpdtabilityLint(t *testing.T) {
-	bpTemplate := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			java_libs: ["myjavalib"],
-			updatable: %v,
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		java_library {
-			name: "myjavalib",
-			srcs: ["MyClass.java"],
-			apex_available: [ "myapex" ],
-			lint: {
-				strict_updatability_linting: %v,
-			},
-			sdk_version: "current",
-			min_sdk_version: "29",
-		}
-		`
-	fs := android.MockFS{
-		"lint-baseline.xml": nil,
-	}
-
-	testCases := []struct {
-		testCaseName              string
-		apexUpdatable             bool
-		javaStrictUpdtabilityLint bool
-		lintFileExists            bool
-		disallowedFlagExpected    bool
-	}{
-		{
-			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
-			apexUpdatable:             true,
-			javaStrictUpdtabilityLint: true,
-			lintFileExists:            false,
-			disallowedFlagExpected:    false,
-		},
-		{
-			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
-			apexUpdatable:             false,
-			javaStrictUpdtabilityLint: false,
-			lintFileExists:            true,
-			disallowedFlagExpected:    false,
-		},
-		{
-			testCaseName:              "non-updatable apex respects strict updatability of javalib",
-			apexUpdatable:             false,
-			javaStrictUpdtabilityLint: true,
-			lintFileExists:            true,
-			disallowedFlagExpected:    true,
-		},
-		{
-			testCaseName:              "updatable apex sets strict updatability of javalib to true",
-			apexUpdatable:             true,
-			javaStrictUpdtabilityLint: false, // will be set to true by mutator
-			lintFileExists:            true,
-			disallowedFlagExpected:    true,
-		},
-	}
-
-	for _, testCase := range testCases {
-		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint)
-		fixtures := []android.FixturePreparer{}
-		if testCase.lintFileExists {
-			fixtures = append(fixtures, fs.AddToFixture())
-		}
-
-		result := testApex(t, bp, fixtures...)
-		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
-
-		if disallowedFlagActual != testCase.disallowedFlagExpected {
-			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-		}
-	}
-}
-
-func TestUpdatabilityLintSkipLibcore(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			java_libs: ["myjavalib"],
-			updatable: true,
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		java_library {
-			name: "myjavalib",
-			srcs: ["MyClass.java"],
-			apex_available: [ "myapex" ],
-			sdk_version: "current",
-			min_sdk_version: "29",
-		}
-		`
-
-	testCases := []struct {
-		testCaseName           string
-		moduleDirectory        string
-		disallowedFlagExpected bool
-	}{
-		{
-			testCaseName:           "lintable module defined outside libcore",
-			moduleDirectory:        "",
-			disallowedFlagExpected: true,
-		},
-		{
-			testCaseName:           "lintable module defined in libcore root directory",
-			moduleDirectory:        "libcore/",
-			disallowedFlagExpected: false,
-		},
-		{
-			testCaseName:           "lintable module defined in libcore child directory",
-			moduleDirectory:        "libcore/childdir/",
-			disallowedFlagExpected: true,
-		},
-	}
-
-	for _, testCase := range testCases {
-		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
-		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
-		result := testApex(t, "", lintFileCreator, bpFileCreator)
-		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
-		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
-
-		if disallowedFlagActual != testCase.disallowedFlagExpected {
-			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-		}
-	}
-}
-
-// checks transtive deps of an apex coming from bootclasspath_fragment
-func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			bootclasspath_fragments: ["mybootclasspathfragment"],
-			updatable: true,
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		bootclasspath_fragment {
-			name: "mybootclasspathfragment",
-			contents: ["myjavalib"],
-			apex_available: ["myapex"],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-		java_library {
-			name: "myjavalib",
-			srcs: ["MyClass.java"],
-			apex_available: [ "myapex" ],
-			sdk_version: "current",
-			min_sdk_version: "29",
-			compile_dex: true,
-		}
-		`
-	fs := android.MockFS{
-		"lint-baseline.xml": nil,
-	}
-
-	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
-	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
-	}
-}
+// TODO(b/193460475): Re-enable this test
+//func TestApexStrictUpdtabilityLint(t *testing.T) {
+//	bpTemplate := `
+//		apex {
+//			name: "myapex",
+//			key: "myapex.key",
+//			java_libs: ["myjavalib"],
+//			updatable: %v,
+//			min_sdk_version: "29",
+//		}
+//		apex_key {
+//			name: "myapex.key",
+//		}
+//		java_library {
+//			name: "myjavalib",
+//			srcs: ["MyClass.java"],
+//			apex_available: [ "myapex" ],
+//			lint: {
+//				strict_updatability_linting: %v,
+//			},
+//			sdk_version: "current",
+//			min_sdk_version: "29",
+//		}
+//		`
+//	fs := android.MockFS{
+//		"lint-baseline.xml": nil,
+//	}
+//
+//	testCases := []struct {
+//		testCaseName              string
+//		apexUpdatable             bool
+//		javaStrictUpdtabilityLint bool
+//		lintFileExists            bool
+//		disallowedFlagExpected    bool
+//	}{
+//		{
+//			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
+//			apexUpdatable:             true,
+//			javaStrictUpdtabilityLint: true,
+//			lintFileExists:            false,
+//			disallowedFlagExpected:    false,
+//		},
+//		{
+//			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
+//			apexUpdatable:             false,
+//			javaStrictUpdtabilityLint: false,
+//			lintFileExists:            true,
+//			disallowedFlagExpected:    false,
+//		},
+//		{
+//			testCaseName:              "non-updatable apex respects strict updatability of javalib",
+//			apexUpdatable:             false,
+//			javaStrictUpdtabilityLint: true,
+//			lintFileExists:            true,
+//			disallowedFlagExpected:    true,
+//		},
+//		{
+//			testCaseName:              "updatable apex sets strict updatability of javalib to true",
+//			apexUpdatable:             true,
+//			javaStrictUpdtabilityLint: false, // will be set to true by mutator
+//			lintFileExists:            true,
+//			disallowedFlagExpected:    true,
+//		},
+//	}
+//
+//	for _, testCase := range testCases {
+//		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint)
+//		fixtures := []android.FixturePreparer{}
+//		if testCase.lintFileExists {
+//			fixtures = append(fixtures, fs.AddToFixture())
+//		}
+//
+//		result := testApex(t, bp, fixtures...)
+//		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+//		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
+//		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
+//
+//		if disallowedFlagActual != testCase.disallowedFlagExpected {
+//			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+//		}
+//	}
+//}
+//
+//func TestUpdatabilityLintSkipLibcore(t *testing.T) {
+//	bp := `
+//		apex {
+//			name: "myapex",
+//			key: "myapex.key",
+//			java_libs: ["myjavalib"],
+//			updatable: true,
+//			min_sdk_version: "29",
+//		}
+//		apex_key {
+//			name: "myapex.key",
+//		}
+//		java_library {
+//			name: "myjavalib",
+//			srcs: ["MyClass.java"],
+//			apex_available: [ "myapex" ],
+//			sdk_version: "current",
+//			min_sdk_version: "29",
+//		}
+//		`
+//
+//	testCases := []struct {
+//		testCaseName           string
+//		moduleDirectory        string
+//		disallowedFlagExpected bool
+//	}{
+//		{
+//			testCaseName:           "lintable module defined outside libcore",
+//			moduleDirectory:        "",
+//			disallowedFlagExpected: true,
+//		},
+//		{
+//			testCaseName:           "lintable module defined in libcore root directory",
+//			moduleDirectory:        "libcore/",
+//			disallowedFlagExpected: false,
+//		},
+//		{
+//			testCaseName:           "lintable module defined in libcore child directory",
+//			moduleDirectory:        "libcore/childdir/",
+//			disallowedFlagExpected: true,
+//		},
+//	}
+//
+//	for _, testCase := range testCases {
+//		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
+//		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
+//		result := testApex(t, "", lintFileCreator, bpFileCreator)
+//		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+//		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
+//		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
+//		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
+//
+//		if disallowedFlagActual != testCase.disallowedFlagExpected {
+//			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+//		}
+//	}
+//}
+//
+//// checks transtive deps of an apex coming from bootclasspath_fragment
+//func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
+//	bp := `
+//		apex {
+//			name: "myapex",
+//			key: "myapex.key",
+//			bootclasspath_fragments: ["mybootclasspathfragment"],
+//			updatable: true,
+//			min_sdk_version: "29",
+//		}
+//		apex_key {
+//			name: "myapex.key",
+//		}
+//		bootclasspath_fragment {
+//			name: "mybootclasspathfragment",
+//			contents: ["myjavalib"],
+//			apex_available: ["myapex"],
+//			hidden_api: {
+//				split_packages: ["*"],
+//			},
+//		}
+//		java_library {
+//			name: "myjavalib",
+//			srcs: ["MyClass.java"],
+//			apex_available: [ "myapex" ],
+//			sdk_version: "current",
+//			min_sdk_version: "29",
+//			compile_dex: true,
+//		}
+//		`
+//	fs := android.MockFS{
+//		"lint-baseline.xml": nil,
+//	}
+//
+//	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
+//	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+//	sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
+//	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
+//		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
+//	}
+//}
 
 // updatable apexes should propagate updatable=true to its apps
 func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) {
@@ -9816,16 +9842,239 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			native_shared_libs: ["libfoo"],
+			native_shared_libs: ["libbaz"],
+			binaries: ["binfoo"],
 			min_sdk_version: "29",
 		}
 		apex_key {
 			name: "myapex.key",
 		}
+		cc_binary {
+			name: "binfoo",
+			shared_libs: ["libbar", "libbaz", "libqux",],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+			recovery_available: false,
+		}
+		cc_library {
+			name: "libbar",
+			srcs: ["libbar.cc"],
+			stubs: {
+				symbol_file: "libbar.map.txt",
+				versions: [
+					"29",
+				],
+			},
+		}
+		cc_library {
+			name: "libbaz",
+			srcs: ["libbaz.cc"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+			stubs: {
+				symbol_file: "libbaz.map.txt",
+				versions: [
+					"29",
+				],
+			},
+		}
+		cc_api_library {
+			name: "libbar",
+			src: "libbar_stub.so",
+			min_sdk_version: "29",
+			variants: ["apex.29"],
+		}
+		cc_api_variant {
+			name: "libbar",
+			variant: "apex",
+			version: "29",
+			src: "libbar_apex_29.so",
+		}
+		cc_api_library {
+			name: "libbaz",
+			src: "libbaz_stub.so",
+			min_sdk_version: "29",
+			variants: ["apex.29"],
+		}
+		cc_api_variant {
+			name: "libbaz",
+			variant: "apex",
+			version: "29",
+			src: "libbaz_apex_29.so",
+		}
+		cc_api_library {
+			name: "libqux",
+			src: "libqux_stub.so",
+			min_sdk_version: "29",
+			variants: ["apex.29"],
+		}
+		cc_api_variant {
+			name: "libqux",
+			variant: "apex",
+			version: "29",
+			src: "libqux_apex_29.so",
+		}
+		api_imports {
+			name: "api_imports",
+			apex_shared_libs: [
+				"libbar",
+				"libbaz",
+				"libqux",
+			],
+		}
+		`
+	result := testApex(t, bp)
+
+	hasDep := func(m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		result.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	// Library defines stubs and cc_api_library should be used with cc_api_library
+	binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Module()
+	libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+	libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
+	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
+
+	binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Rule("ld").Args["libFlags"]
+	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
+	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
+	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
+
+	// Library defined in the same APEX should be linked with original definition instead of cc_api_library
+	libbazApexVariant := result.ModuleForTests("libbaz", "android_arm64_armv8-a_shared_apex29").Module()
+	libbazApiImportCoreVariant := result.ModuleForTests("libbaz.apiimport", "android_arm64_armv8-a_shared").Module()
+	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even from same APEX", true, hasDep(binfooApexVariant, libbazApiImportCoreVariant))
+	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbazApexVariant))
+
+	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbaz.so")
+	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbaz.apiimport.so")
+	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbaz.apex.29.apiimport.so")
+
+	// cc_api_library defined without original library should be linked with cc_api_library
+	libquxApiImportApexVariant := result.ModuleForTests("libqux.apiimport", "android_arm64_armv8-a_shared").Module()
+	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even original library definition does not exist", true, hasDep(binfooApexVariant, libquxApiImportApexVariant))
+	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libqux.apex.29.apiimport.so")
+}
+
+func TestPlatformBinaryBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libbar"],
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		cc_binary {
+			name: "binfoo",
+			shared_libs: ["libbar"],
+			recovery_available: false,
+		}
+		cc_library {
+			name: "libbar",
+			srcs: ["libbar.cc"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+			stubs: {
+				symbol_file: "libbar.map.txt",
+				versions: [
+					"29",
+				],
+			},
+		}
+		cc_api_library {
+			name: "libbar",
+			src: "libbar_stub.so",
+			variants: ["apex.29"],
+		}
+		cc_api_variant {
+			name: "libbar",
+			variant: "apex",
+			version: "29",
+			src: "libbar_apex_29.so",
+		}
+		api_imports {
+			name: "api_imports",
+			apex_shared_libs: [
+				"libbar",
+			],
+		}
+		`
+
+	result := testApex(t, bp)
+
+	hasDep := func(m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		result.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	// Library defines stubs and cc_api_library should be used with cc_api_library
+	binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Module()
+	libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+	libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
+	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
+
+	binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
+	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
+	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
+	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
+}
+
+func TestTrimmedApex(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo","libbaz"],
+			min_sdk_version: "29",
+			trim_against: "mydcla",
+    }
+		apex {
+			name: "mydcla",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo","libbar"],
+			min_sdk_version: "29",
+			file_contexts: ":myapex-file_contexts",
+			dynamic_common_lib_apex: true,
+		}
+		apex_key {
+			name: "myapex.key",
+		}
 		cc_library {
 			name: "libfoo",
 			shared_libs: ["libc"],
-			apex_available: ["myapex"],
+			apex_available: ["myapex","mydcla"],
+			min_sdk_version: "29",
+		}
+		cc_library {
+			name: "libbar",
+			shared_libs: ["libc"],
+			apex_available: ["myapex","mydcla"],
+			min_sdk_version: "29",
+		}
+		cc_library {
+			name: "libbaz",
+			shared_libs: ["libc"],
+			apex_available: ["myapex","mydcla"],
 			min_sdk_version: "29",
 		}
 		cc_api_library {
@@ -9842,28 +10091,17 @@
 			header_libs: [],
 		}
 		`
-	result := testApex(t, bp)
-
-	hasDep := func(m android.Module, wantDep android.Module) bool {
-		t.Helper()
-		var found bool
-		result.VisitDirectDeps(m, func(dep blueprint.Module) {
-			if dep == wantDep {
-				found = true
-			}
-		})
-		return found
+	ctx := testApex(t, bp)
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.MaybeRule("apexRule")
+	if apexRule.Rule == nil {
+		t.Errorf("Expecting regular apex rule but a non regular apex rule found")
 	}
 
-	libfooApexVariant := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex29").Module()
-	libcApexVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared_apex29").Module()
-
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(libfooApexVariant, libcApexVariant))
-
-	// libfoo core variant should be buildable in the same inner tree since
-	// certain mcombo files might build system and apexes in the same inner tree
-	// libfoo core variant should link against source libc
-	libfooCoreVariant := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libcCoreVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared").Module()
-	android.AssertBoolEquals(t, "core variant should link against source libc", true, hasDep(libfooCoreVariant, libcCoreVariant))
+	ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
+	trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("TrimmedApexRule")
+	libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
+	android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
+	android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
+	android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
 }
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index af4fd9f..2ddfd03 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -530,9 +530,8 @@
 			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
-		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+		ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
 			"etc/boot-image.prof",
-			"etc/classpaths/bootclasspath.pb",
 			"javalib/arm/boot.art",
 			"javalib/arm/boot.oat",
 			"javalib/arm/boot.vdex",
@@ -592,9 +591,8 @@
 			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
 		).RunTest(t)
 
-		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+		ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
 			"etc/boot-image.prof",
-			"etc/classpaths/bootclasspath.pb",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
 		})
diff --git a/apex/bp2build.go b/apex/bp2build.go
index d28f512..a3dda83 100644
--- a/apex/bp2build.go
+++ b/apex/bp2build.go
@@ -15,16 +15,22 @@
 
 import (
 	"android/soong/android"
+	"encoding/json"
 	"strings"
 )
 
 // This file contains the bp2build integration for the apex package.
 
 // Export constants as Starlark using bp2build to Bazel.
-func BazelApexToolchainVars() string {
+func BazelApexToolchainVars() (string, error) {
+	marshalled, err := json.Marshal(apexAvailBaseline)
+	if err != nil {
+		return "", err
+	}
 	content := []string{
 		"# GENERATED BY SOONG. DO NOT EDIT.",
 		"default_manifest_version = " + android.DefaultUpdatableModuleVersion, // constants.go is different in every branch.
+		"apex_available_baseline = json.decode('''" + string(marshalled) + "''')",
 	}
-	return strings.Join(content, "\n")
+	return strings.Join(content, "\n"), nil
 }
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 01afa52..2a0f6e9 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -25,6 +25,7 @@
 apex_key{
 	name: "foo_key",
 }
+
 apex {
 	name: "foo",
 	key: "foo_key",
@@ -42,6 +43,7 @@
 				OutputBaseDir: outputBaseDir,
 				LabelToApexInfo: map[string]cquery.ApexInfo{
 					"//:foo": cquery.ApexInfo{
+						// ApexInfo Starlark provider.
 						SignedOutput:           "signed_out.apex",
 						SignedCompressedOutput: "signed_out.capex",
 						UnsignedOutput:         "unsigned_out.apex",
@@ -56,6 +58,19 @@
 						// unused
 						PackageName:  "pkg_name",
 						ProvidesLibs: []string{"a", "b"},
+
+						// ApexMkInfo Starlark provider
+						PayloadFilesInfo: []map[string]string{
+							{
+								"built_file":       "bazel-out/adbd",
+								"install_dir":      "bin",
+								"class":            "nativeExecutable",
+								"make_module_name": "adbd",
+								"basename":         "adbd",
+								"package":          "foo",
+							},
+						},
+						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
 					},
 				},
 			}
@@ -64,10 +79,12 @@
 
 	m := result.ModuleForTests("foo", "android_common_foo_image").Module()
 	ab, ok := m.(*apexBundle)
+
 	if !ok {
 		t.Fatalf("Expected module to be an apexBundle, was not")
 	}
 
+	// TODO: refactor to android.AssertStringEquals
 	if w, g := "out/bazel/execroot/__main__/public_key", ab.publicKeyFile.String(); w != g {
 		t.Errorf("Expected public key %q, got %q", w, g)
 	}
@@ -111,11 +128,141 @@
 	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
 	}
-	if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+	// make modules to be installed to system
+	if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+		t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+	}
+	if w := "LOCAL_REQUIRED_MODULES := adbd.foo c"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
 	}
 }
 
+func TestApexImageCreatesFilesInfoForMake(t *testing.T) {
+	bp := `
+apex_key{
+	name: "foo_key",
+}
+
+apex {
+	name: "foo",
+	key: "foo_key",
+	updatable: true,
+	min_sdk_version: "31",
+	file_contexts: ":myapex-file_contexts",
+	bazel_module: { label: "//:foo" },
+}`
+
+	outputBaseDir := "out/bazel"
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outputBaseDir,
+				LabelToApexInfo: map[string]cquery.ApexInfo{
+					"//:foo": {
+						// ApexInfo Starlark provider. Necessary for the test.
+						SignedOutput:     "signed_out.apex",
+						BundleKeyInfo:    []string{"public_key", "private_key"},
+						ContainerKeyInfo: []string{"container_cert", "container_private"},
+
+						// ApexMkInfo Starlark provider
+						PayloadFilesInfo: []map[string]string{
+							{
+								"arch":                  "arm64",
+								"basename":              "libcrypto.so",
+								"built_file":            "bazel-out/64/libcrypto.so",
+								"class":                 "nativeSharedLib",
+								"install_dir":           "lib64",
+								"make_module_name":      "libcrypto",
+								"package":               "foo/bar",
+								"unstripped_built_file": "bazel-out/64/unstripped_libcrypto.so",
+							},
+							{
+								"arch":             "arm",
+								"basename":         "libcrypto.so",
+								"built_file":       "bazel-out/32/libcrypto.so",
+								"class":            "nativeSharedLib",
+								"install_dir":      "lib",
+								"make_module_name": "libcrypto",
+								"package":          "foo/bar",
+							},
+							{
+								"arch":             "arm64",
+								"basename":         "adbd",
+								"built_file":       "bazel-out/adbd",
+								"class":            "nativeExecutable",
+								"install_dir":      "bin",
+								"make_module_name": "adbd",
+								"package":          "foo",
+							},
+						},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+
+	m := result.ModuleForTests("foo", "android_common_foo_image").Module()
+	ab, ok := m.(*apexBundle)
+
+	if !ok {
+		t.Fatalf("Expected module to be an apexBundle, was not")
+	}
+
+	expectedFilesInfo := []apexFile{
+		{
+			androidMkModuleName: "libcrypto",
+			builtFile:           android.PathForTesting("out/bazel/execroot/__main__/bazel-out/64/libcrypto.so"),
+			class:               nativeSharedLib,
+			customStem:          "libcrypto.so",
+			installDir:          "lib64",
+			moduleDir:           "foo/bar",
+			arch:                "arm64",
+			unstrippedBuiltFile: android.PathForTesting("out/bazel/execroot/__main__/bazel-out/64/unstripped_libcrypto.so"),
+		},
+		{
+			androidMkModuleName: "libcrypto",
+			builtFile:           android.PathForTesting("out/bazel/execroot/__main__/bazel-out/32/libcrypto.so"),
+			class:               nativeSharedLib,
+			customStem:          "libcrypto.so",
+			installDir:          "lib",
+			moduleDir:           "foo/bar",
+			arch:                "arm",
+		},
+		{
+			androidMkModuleName: "adbd",
+			builtFile:           android.PathForTesting("out/bazel/execroot/__main__/bazel-out/adbd"),
+			class:               nativeExecutable,
+			customStem:          "adbd",
+			installDir:          "bin",
+			moduleDir:           "foo",
+			arch:                "arm64",
+		},
+	}
+
+	if len(ab.filesInfo) != len(expectedFilesInfo) {
+		t.Errorf("Expected %d entries in ab.filesInfo, but got %d", len(ab.filesInfo), len(expectedFilesInfo))
+	}
+
+	for idx, f := range ab.filesInfo {
+		expected := expectedFilesInfo[idx]
+		android.AssertSame(t, "different class", expected.class, f.class)
+		android.AssertStringEquals(t, "different built file", expected.builtFile.String(), f.builtFile.String())
+		android.AssertStringEquals(t, "different custom stem", expected.customStem, f.customStem)
+		android.AssertStringEquals(t, "different install dir", expected.installDir, f.installDir)
+		android.AssertStringEquals(t, "different make module name", expected.androidMkModuleName, f.androidMkModuleName)
+		android.AssertStringEquals(t, "different moduleDir", expected.moduleDir, f.moduleDir)
+		android.AssertStringEquals(t, "different arch", expected.arch, f.arch)
+		if expected.unstrippedBuiltFile != nil {
+			if f.unstrippedBuiltFile == nil {
+				t.Errorf("expected an unstripped built file path.")
+			}
+			android.AssertStringEquals(t, "different unstripped built file", expected.unstrippedBuiltFile.String(), f.unstrippedBuiltFile.String())
+		}
+	}
+}
+
 func TestCompressedApexImageInMixedBuilds(t *testing.T) {
 	bp := `
 apex_key{
@@ -212,6 +359,7 @@
 				OutputBaseDir: outputBaseDir,
 				LabelToApexInfo: map[string]cquery.ApexInfo{
 					"//:foo": cquery.ApexInfo{
+						// ApexInfo Starlark provider
 						SignedOutput:          "signed_out.apex",
 						UnsignedOutput:        "unsigned_out.apex",
 						BundleKeyInfo:         []string{"public_key", "private_key"},
@@ -225,8 +373,12 @@
 						// unused
 						PackageName:  "pkg_name",
 						ProvidesLibs: []string{"a", "b"},
+
+						// ApexMkInfo Starlark provider
+						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
 					},
 					"//:override_foo": cquery.ApexInfo{
+						// ApexInfo Starlark provider
 						SignedOutput:          "override_signed_out.apex",
 						UnsignedOutput:        "override_unsigned_out.apex",
 						BundleKeyInfo:         []string{"override_public_key", "override_private_key"},
@@ -240,6 +392,9 @@
 						// unused
 						PackageName:  "override_pkg_name",
 						ProvidesLibs: []string{"a", "b"},
+
+						// ApexMkInfo Starlark provider
+						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
 					},
 				},
 			}
@@ -295,7 +450,12 @@
 	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
 	}
-	if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+	// make modules to be installed to system
+	if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+		t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+	}
+	if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
 	}
 }
diff --git a/apex/builder.go b/apex/builder.go
index 3b9cac0..e3b6f8e 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -40,6 +40,8 @@
 	pctx.Import("android/soong/java")
 	pctx.HostBinToolVariable("apexer", "apexer")
 	pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
+	pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
+
 	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
 	// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
 	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
@@ -146,6 +148,34 @@
 	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
 		"opt_flags", "manifest", "is_DCLA")
 
+	TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
+		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+			`(. ${out}.copy_commands) && ` +
+			`APEXER_TOOL_PATH=${tool_path} ` +
+			`${apexer_with_trim_preprocessing} ` +
+			`--apexer ${apexer} ` +
+			`--canned_fs_config ${canned_fs_config} ` +
+			`--manifest ${manifest} ` +
+			`--libs_to_trim ${libs_to_trim} ` +
+			`${image_dir} ` +
+			`${out} ` +
+			`-- ` +
+			`--include_build_info ` +
+			`--force ` +
+			`--payload_type image ` +
+			`--key ${key} ` +
+			`--file_contexts ${file_contexts} ` +
+			`${opt_flags} `,
+		CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
+			"${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
+			"${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
+			"prebuilts/sdk/current/public/android.jar"},
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "APEX ${image_dir} => ${out}",
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+		"opt_flags", "manifest", "libs_to_trim")
+
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
 			`(. ${out}.copy_commands) && ` +
@@ -211,10 +241,11 @@
 	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
 	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
 
-	// APEX name can be overridden
+	// VNDK APEX name is determined at runtime, so update "name" in apex_manifest
 	optCommands := []string{}
-	if a.properties.Apex_name != nil {
-		optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
+	if a.vndkApex {
+		apexName := vndkApexNamePrefix + a.vndkVersion(ctx.DeviceConfig())
+		optCommands = append(optCommands, "-v name "+apexName)
 	}
 
 	// Collect jniLibs. Notice that a.filesInfo is already sorted
@@ -302,6 +333,8 @@
 		ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
 	}
 
+	useFileContextsAsIs := proptools.Bool(a.properties.Use_file_contexts_as_is)
+
 	output := android.PathForModuleOut(ctx, "file_contexts")
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -313,9 +346,11 @@
 		rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output)
 		// new line
 		rule.Command().Text("echo").Text(">>").Output(output)
-		// force-label /apex_manifest.pb and / as system_file so that apexd can read them
-		rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
-		rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
+		if !useFileContextsAsIs {
+			// force-label /apex_manifest.pb and / as system_file so that apexd can read them
+			rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
+			rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
+		}
 	case flattenedApex:
 		// For flattened apexes, install path should be prepended.
 		// File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS
@@ -328,9 +363,11 @@
 		rule.Command().Text("awk").Text(`'/object_r/{printf("` + apexPath + `%s\n", $0)}'`).Input(fileContexts).Text(">").Output(output)
 		// new line
 		rule.Command().Text("echo").Text(">>").Output(output)
-		// force-label /apex_manifest.pb and / as system_file so that apexd can read them
-		rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output)
-		rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output)
+		if !useFileContextsAsIs {
+			// force-label /apex_manifest.pb and / as system_file so that apexd can read them
+			rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output)
+			rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output)
+		}
 	default:
 		panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
 	}
@@ -415,7 +452,7 @@
 func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
 	apexType := a.properties.ApexType
 	suffix := apexType.suffix()
-	apexName := proptools.StringDefault(a.properties.Apex_name, a.BaseModuleName())
+	apexName := a.BaseModuleName()
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// Step 1: copy built files to appropriate directories under the image directory
@@ -424,26 +461,13 @@
 
 	installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable()
 
-	// b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
-	// APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
-	// as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
-	// for the overriding VNDK APEXes.
-	if a.vndkApex && len(a.overridableProperties.Overrides) > 0 {
-		installSymbolFiles = false
-	}
-
-	// Avoid creating duplicate build rules for multi-installed APEXes.
-	if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
-		installSymbolFiles = false
-
-	}
 	// set of dependency module:location mappings
 	installMapSet := make(map[string]bool)
 
 	// TODO(jiyong): use the RuleBuilder
 	var copyCommands []string
 	var implicitInputs []android.Path
-	pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
+	apexDir := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
 	for _, fi := range a.filesInfo {
 		destPath := imageDir.Join(ctx, fi.path()).String()
 		// Prepare the destination path
@@ -458,8 +482,7 @@
 		// Copy the built file to the directory. But if the symlink optimization is turned
 		// on, place a symlink to the corresponding file in /system partition instead.
 		if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
-			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
-			pathOnDevice := filepath.Join("/system", fi.path())
+			pathOnDevice := filepath.Join("/", fi.partition, fi.path())
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
 			// Copy the file into APEX
@@ -473,12 +496,12 @@
 					fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
 						fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
 				if installSymbolFiles {
-					installedPath = ctx.InstallFileWithExtraFilesZip(pathWhenActivated.Join(ctx, fi.installDir),
+					installedPath = ctx.InstallFileWithExtraFilesZip(apexDir.Join(ctx, fi.installDir),
 						fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs())
 				}
 			} else {
 				if installSymbolFiles {
-					installedPath = ctx.InstallFile(pathWhenActivated.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
+					installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
 				}
 			}
 			implicitInputs = append(implicitInputs, fi.builtFile)
@@ -492,7 +515,7 @@
 				symlinkDest := imageDir.Join(ctx, symlinkPath).String()
 				copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
 				if installSymbolFiles {
-					installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
+					installedSymlink := ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
 					implicitInputs = append(implicitInputs, installedSymlink)
 				}
 			}
@@ -519,14 +542,14 @@
 	}
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
 	if installSymbolFiles {
-		installedManifest := ctx.InstallFile(pathWhenActivated, "apex_manifest.pb", a.manifestPbOut)
-		installedKey := ctx.InstallFile(pathWhenActivated, "apex_pubkey", a.publicKeyFile)
+		installedManifest := ctx.InstallFile(apexDir, "apex_manifest.pb", a.manifestPbOut)
+		installedKey := ctx.InstallFile(apexDir, "apex_pubkey", a.publicKeyFile)
 		implicitInputs = append(implicitInputs, installedManifest, installedKey)
 	}
 
 	if len(installMapSet) > 0 {
 		var installs []string
-		installs = append(installs, android.SortedStringKeys(installMapSet)...)
+		installs = append(installs, android.SortedKeys(installMapSet)...)
 		a.SetLicenseInstallMap(installs)
 	}
 
@@ -676,12 +699,6 @@
 			optFlags = append(optFlags, "--unsigned_payload")
 		}
 
-		if a.properties.Apex_name != nil {
-			// If apex_name is set, apexer can skip checking if key name matches with
-			// apex name.  Note that apex_manifest is also mended.
-			optFlags = append(optFlags, "--do_not_check_keyname")
-		}
-
 		if moduleMinSdkVersion == android.SdkVersion_Android10 {
 			implicitInputs = append(implicitInputs, a.manifestJsonOut)
 			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
@@ -706,6 +723,24 @@
 					"opt_flags":        strings.Join(optFlags, " "),
 				},
 			})
+		} else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        TrimmedApexRule,
+				Implicits:   implicitInputs,
+				Output:      unsignedOutputFile,
+				Description: "apex (" + apexType.name() + ")",
+				Args: map[string]string{
+					"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+					"image_dir":        imageDir.String(),
+					"copy_commands":    strings.Join(copyCommands, " && "),
+					"manifest":         a.manifestPbOut.String(),
+					"file_contexts":    fileContexts.String(),
+					"canned_fs_config": cannedFsConfig.String(),
+					"key":              a.privateKeyFile.String(),
+					"opt_flags":        strings.Join(optFlags, " "),
+					"libs_to_trim":     strings.Join(a.libs_to_trim(ctx), ","),
+				},
+			})
 		} else {
 			ctx.Build(pctx, android.BuildParams{
 				Rule:        apexRule,
@@ -911,8 +946,7 @@
 			dir := filepath.Join("apex", bundleName, fi.installDir)
 			installDir := android.PathForModuleInstall(ctx, dir)
 			if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
-				// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
-				pathOnDevice := filepath.Join("/system", fi.path())
+				pathOnDevice := filepath.Join("/", fi.partition, fi.path())
 				installedSymlinks = append(installedSymlinks,
 					ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice))
 			} else {
@@ -970,7 +1004,7 @@
 	if a.vndkApex {
 		overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
 		if overridden {
-			return strings.Replace(*a.properties.Apex_name, vndkApexName, overrideName, 1)
+			return overrideName + ".v" + a.vndkVersion(ctx.DeviceConfig())
 		}
 		return ""
 	}
@@ -1029,10 +1063,10 @@
 		} else {
 			toMinSdkVersion := "(no version)"
 			if m, ok := to.(interface {
-				MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+				MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
 			}); ok {
-				if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
-					toMinSdkVersion = v.ApiLevel.String()
+				if v := m.MinSdkVersion(ctx); !v.IsNone() {
+					toMinSdkVersion = v.String()
 				}
 			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
 				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
@@ -1053,7 +1087,7 @@
 		return !externalDep
 	})
 
-	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).Raw, depInfos)
+	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depInfos)
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Phony,
diff --git a/apex/metadata.go b/apex/metadata.go
new file mode 100644
index 0000000..b1dff3e
--- /dev/null
+++ b/apex/metadata.go
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package apex
+
+import (
+	"encoding/json"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+var (
+	mtctx = android.NewPackageContext("android/soong/multitree_apex")
+)
+
+func init() {
+	RegisterModulesSingleton(android.InitRegistrationContext)
+}
+
+func RegisterModulesSingleton(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("apex_multitree_singleton", multitreeAnalysisSingletonFactory)
+}
+
+var PrepareForTestWithApexMultitreeSingleton = android.FixtureRegisterWithContext(RegisterModulesSingleton)
+
+func multitreeAnalysisSingletonFactory() android.Singleton {
+	return &multitreeAnalysisSingleton{}
+}
+
+type multitreeAnalysisSingleton struct {
+	multitreeApexMetadataPath android.OutputPath
+}
+
+type ApexMultitreeMetadataEntry struct {
+	// The name of the apex.
+	Name string
+
+	// TODO: Add other properties as needed.
+}
+
+type ApexMultitreeMetadata struct {
+	// Information about the installable apexes.
+	Apexes map[string]ApexMultitreeMetadataEntry
+}
+
+func (p *multitreeAnalysisSingleton) GenerateBuildActions(context android.SingletonContext) {
+	data := ApexMultitreeMetadata{
+		Apexes: make(map[string]ApexMultitreeMetadataEntry, 0),
+	}
+	context.VisitAllModules(func(module android.Module) {
+		// If this module is not being installed, ignore it.
+		if !module.Enabled() || module.IsSkipInstall() {
+			return
+		}
+		// Actual apexes provide ApexBundleInfoProvider.
+		if _, ok := context.ModuleProvider(module, ApexBundleInfoProvider).(ApexBundleInfo); !ok {
+			return
+		}
+		bundle, ok := module.(*apexBundle)
+		if ok && !bundle.testApex && !bundle.vndkApex && bundle.primaryApexType {
+			name := module.Name()
+			entry := ApexMultitreeMetadataEntry{
+				Name: name,
+			}
+			data.Apexes[name] = entry
+		}
+	})
+	p.multitreeApexMetadataPath = android.PathForOutput(context, "multitree_apex_metadata.json")
+
+	jsonStr, err := json.Marshal(data)
+	if err != nil {
+		context.Errorf(err.Error())
+	}
+	android.WriteFileRule(context, p.multitreeApexMetadataPath, string(jsonStr))
+	// This seems cleaner, but doesn't emit the phony rule in testing.
+	// context.Phony("multitree_apex_metadata", p.multitreeApexMetadataPath)
+
+	context.Build(mtctx, android.BuildParams{
+		Rule:        blueprint.Phony,
+		Description: "phony rule for multitree_apex_metadata",
+		Inputs:      []android.Path{p.multitreeApexMetadataPath},
+		Output:      android.PathForPhony(context, "multitree_apex_metadata"),
+	})
+}
diff --git a/apex/metadata_test.go b/apex/metadata_test.go
new file mode 100644
index 0000000..f6ead42
--- /dev/null
+++ b/apex/metadata_test.go
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package apex
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func TestModulesSingleton(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithApexMultitreeSingleton,
+		java.PrepareForTestWithDexpreopt,
+		PrepareForTestWithApexBuildComponents,
+		java.FixtureConfigureApexBootJars("myapex:foo"),
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+	).RunTestWithBp(t, `
+		prebuilt_apex {
+			name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
+		}
+
+		// A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
+		// because AlwaysUsePrebuiltSdks() is true.
+		java_sdk_library_import {
+			name: "foo",
+			prefer: false,
+			shared_library: false,
+			permitted_packages: ["foo"],
+			public: {
+				jars: ["sdk_library/public/foo-stubs.jar"],
+				stub_srcs: ["sdk_library/public/foo_stub_sources"],
+				current_api: "sdk_library/public/foo.txt",
+				removed_api: "sdk_library/public/foo-removed.txt",
+				sdk_version: "current",
+			},
+			apex_available: ["myapex"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "mybootclasspath-fragment",
+			apex_available: [
+				"myapex",
+			],
+			contents: [
+				"foo",
+			],
+			hidden_api: {
+				stub_flags: "prebuilt-stub-flags.csv",
+				annotation_flags: "prebuilt-annotation-flags.csv",
+				metadata: "prebuilt-metadata.csv",
+				index: "prebuilt-index.csv",
+				all_flags: "prebuilt-all-flags.csv",
+			},
+		}
+
+		platform_bootclasspath {
+			name: "myplatform-bootclasspath",
+			fragments: [
+				{
+					apex: "myapex",
+					module:"mybootclasspath-fragment",
+				},
+			],
+		}
+`,
+	)
+
+	outputs := result.SingletonForTests("apex_multitree_singleton").AllOutputs()
+	for _, output := range outputs {
+		testingBuildParam := result.SingletonForTests("apex_multitree_singleton").Output(output)
+		switch {
+		case strings.Contains(output, "soong/multitree_apex_metadata.json"):
+			android.AssertStringEquals(t, "Invalid build rule", "android/soong/android.writeFile", testingBuildParam.Rule.String())
+			android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0)
+			android.AssertStringDoesContain(t, "Invalid output path", output, "soong/multitree_apex_metadata.json")
+
+		case strings.HasSuffix(output, "multitree_apex_metadata"):
+			android.AssertStringEquals(t, "Invalid build rule", "<builtin>:phony", testingBuildParam.Rule.String())
+			android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/multitree_apex_metadata.json")
+			android.AssertStringEquals(t, "Invalid output path", output, "multitree_apex_metadata")
+			android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
+		}
+	}
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 0997a68..cae507e 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -35,11 +35,12 @@
 		blueprint.RuleParams{
 			Command: `rm -rf "$out" && ` +
 				`${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
-				`-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` +
+				`-sdk-version=${sdk-version} -skip-sdk-check=${skip-sdk-check} -abis=${abis} ` +
+				`-screen-densities=all -extract-single ` +
 				`${in}`,
 			CommandDeps: []string{"${extract_apks}"},
 		},
-		"abis", "allow-prereleased", "sdk-version")
+		"abis", "allow-prereleased", "sdk-version", "skip-sdk-check")
 )
 
 type prebuilt interface {
@@ -845,6 +846,7 @@
 				"abis":              strings.Join(abis, ","),
 				"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(p.properties.Prerelease, defaultAllowPrerelease)),
 				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
+				"skip-sdk-check":    strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
 			},
 		})
 }
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index d037664..f94e50f 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -15,10 +15,11 @@
 package apex
 
 import (
-	"android/soong/dexpreopt"
+	"strings"
 	"testing"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 )
 
@@ -31,7 +32,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
-		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo", "myapex:bar", "myapex:baz"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
@@ -57,10 +58,36 @@
 			],
 		}
 
+		java_library {
+			name: "bar",
+			srcs: ["c.java"],
+			installable: true,
+			dex_preopt: {
+				profile: "bar-art-profile",
+			},
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["d.java"],
+			installable: true,
+			dex_preopt: {
+				profile_guided: true, // ignored
+			},
+			apex_available: [
+				"myapex",
+			],
+		}
+
 		systemserverclasspath_fragment {
 			name: "mysystemserverclasspathfragment",
 			contents: [
 				"foo",
+				"bar",
+				"baz",
 			],
 			apex_available: [
 				"myapex",
@@ -68,15 +95,24 @@
 		}
 	`)
 
-	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+	ctx := result.TestContext
+
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"etc/classpaths/systemserverclasspath.pb",
 		"javalib/foo.jar",
+		"javalib/bar.jar",
+		"javalib/bar.jar.prof",
+		"javalib/baz.jar",
 	})
 
-	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex_image", []string{
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
 	})
+
+	assertProfileGuided(t, ctx, "foo", "android_common_apex10000", false)
+	assertProfileGuided(t, ctx, "bar", "android_common_apex10000", true)
+	assertProfileGuided(t, ctx, "baz", "android_common_apex10000", false)
 }
 
 func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
@@ -186,7 +222,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
-		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo", "myapex:bar"),
 	).RunTestWithBp(t, `
 		prebuilt_apex {
 			name: "myapex",
@@ -209,11 +245,23 @@
 			],
 		}
 
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+			dex_preopt: {
+				profile_guided: true,
+			},
+			apex_available: [
+				"myapex",
+			],
+		}
+
 		prebuilt_systemserverclasspath_fragment {
 			name: "mysystemserverclasspathfragment",
 			prefer: true,
 			contents: [
 				"foo",
+				"bar",
 			],
 			apex_available: [
 				"myapex",
@@ -221,22 +269,34 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+	ctx := result.TestContext
+
+	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
 		`myapex.apex.selector`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
-	java.CheckModuleDependencies(t, result.TestContext, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
 		`myapex.deapexer`,
+		`prebuilt_bar`,
 		`prebuilt_foo`,
 	})
+
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common", []string{
+		"javalib/foo.jar",
+		"javalib/bar.jar",
+		"javalib/bar.jar.prof",
+	})
+
+	assertProfileGuided(t, ctx, "foo", "android_common_myapex", false)
+	assertProfileGuided(t, ctx, "bar", "android_common_myapex", true)
 }
 
 func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
-		dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+		dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo", "myapex:bar", "myapex:baz"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
@@ -262,10 +322,36 @@
 			],
 		}
 
+		java_library {
+			name: "bar",
+			srcs: ["c.java"],
+			dex_preopt: {
+				profile: "bar-art-profile",
+			},
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["d.java"],
+			dex_preopt: {
+				profile_guided: true, // ignored
+			},
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
 		systemserverclasspath_fragment {
 			name: "mysystemserverclasspathfragment",
 			standalone_contents: [
 				"foo",
+				"bar",
+				"baz",
 			],
 			apex_available: [
 				"myapex",
@@ -273,17 +359,26 @@
 		}
 	`)
 
-	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+	ctx := result.TestContext
+
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"etc/classpaths/systemserverclasspath.pb",
 		"javalib/foo.jar",
+		"javalib/bar.jar",
+		"javalib/bar.jar.prof",
+		"javalib/baz.jar",
 	})
+
+	assertProfileGuided(t, ctx, "foo", "android_common_apex10000", false)
+	assertProfileGuided(t, ctx, "bar", "android_common_apex10000", true)
+	assertProfileGuided(t, ctx, "baz", "android_common_apex10000", false)
 }
 
 func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
-		dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+		dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo", "myapex:bar"),
 	).RunTestWithBp(t, `
 		prebuilt_apex {
 			name: "myapex",
@@ -306,11 +401,23 @@
 			],
 		}
 
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+			dex_preopt: {
+				profile_guided: true,
+			},
+			apex_available: [
+				"myapex",
+			],
+		}
+
 		prebuilt_systemserverclasspath_fragment {
 			name: "mysystemserverclasspathfragment",
 			prefer: true,
 			standalone_contents: [
 				"foo",
+				"bar",
 			],
 			apex_available: [
 				"myapex",
@@ -318,8 +425,28 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+	ctx := result.TestContext
+
+	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
 		`myapex.deapexer`,
+		`prebuilt_bar`,
 		`prebuilt_foo`,
 	})
+
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common", []string{
+		"javalib/foo.jar",
+		"javalib/bar.jar",
+		"javalib/bar.jar.prof",
+	})
+
+	assertProfileGuided(t, ctx, "foo", "android_common_myapex", false)
+	assertProfileGuided(t, ctx, "bar", "android_common_myapex", true)
+}
+
+func assertProfileGuided(t *testing.T, ctx *android.TestContext, moduleName string, variant string, expected bool) {
+	dexpreopt := ctx.ModuleForTests(moduleName, variant).Rule("dexpreopt")
+	actual := strings.Contains(dexpreopt.RuleParams.Command, "--profile-file=")
+	if expected != actual {
+		t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
+	}
 }
diff --git a/apex/vndk.go b/apex/vndk.go
index ef3e5e1..095e89d 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -65,8 +65,21 @@
 		}
 
 		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
-		// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
-		ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
+		apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+		if err != nil {
+			mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+			return
+		}
+
+		targets := mctx.MultiTargets()
+		if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) &&
+			vndkVersion != mctx.DeviceConfig().PlatformVndkVersion() {
+			// Disable VNDK APEXes for VNDK versions less than the minimum supported API
+			// level for the primary architecture. This validation is skipped if the VNDK
+			// version matches the platform VNDK version, which can occur when the device
+			// config targets the 'current' VNDK (see `vndkVersion`).
+			ab.Disable()
+		}
 	}
 }
 
diff --git a/bazel/Android.bp b/bazel/Android.bp
index d11c78b..4709f5c 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/bazel",
     srcs: [
         "aquery.go",
+        "bazel_proxy.go",
         "configurability.go",
         "constants.go",
         "properties.go",
diff --git a/bazel/aquery.go b/bazel/aquery.go
index bc823b3..4d39e8f 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -22,10 +22,13 @@
 	"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"
-	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 )
 
 type artifactId int
@@ -103,7 +106,7 @@
 	Depfile      *string
 	OutputPaths  []string
 	SymlinkPaths []string
-	Env          []KeyValuePair
+	Env          []*analysis_v2_proto.KeyValuePair
 	Mnemonic     string
 
 	// Inputs of this build statement, either as unexpanded depsets or expanded
@@ -118,18 +121,17 @@
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
 // less readable Bazel structures (depset and path fragment).
 type aqueryArtifactHandler struct {
-	// Switches to true if any depset contains only `bazelToolsDependencySentinel`
-	bazelToolsDependencySentinelNeeded bool
 	// Maps depset id to AqueryDepset, a representation of depset which is
 	// post-processed for middleman artifact handling, unhandled artifact
 	// dropping, content hashing, etc.
 	depsetIdToAqueryDepset map[depsetId]AqueryDepset
+	emptyDepsetIds         map[depsetId]struct{}
 	// Maps content hash to AqueryDepset.
 	depsetHashToAqueryDepset map[string]AqueryDepset
 
 	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
 	// may be an expensive operation.
-	depsetHashToArtifactPathsCache map[string][]string
+	depsetHashToArtifactPathsCache sync.Map
 	// Maps artifact ids to fully expanded paths.
 	artifactIdToPath map[artifactId]string
 }
@@ -142,11 +144,11 @@
 	"%python_binary%": "python3",
 }
 
-// The file name of py3wrapper.sh, which is used by py_binary targets.
-const py3wrapperFileName = "/py3wrapper.sh"
-
-// A file to be put into depsets that are otherwise empty
-const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
+const (
+	middlemanMnemonic = "Middleman"
+	// The file name of py3wrapper.sh, which is used by py_binary targets.
+	py3wrapperFileName = "/py3wrapper.sh"
+)
 
 func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
 	m := map[K]V{}
@@ -156,18 +158,18 @@
 	return m
 }
 
-func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
-	pathFragments := indexBy(aqueryResult.PathFragments, func(pf pathFragment) pathFragmentId {
-		return pf.Id
+func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
+	pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
+		return pathFragmentId(pf.Id)
 	})
 
-	artifactIdToPath := map[artifactId]string{}
+	artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
 	for _, artifact := range aqueryResult.Artifacts {
-		artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+		artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
 		if err != nil {
 			return nil, err
 		}
-		artifactIdToPath[artifact.Id] = artifactPath
+		artifactIdToPath[artifactId(artifact.Id)] = artifactPath
 	}
 
 	// Map middleman artifact ContentHash to input artifact depset ID.
@@ -175,23 +177,24 @@
 	// if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
 	// for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
 	// that action instead.
-	middlemanIdToDepsetIds := map[artifactId][]depsetId{}
+	middlemanIdToDepsetIds := map[artifactId][]uint32{}
 	for _, actionEntry := range aqueryResult.Actions {
-		if actionEntry.Mnemonic == "Middleman" {
+		if actionEntry.Mnemonic == middlemanMnemonic {
 			for _, outputId := range actionEntry.OutputIds {
-				middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds
+				middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
 			}
 		}
 	}
 
-	depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d depSetOfFiles) depsetId {
-		return d.Id
+	depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
+		return depsetId(d.Id)
 	})
 
 	aqueryHandler := aqueryArtifactHandler{
 		depsetIdToAqueryDepset:         map[depsetId]AqueryDepset{},
 		depsetHashToAqueryDepset:       map[string]AqueryDepset{},
-		depsetHashToArtifactPathsCache: map[string][]string{},
+		depsetHashToArtifactPathsCache: sync.Map{},
+		emptyDepsetIds:                 make(map[depsetId]struct{}, 0),
 		artifactIdToPath:               artifactIdToPath,
 	}
 
@@ -208,24 +211,26 @@
 
 // Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
 // depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
-	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
-		return aqueryDepset, nil
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
+	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
+		return &aqueryDepset, nil
 	}
 	transitiveDepsetIds := depset.TransitiveDepSetIds
-	var directArtifactPaths []string
-	for _, artifactId := range depset.DirectArtifactIds {
-		path, pathExists := a.artifactIdToPath[artifactId]
+	directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
+	for _, id := range depset.DirectArtifactIds {
+		aId := artifactId(id)
+		path, pathExists := a.artifactIdToPath[aId]
 		if !pathExists {
-			return AqueryDepset{}, fmt.Errorf("undefined input artifactId %d", artifactId)
+			return nil, fmt.Errorf("undefined input artifactId %d", aId)
 		}
 		// Filter out any inputs which are universally dropped, and swap middleman
 		// artifacts with their corresponding depsets.
-		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[artifactId]; isMiddleman {
+		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
 			// Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
 			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
 		} else if strings.HasSuffix(path, py3wrapperFileName) ||
 			strings.HasPrefix(path, "../bazel_tools") {
+			continue
 			// Drop these artifacts.
 			// See go/python-binary-host-mixed-build for more details.
 			// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
@@ -237,43 +242,48 @@
 		}
 	}
 
-	var childDepsetHashes []string
-	for _, childDepsetId := range transitiveDepsetIds {
+	childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
+	for _, id := range transitiveDepsetIds {
+		childDepsetId := depsetId(id)
 		childDepset, exists := depsetIdToDepset[childDepsetId]
 		if !exists {
-			return AqueryDepset{}, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+			if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
+				continue
+			} else {
+				return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+			}
 		}
-		childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset)
-		if err != nil {
-			return AqueryDepset{}, err
+		if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
+			return nil, err
+		} else if childAqueryDepset == nil {
+			continue
+		} else {
+			childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
 		}
-		childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
 	}
 	if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
-		// We could omit this depset altogether but that requires cleanup on
-		// transitive dependents.
-		// As a simpler alternative, we use this sentinel file as a dependency.
-		directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
-		a.bazelToolsDependencySentinelNeeded = true
+		a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
+		return nil, nil
 	}
 	aqueryDepset := AqueryDepset{
 		ContentHash:            depsetContentHash(directArtifactPaths, childDepsetHashes),
 		DirectArtifacts:        directArtifactPaths,
 		TransitiveDepSetHashes: childDepsetHashes,
 	}
-	a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
+	a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
 	a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
-	return aqueryDepset, nil
+	return &aqueryDepset, nil
 }
 
 // getInputPaths flattens the depsets of the given IDs and returns all transitive
 // input paths contained in these depsets.
 // This is a potentially expensive operation, and should not be invoked except
 // for actions which need specialized input handling.
-func (a *aqueryArtifactHandler) getInputPaths(depsetIds []depsetId) ([]string, error) {
+func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
 	var inputPaths []string
 
-	for _, inputDepSetId := range depsetIds {
+	for _, id := range depsetIds {
+		inputDepSetId := depsetId(id)
 		depset := a.depsetIdToAqueryDepset[inputDepSetId]
 		inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
 		if err != nil {
@@ -288,8 +298,8 @@
 }
 
 func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
-	if result, exists := a.depsetHashToArtifactPathsCache[depsetHash]; exists {
-		return result, nil
+	if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
+		return result.([]string), nil
 	}
 	if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
 		result := depset.DirectArtifacts
@@ -300,7 +310,7 @@
 			}
 			result = append(result, childArtifactIds...)
 		}
-		a.depsetHashToArtifactPathsCache[depsetHash] = result
+		a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
 		return result, nil
 	} else {
 		return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
@@ -312,156 +322,104 @@
 // action graph, as described by the given action graph json proto.
 // BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
 // are one-to-one with Bazel's depSetOfFiles objects.
-func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDepset, error) {
+func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
 	aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
 	err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
 	if err != nil {
 		return nil, nil, err
 	}
-	aqueryResult := actionGraphContainer{}
 
-	for _, protoArtifact := range aqueryProto.Artifacts {
-		aqueryResult.Artifacts = append(aqueryResult.Artifacts, artifact{artifactId(protoArtifact.Id),
-			pathFragmentId(protoArtifact.PathFragmentId)})
+	var aqueryHandler *aqueryArtifactHandler
+	{
+		eventHandler.Begin("init_handler")
+		defer eventHandler.End("init_handler")
+		aqueryHandler, err = newAqueryHandler(aqueryProto)
+		if err != nil {
+			return nil, nil, err
+		}
 	}
 
-	for _, protoAction := range aqueryProto.Actions {
-		var environmentVariable []KeyValuePair
-		var inputDepSetIds []depsetId
-		var outputIds []artifactId
-		var substitutions []KeyValuePair
+	// allocate both length and capacity so each goroutine can write to an index independently without
+	// any need for synchronization for slice access.
+	buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
+	{
+		eventHandler.Begin("build_statements")
+		defer eventHandler.End("build_statements")
+		wg := sync.WaitGroup{}
+		var errOnce sync.Once
 
-		for _, protoEnvironmentVariable := range protoAction.EnvironmentVariables {
-			environmentVariable = append(environmentVariable, KeyValuePair{
-				protoEnvironmentVariable.Key, protoEnvironmentVariable.Value,
-			})
+		for i, actionEntry := range aqueryProto.Actions {
+			wg.Add(1)
+			go func(i int, actionEntry *analysis_v2_proto.Action) {
+				buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry)
+				if aErr != nil {
+					errOnce.Do(func() {
+						err = aErr
+					})
+				} else {
+					// set build statement at an index rather than appending such that each goroutine does not
+					// impact other goroutines
+					buildStatements[i] = buildStatement
+				}
+				wg.Done()
+			}(i, actionEntry)
 		}
-		for _, protoInputDepSetIds := range protoAction.InputDepSetIds {
-			inputDepSetIds = append(inputDepSetIds, depsetId(protoInputDepSetIds))
-		}
-		for _, protoOutputIds := range protoAction.OutputIds {
-			outputIds = append(outputIds, artifactId(protoOutputIds))
-		}
-		for _, protoSubstitutions := range protoAction.Substitutions {
-			substitutions = append(substitutions, KeyValuePair{
-				protoSubstitutions.Key, protoSubstitutions.Value,
-			})
-		}
-
-		aqueryResult.Actions = append(aqueryResult.Actions,
-			action{
-				Arguments:            protoAction.Arguments,
-				EnvironmentVariables: environmentVariable,
-				InputDepSetIds:       inputDepSetIds,
-				Mnemonic:             protoAction.Mnemonic,
-				OutputIds:            outputIds,
-				TemplateContent:      protoAction.TemplateContent,
-				Substitutions:        substitutions,
-				FileContents:         protoAction.FileContents})
+		wg.Wait()
 	}
-
-	for _, protoDepSetOfFiles := range aqueryProto.DepSetOfFiles {
-		var directArtifactIds []artifactId
-		var transitiveDepSetIds []depsetId
-
-		for _, protoDirectArtifactIds := range protoDepSetOfFiles.DirectArtifactIds {
-			directArtifactIds = append(directArtifactIds, artifactId(protoDirectArtifactIds))
-		}
-		for _, protoTransitiveDepSetIds := range protoDepSetOfFiles.TransitiveDepSetIds {
-			transitiveDepSetIds = append(transitiveDepSetIds, depsetId(protoTransitiveDepSetIds))
-		}
-		aqueryResult.DepSetOfFiles = append(aqueryResult.DepSetOfFiles,
-			depSetOfFiles{
-				Id:                  depsetId(protoDepSetOfFiles.Id),
-				DirectArtifactIds:   directArtifactIds,
-				TransitiveDepSetIds: transitiveDepSetIds})
-
-	}
-
-	for _, protoPathFragments := range aqueryProto.PathFragments {
-		aqueryResult.PathFragments = append(aqueryResult.PathFragments,
-			pathFragment{
-				Id:       pathFragmentId(protoPathFragments.Id),
-				Label:    protoPathFragments.Label,
-				ParentId: pathFragmentId(protoPathFragments.ParentId)})
-
-	}
-	aqueryHandler, err := newAqueryHandler(aqueryResult)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	var buildStatements []BuildStatement
-	if aqueryHandler.bazelToolsDependencySentinelNeeded {
-		buildStatements = append(buildStatements, BuildStatement{
-			Command:     fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
-			OutputPaths: []string{bazelToolsDependencySentinel},
-			Mnemonic:    bazelToolsDependencySentinel,
-		})
-	}
-
-	for _, actionEntry := range aqueryResult.Actions {
-		if shouldSkipAction(actionEntry) {
-			continue
-		}
-
-		var buildStatement BuildStatement
-		if actionEntry.isSymlinkAction() {
-			buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
-		} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
-			buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
-		} else if actionEntry.isFileWriteAction() {
-			buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
-		} else if actionEntry.isSymlinkTreeAction() {
-			buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
-		} else if len(actionEntry.Arguments) < 1 {
-			return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
-		} else {
-			buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
-		}
-
-		if err != nil {
-			return nil, nil, err
-		}
-		buildStatements = append(buildStatements, buildStatement)
-	}
-
 	depsetsByHash := map[string]AqueryDepset{}
-	var depsets []AqueryDepset
-	for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
-		if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
-			// Two depsets collide on hash. Ensure that their contents are identical.
-			if !reflect.DeepEqual(aqueryDepset, prevEntry) {
-				return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
+	depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
+	{
+		eventHandler.Begin("depsets")
+		defer eventHandler.End("depsets")
+		for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
+			if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
+				// Two depsets collide on hash. Ensure that their contents are identical.
+				if !reflect.DeepEqual(aqueryDepset, prevEntry) {
+					return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
+				}
+			} else {
+				depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
+				depsets = append(depsets, aqueryDepset)
 			}
-		} else {
-			depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
-			depsets = append(depsets, aqueryDepset)
 		}
 	}
 
-	// Build Statements and depsets must be sorted by their content hash to
-	// preserve determinism between builds (this will result in consistent ninja file
-	// output). Note they are not sorted by their original IDs nor their Bazel ordering,
-	// as Bazel gives nondeterministic ordering / identifiers in aquery responses.
-	sort.Slice(buildStatements, func(i, j int) bool {
-		// For build statements, compare output lists. In Bazel, each output file
-		// may only have one action which generates it, so this will provide
-		// a deterministic ordering.
-		outputs_i := buildStatements[i].OutputPaths
-		outputs_j := buildStatements[j].OutputPaths
-		if len(outputs_i) != len(outputs_j) {
-			return len(outputs_i) < len(outputs_j)
-		}
-		if len(outputs_i) == 0 {
-			// No outputs for these actions, so compare commands.
-			return buildStatements[i].Command < buildStatements[j].Command
-		}
-		// There may be multiple outputs, but the output ordering is deterministic.
-		return outputs_i[0] < outputs_j[0]
+	eventHandler.Do("build_statement_sort", func() {
+		// Build Statements and depsets must be sorted by their content hash to
+		// preserve determinism between builds (this will result in consistent ninja file
+		// output). Note they are not sorted by their original IDs nor their Bazel ordering,
+		// as Bazel gives nondeterministic ordering / identifiers in aquery responses.
+		sort.Slice(buildStatements, func(i, j int) bool {
+			// Sort all nil statements to the end of the slice
+			if buildStatements[i] == nil {
+				return false
+			} else if buildStatements[j] == nil {
+				return true
+			}
+			//For build statements, compare output lists. In Bazel, each output file
+			// may only have one action which generates it, so this will provide
+			// a deterministic ordering.
+			outputs_i := buildStatements[i].OutputPaths
+			outputs_j := buildStatements[j].OutputPaths
+			if len(outputs_i) != len(outputs_j) {
+				return len(outputs_i) < len(outputs_j)
+			}
+			if len(outputs_i) == 0 {
+				// No outputs for these actions, so compare commands.
+				return buildStatements[i].Command < buildStatements[j].Command
+			}
+			// There may be multiple outputs, but the output ordering is deterministic.
+			return outputs_i[0] < outputs_j[0]
+		})
 	})
-	sort.Slice(depsets, func(i, j int) bool {
-		return depsets[i].ContentHash < depsets[j].ContentHash
+	eventHandler.Do("depset_sort", func() {
+		sort.Slice(depsets, func(i, j int) bool {
+			return depsets[i].ContentHash < depsets[j].ContentHash
+		})
 	})
 	return buildStatements, depsets, nil
 }
@@ -480,11 +438,14 @@
 	return fullHash
 }
 
-func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []depsetId) ([]string, error) {
+func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
 	var hashes []string
-	for _, depsetId := range inputDepsetIds {
-		if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
-			return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+	for _, id := range inputDepsetIds {
+		dId := depsetId(id)
+		if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
+			if _, empty := a.emptyDepsetIds[dId]; !empty {
+				return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
+			}
 		} else {
 			hashes = append(hashes, aqueryDepset.ContentHash)
 		}
@@ -492,18 +453,18 @@
 	return hashes, nil
 }
 
-func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
 	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 
-	buildStatement := BuildStatement{
+	buildStatement := &BuildStatement{
 		Command:           command,
 		Depfile:           depfile,
 		OutputPaths:       outputPaths,
@@ -514,13 +475,13 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	if len(outputPaths) != 1 {
-		return BuildStatement{}, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
+		return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
 	}
 	expandedTemplateContent := expandTemplateContent(actionEntry)
 	// The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
@@ -532,10 +493,10 @@
 		escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
 	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 
-	buildStatement := BuildStatement{
+	buildStatement := &BuildStatement{
 		Command:           command,
 		Depfile:           depfile,
 		OutputPaths:       outputPaths,
@@ -546,16 +507,16 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, _, err := a.getOutputPaths(actionEntry)
 	var depsetHashes []string
 	if err == nil {
 		depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
 	}
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
-	return BuildStatement{
+	return &BuildStatement{
 		Depfile:           nil,
 		OutputPaths:       outputPaths,
 		Env:               actionEntry.EnvironmentVariables,
@@ -565,20 +526,20 @@
 	}, nil
 }
 
-func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, _, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	if len(inputPaths) != 1 || len(outputPaths) != 1 {
-		return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+		return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
 	}
 	// The actual command is generated in bazelSingleton.GenerateBuildActions
-	return BuildStatement{
+	return &BuildStatement{
 		Depfile:     nil,
 		OutputPaths: outputPaths,
 		Env:         actionEntry.EnvironmentVariables,
@@ -587,18 +548,18 @@
 	}, nil
 }
 
-func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 
 	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	if len(inputPaths) != 1 || len(outputPaths) != 1 {
-		return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+		return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
 	}
 	out := outputPaths[0]
 	outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
@@ -608,7 +569,7 @@
 	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
 	symlinkPaths := outputPaths[:]
 
-	buildStatement := BuildStatement{
+	buildStatement := &BuildStatement{
 		Command:      command,
 		Depfile:      depfile,
 		OutputPaths:  outputPaths,
@@ -620,9 +581,9 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths []string, depfile *string, err error) {
+func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
 	for _, outputId := range actionEntry.OutputIds {
-		outputPath, exists := a.artifactIdToPath[outputId]
+		outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
 		if !exists {
 			err = fmt.Errorf("undefined outputId %d", outputId)
 			return
@@ -643,70 +604,69 @@
 }
 
 // expandTemplateContent substitutes the tokens in a template.
-func expandTemplateContent(actionEntry action) string {
-	var replacerString []string
-	for _, pair := range actionEntry.Substitutions {
+func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
+	replacerString := make([]string, len(actionEntry.Substitutions)*2)
+	for i, pair := range actionEntry.Substitutions {
 		value := pair.Value
 		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
 			value = val
 		}
-		replacerString = append(replacerString, pair.Key, value)
+		replacerString[i*2] = pair.Key
+		replacerString[i*2+1] = value
 	}
 	replacer := strings.NewReplacer(replacerString...)
 	return replacer.Replace(actionEntry.TemplateContent)
 }
 
+// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
+var commandLineArgumentReplacer = strings.NewReplacer(
+	`\`, `\\`,
+	`$`, `\$`,
+	"`", "\\`",
+	`"`, `\"`,
+	"\n", "\\n",
+	`'`, `'"'"'`,
+)
+
 func escapeCommandlineArgument(str string) string {
-	// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
-	replacer := strings.NewReplacer(
-		`\`, `\\`,
-		`$`, `\$`,
-		"`", "\\`",
-		`"`, `\"`,
-		"\n", "\\n",
-		`'`, `'"'"'`,
-	)
-	return replacer.Replace(str)
+	return commandLineArgumentReplacer.Replace(str)
 }
 
-func (a action) isSymlinkAction() bool {
-	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
-}
-
-func (a action) isTemplateExpandAction() bool {
-	return a.Mnemonic == "TemplateExpand"
-}
-
-func (a action) isFileWriteAction() bool {
-	return a.Mnemonic == "FileWrite" || a.Mnemonic == "SourceSymlinkManifest"
-}
-
-func (a action) isSymlinkTreeAction() bool {
-	return a.Mnemonic == "SymlinkTree"
-}
-
-func shouldSkipAction(a action) bool {
+func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+	switch actionEntry.Mnemonic {
 	// Middleman actions are not handled like other actions; they are handled separately as a
 	// preparatory step so that their inputs may be relayed to actions depending on middleman
 	// artifacts.
-	if a.Mnemonic == "Middleman" {
-		return true
-	}
+	case middlemanMnemonic:
+		return nil, nil
 	// PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
-	if a.Mnemonic == "PythonZipper" {
-		return true
-	}
+	case "PythonZipper":
+		return nil, nil
 	// Skip "Fail" actions, which are placeholder actions designed to always fail.
-	if a.Mnemonic == "Fail" {
-		return true
+	case "Fail":
+		return nil, nil
+	case "BaselineCoverage":
+		return nil, nil
+	case "Symlink", "SolibSymlink", "ExecutableSymlink":
+		return a.symlinkActionBuildStatement(actionEntry)
+	case "TemplateExpand":
+		if len(actionEntry.Arguments) < 1 {
+			return a.templateExpandActionBuildStatement(actionEntry)
+		}
+	case "FileWrite", "SourceSymlinkManifest":
+		return a.fileWriteActionBuildStatement(actionEntry)
+	case "SymlinkTree":
+		return a.symlinkTreeActionBuildStatement(actionEntry)
 	}
-	if a.Mnemonic == "BaselineCoverage" {
-		return true
+
+	if len(actionEntry.Arguments) < 1 {
+		return nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
 	}
-	return false
+	return a.normalActionBuildStatement(actionEntry)
+
 }
 
-func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]pathFragment) (string, error) {
+func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
 	var labels []string
 	currId := id
 	// Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
@@ -716,10 +676,11 @@
 			return "", fmt.Errorf("undefined path fragment id %d", currId)
 		}
 		labels = append([]string{currFragment.Label}, labels...)
-		if currId == currFragment.ParentId {
+		parentId := pathFragmentId(currFragment.ParentId)
+		if currId == parentId {
 			return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
 		}
-		currId = currFragment.ParentId
+		currId = parentId
 	}
 	return filepath.Join(labels...), nil
 }
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 2eacafa..19a584f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -21,8 +21,10 @@
 	"sort"
 	"testing"
 
-	"google.golang.org/protobuf/proto"
 	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+
+	"github.com/google/blueprint/metrics"
+	"google.golang.org/protobuf/proto"
 )
 
 func TestAqueryMultiArchGenrule(t *testing.T) {
@@ -136,18 +138,18 @@
 		t.Error(err)
 		return
 	}
-	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
-	var expectedBuildStatements []BuildStatement
+	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+	var expectedBuildStatements []*BuildStatement
 	for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
 		expectedBuildStatements = append(expectedBuildStatements,
-			BuildStatement{
+			&BuildStatement{
 				Command: fmt.Sprintf(
 					"/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
 					arch, arch),
 				OutputPaths: []string{
 					fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
 				},
-				Env: []KeyValuePair{
+				Env: []*analysis_v2_proto.KeyValuePair{
 					{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
 				},
 				Mnemonic: "Genrule",
@@ -195,7 +197,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, "undefined outputId 3")
 }
 
@@ -226,8 +228,8 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
-	assertError(t, err, "undefined input depsetId 2")
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
+	assertError(t, err, "undefined (not even empty) input depsetId 2")
 }
 
 func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
@@ -257,7 +259,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
 }
 
@@ -288,7 +290,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, "undefined input artifactId 3")
 }
 
@@ -319,7 +321,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, "undefined path fragment id 3")
 }
 
@@ -352,7 +354,7 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
@@ -402,7 +404,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
 }
 
@@ -483,13 +485,14 @@
 		t.Error(err)
 		return
 	}
-	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
+	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
 
-	expectedBuildStatements := []BuildStatement{
-		{
-			Command:     "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
-			OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
-			Mnemonic:    "Action",
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
+			Command:      "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
+			OutputPaths:  []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
+			Mnemonic:     "Action",
+			SymlinkPaths: []string{},
 		},
 	}
 	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
@@ -538,16 +541,17 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, []BuildStatement{
-		{
-			Command:     "",
-			OutputPaths: []string{"foo.runfiles/MANIFEST"},
-			Mnemonic:    "SymlinkTree",
-			InputPaths:  []string{"foo.manifest"},
+	assertBuildStatements(t, []*BuildStatement{
+		&BuildStatement{
+			Command:      "",
+			OutputPaths:  []string{"foo.runfiles/MANIFEST"},
+			Mnemonic:     "SymlinkTree",
+			InputPaths:   []string{"foo.manifest"},
+			SymlinkPaths: []string{},
 		},
 	}, actual)
 }
@@ -584,13 +588,18 @@
    { "id": 60, "label": ".."}
  ]
 }`
+	/* depsets
+	       1111  2222
+	       /  \   |
+	../dep2    ../bazel_tools/dep1
+	*/
 	data, err := JsonToActionGraphContainer(inputString)
 	if err != nil {
 		t.Error(err)
 		return
 	}
-	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
-	if len(actualDepsets) != 2 {
+	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if len(actualDepsets) != 1 {
 		t.Errorf("expected 1 depset but found %#v", actualDepsets)
 		return
 	}
@@ -606,7 +615,84 @@
 		t.Errorf("dependency ../dep2 expected but not found")
 	}
 
-	expectedBuildStatement := BuildStatement{
+	expectedBuildStatement := &BuildStatement{
+		Command:      "bogus command",
+		OutputPaths:  []string{"output"},
+		Mnemonic:     "x",
+		SymlinkPaths: []string{},
+	}
+	buildStatementFound := false
+	for _, actualBuildStatement := range actualBuildStatements {
+		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+			buildStatementFound = true
+			break
+		}
+	}
+	if !buildStatementFound {
+		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+		return
+	}
+}
+
+func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
+	const inputString = `{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 10 },
+   { "id": 2, "path_fragment_id": 20 },
+   { "id": 3, "path_fragment_id": 30 }],
+ "dep_set_of_files": [{
+   "id": 1111,
+   "transitive_dep_set_ids": [2222]
+ }, {
+   "id": 2222,
+   "direct_artifact_ids": [3]
+ }, {
+   "id": 3333,
+   "direct_artifact_ids": [3]
+ }, {
+   "id": 4444,
+   "transitive_dep_set_ids": [3333]
+ }],
+ "actions": [{
+   "target_id": 100,
+   "action_key": "x",
+   "input_dep_set_ids": [1111, 4444],
+   "mnemonic": "x",
+   "arguments": ["bogus", "command"],
+   "output_ids": [2],
+   "primary_output_id": 1
+ }],
+ "path_fragments": [
+   { "id": 10, "label": "input" },
+   { "id": 20, "label": "output" },
+   { "id": 30, "label": "dep", "parent_id": 50 },
+   { "id": 50, "label": "bazel_tools", "parent_id": 60 },
+   { "id": 60, "label": ".."}
+ ]
+}`
+	/* depsets
+	    1111    4444
+	     ||      ||
+	    2222    3333
+	      |      |
+	../bazel_tools/dep
+	Note: in dep_set_of_files:
+	  1111 appears BEFORE its dependency,2222 while
+	  4444 appears AFTER its dependency 3333
+	and this test shows that that order doesn't affect empty depset pruning
+	*/
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if len(actualDepsets) != 0 {
+		t.Errorf("expected 0 depsets but found %#v", actualDepsets)
+		return
+	}
+
+	expectedBuildStatement := &BuildStatement{
 		Command:     "bogus command",
 		OutputPaths: []string{"output"},
 		Mnemonic:    "x",
@@ -667,12 +753,12 @@
 		t.Error(err)
 		return
 	}
-	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data)
+	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	if expected := 1; len(actualBuildStatements) != expected {
-		t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
+	if expected := 2; len(actualBuildStatements) != expected {
+		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
 	}
 
 	expectedDepsetFiles := [][]string{
@@ -697,6 +783,11 @@
 	if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
 		t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
 	}
+
+	bs = actualBuildStatements[1]
+	if bs != nil {
+		t.Errorf("Expected nil action for skipped")
+	}
 }
 
 // Returns the contents of given depsets in concatenated post order.
@@ -764,14 +855,14 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
 
-	expectedBuildStatements := []BuildStatement{
-		{
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
 			Command: "mkdir -p one/symlink_subdir && " +
 				"rm -f one/symlink_subdir/symlink && " +
 				"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
@@ -813,13 +904,13 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
 
-	expectedBuildStatements := []BuildStatement{
-		{
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
 			Command: "mkdir -p 'one/symlink subdir' && " +
 				"rm -f 'one/symlink subdir/symlink' && " +
 				"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
@@ -859,7 +950,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
 }
 
@@ -890,7 +981,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, "undefined outputId 2")
 }
 
@@ -923,17 +1014,18 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
 
-	expectedBuildStatements := []BuildStatement{
-		{
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
 			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
 				"chmod a+x template_file'",
-			OutputPaths: []string{"template_file"},
-			Mnemonic:    "TemplateExpand",
+			OutputPaths:  []string{"template_file"},
+			Mnemonic:     "TemplateExpand",
+			SymlinkPaths: []string{},
 		},
 	}
 	assertBuildStatements(t, expectedBuildStatements, actual)
@@ -965,7 +1057,7 @@
 		t.Error(err)
 		return
 	}
-	_, _, err = AqueryBuildStatements(data)
+	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
 	assertError(t, err, `Expect 1 output to template expand action, got: output []`)
 }
 
@@ -993,15 +1085,16 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, []BuildStatement{
-		{
+	assertBuildStatements(t, []*BuildStatement{
+		&BuildStatement{
 			OutputPaths:  []string{"foo.manifest"},
 			Mnemonic:     "FileWrite",
 			FileContents: "file data\n",
+			SymlinkPaths: []string{},
 		},
 	}, actual)
 }
@@ -1030,14 +1123,15 @@
 		t.Error(err)
 		return
 	}
-	actual, _, err := AqueryBuildStatements(data)
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, []BuildStatement{
-		{
-			OutputPaths: []string{"foo.manifest"},
-			Mnemonic:    "SourceSymlinkManifest",
+	assertBuildStatements(t, []*BuildStatement{
+		&BuildStatement{
+			OutputPaths:  []string{"foo.manifest"},
+			Mnemonic:     "SourceSymlinkManifest",
+			SymlinkPaths: []string{},
 		},
 	}, actual)
 }
@@ -1053,7 +1147,7 @@
 
 // Asserts that the given actual build statements match the given expected build statements.
 // Build statement equivalence is determined using buildStatementEquals.
-func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
 	t.Helper()
 	if len(expected) != len(actual) {
 		t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
@@ -1061,8 +1155,13 @@
 		return
 	}
 	type compareFn = func(i int, j int) bool
-	byCommand := func(slice []BuildStatement) compareFn {
+	byCommand := func(slice []*BuildStatement) compareFn {
 		return func(i int, j int) bool {
+			if slice[i] == nil {
+				return false
+			} else if slice[j] == nil {
+				return false
+			}
 			return slice[i].Command < slice[j].Command
 		}
 	}
@@ -1078,7 +1177,10 @@
 	}
 }
 
-func buildStatementEquals(first BuildStatement, second BuildStatement) string {
+func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
+	if (first == nil) != (second == nil) {
+		return "Nil"
+	}
 	if first.Mnemonic != second.Mnemonic {
 		return "Mnemonic"
 	}
diff --git a/bazel/bazel_proxy.go b/bazel/bazel_proxy.go
new file mode 100644
index 0000000..d7f5e64
--- /dev/null
+++ b/bazel/bazel_proxy.go
@@ -0,0 +1,219 @@
+// 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 bazel
+
+import (
+	"bytes"
+	"encoding/gob"
+	"fmt"
+	"net"
+	os_lib "os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// Logs fatal events of ProxyServer.
+type ServerLogger interface {
+	Fatal(v ...interface{})
+	Fatalf(format string, v ...interface{})
+}
+
+// CmdRequest is a request to the Bazel Proxy server.
+type CmdRequest struct {
+	// Args to the Bazel command.
+	Argv []string
+	// Environment variables to pass to the Bazel invocation. Strings should be of
+	// the form "KEY=VALUE".
+	Env []string
+}
+
+// CmdResponse is a response from the Bazel Proxy server.
+type CmdResponse struct {
+	Stdout      string
+	Stderr      string
+	ErrorString string
+}
+
+// ProxyClient is a client which can issue Bazel commands to the Bazel
+// proxy server. Requests are issued (and responses received) via a unix socket.
+// See ProxyServer for more details.
+type ProxyClient struct {
+	outDir string
+}
+
+// ProxyServer is a server which runs as a background goroutine. Each
+// request to the server describes a Bazel command which the server should run.
+// The server then issues the Bazel command, and returns a response describing
+// the stdout/stderr of the command.
+// Client-server communication is done via a unix socket under the output
+// directory.
+// The server is intended to circumvent sandboxing for subprocesses of the
+// build. The build orchestrator (soong_ui) can launch a server to exist outside
+// of sandboxing, and sandboxed processes (such as soong_build) can issue
+// bazel commands through this socket tunnel. This allows a sandboxed process
+// to issue bazel requests to a bazel that resides outside of sandbox. This
+// is particularly useful to maintain a persistent Bazel server which lives
+// past the duration of a single build.
+// The ProxyServer will only live as long as soong_ui does; the
+// underlying Bazel server will live past the duration of the build.
+type ProxyServer struct {
+	logger       ServerLogger
+	outDir       string
+	workspaceDir string
+	// The server goroutine will listen on this channel and stop handling requests
+	// once it is written to.
+	done chan struct{}
+}
+
+// NewProxyClient is a constructor for a ProxyClient.
+func NewProxyClient(outDir string) *ProxyClient {
+	return &ProxyClient{
+		outDir: outDir,
+	}
+}
+
+func unixSocketPath(outDir string) string {
+	return filepath.Join(outDir, "bazelsocket.sock")
+}
+
+// IssueCommand issues a request to the Bazel Proxy Server to issue a Bazel
+// request. Returns a response describing the output from the Bazel process
+// (if the Bazel process had an error, then the response will include an error).
+// Returns an error if there was an issue with the connection to the Bazel Proxy
+// server.
+func (b *ProxyClient) IssueCommand(req CmdRequest) (CmdResponse, error) {
+	var resp CmdResponse
+	var err error
+	// Check for connections every 1 second. This is chosen to be a relatively
+	// short timeout, because the proxy server should accept requests quite
+	// quickly.
+	d := net.Dialer{Timeout: 1 * time.Second}
+	var conn net.Conn
+	conn, err = d.Dial("unix", unixSocketPath(b.outDir))
+	if err != nil {
+		return resp, err
+	}
+	defer conn.Close()
+
+	enc := gob.NewEncoder(conn)
+	if err = enc.Encode(req); err != nil {
+		return resp, err
+	}
+	dec := gob.NewDecoder(conn)
+	err = dec.Decode(&resp)
+	return resp, err
+}
+
+// NewProxyServer is a constructor for a ProxyServer.
+func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string) *ProxyServer {
+	return &ProxyServer{
+		logger:       logger,
+		outDir:       outDir,
+		workspaceDir: workspaceDir,
+		done:         make(chan struct{}),
+	}
+}
+
+func (b *ProxyServer) handleRequest(conn net.Conn) error {
+	defer conn.Close()
+
+	dec := gob.NewDecoder(conn)
+	var req CmdRequest
+	if err := dec.Decode(&req); err != nil {
+		return fmt.Errorf("Error decoding request: %s", err)
+	}
+
+	bazelCmd := exec.Command("./build/bazel/bin/bazel", req.Argv...)
+	bazelCmd.Dir = b.workspaceDir
+	bazelCmd.Env = req.Env
+
+	stderr := &bytes.Buffer{}
+	bazelCmd.Stderr = stderr
+	var stdout string
+	var bazelErrString string
+
+	if output, err := bazelCmd.Output(); err != nil {
+		bazelErrString = fmt.Sprintf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+			err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
+	} else {
+		stdout = string(output)
+	}
+
+	resp := CmdResponse{stdout, string(stderr.Bytes()), bazelErrString}
+	enc := gob.NewEncoder(conn)
+	if err := enc.Encode(&resp); err != nil {
+		return fmt.Errorf("Error encoding response: %s", err)
+	}
+	return nil
+}
+
+func (b *ProxyServer) listenUntilClosed(listener net.Listener) error {
+	for {
+		// Check for connections every 1 second. This is a blocking operation, so
+		// if the server is closed, the goroutine will not fully close until this
+		// deadline is reached. Thus, this deadline is short (but not too short
+		// so that the routine churns).
+		listener.(*net.UnixListener).SetDeadline(time.Now().Add(time.Second))
+		conn, err := listener.Accept()
+
+		select {
+		case <-b.done:
+			return nil
+		default:
+		}
+
+		if err != nil {
+			if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
+				// Timeout is normal and expected while waiting for client to establish
+				// a connection.
+				continue
+			} else {
+				b.logger.Fatalf("Listener error: %s", err)
+			}
+		}
+
+		err = b.handleRequest(conn)
+		if err != nil {
+			b.logger.Fatal(err)
+		}
+	}
+}
+
+// Start initializes the server unix socket and (in a separate goroutine)
+// handles requests on the socket until the server is closed. Returns an error
+// if a failure occurs during initialization. Will log any post-initialization
+// errors to the server's logger.
+func (b *ProxyServer) Start() error {
+	unixSocketAddr := unixSocketPath(b.outDir)
+	if err := os_lib.RemoveAll(unixSocketAddr); err != nil {
+		return fmt.Errorf("couldn't remove socket '%s': %s", unixSocketAddr, err)
+	}
+	listener, err := net.Listen("unix", unixSocketAddr)
+
+	if err != nil {
+		return fmt.Errorf("error listening on socket '%s': %s", unixSocketAddr, err)
+	}
+
+	go b.listenUntilClosed(listener)
+	return nil
+}
+
+// Close shuts down the server. This will stop the server from listening for
+// additional requests.
+func (b *ProxyServer) Close() {
+	b.done <- struct{}{}
+}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 2b8753b..4680256 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -343,8 +343,8 @@
 }
 
 func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
-	if ca.configurationType < other.configurationType {
-		return true
+	if ca.configurationType == other.configurationType {
+		return ca.subType < other.subType
 	}
-	return ca.subType < other.subType
+	return ca.configurationType < other.configurationType
 }
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 81c60d9..bf3a6b5 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,7 +14,14 @@
 	GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
 )
 
+type CcAndroidMkInfo struct {
+	LocalStaticLibs      []string
+	LocalWholeStaticLibs []string
+	LocalSharedLibs      []string
+}
+
 type CcInfo struct {
+	CcAndroidMkInfo
 	OutputFiles          []string
 	CcObjectFiles        []string
 	CcSharedLibraryFiles []string
@@ -173,27 +180,40 @@
 tidy_files = []
 clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
 if clang_tidy_info:
-  tidy_files = [v.path for v in clang_tidy_info.tidy_files.to_list()]
+  tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
 
 abi_diff_files = []
 abi_diff_info = p.get("//build/bazel/rules/abi:abi_dump.bzl%AbiDiffInfo")
 if abi_diff_info:
   abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
 
-return json_encode({
-	"OutputFiles": outputFiles,
-	"CcObjectFiles": ccObjectFiles,
-	"CcSharedLibraryFiles": sharedLibraries,
-	"CcStaticLibraryFiles": staticLibraries,
-	"Includes": includes,
-	"SystemIncludes": system_includes,
-	"Headers": headers,
-	"RootStaticArchives": rootStaticArchives,
-	"RootDynamicLibraries": rootSharedLibraries,
-	"TidyFiles": tidy_files,
-	"TocFile": toc_file,
-	"UnstrippedOutput": unstripped,
-	"AbiDiffFiles": abi_diff_files,
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+    androidmk_info = p[androidmk_tag]
+    local_static_libs = androidmk_info.local_static_libs
+    local_whole_static_libs = androidmk_info.local_whole_static_libs
+    local_shared_libs = androidmk_info.local_shared_libs
+
+return json.encode({
+    "OutputFiles": outputFiles,
+    "CcObjectFiles": ccObjectFiles,
+    "CcSharedLibraryFiles": sharedLibraries,
+    "CcStaticLibraryFiles": staticLibraries,
+    "Includes": includes,
+    "SystemIncludes": system_includes,
+    "Headers": headers,
+    "RootStaticArchives": rootStaticArchives,
+    "RootDynamicLibraries": rootSharedLibraries,
+    "TidyFiles": [t for t in tidy_files],
+    "TocFile": toc_file,
+    "UnstrippedOutput": unstripped,
+    "AbiDiffFiles": abi_diff_files,
+    "LocalStaticLibs": [l for l in local_static_libs],
+    "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+    "LocalSharedLibs": [l for l in local_shared_libs],
 })`
 
 }
@@ -237,7 +257,16 @@
 if info.signed_compressed_output:
     signed_compressed_output = info.signed_compressed_output.path
 
-return json_encode({
+mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
+if not mk_info:
+  fail("%s did not provide ApexMkInfo" % id_string)
+
+tidy_files = []
+clang_tidy_info = providers(target).get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+    tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
+
+return json.encode({
     "signed_output": info.signed_output.path,
     "signed_compressed_output": signed_compressed_output,
     "unsigned_output": info.unsigned_output.path,
@@ -251,10 +280,14 @@
     "backing_libs": info.backing_libs.path,
     "bundle_file": info.base_with_config_zip.path,
     "installed_files": info.installed_files.path,
+    "make_modules_to_install": mk_info.make_modules_to_install,
+    "files_info": mk_info.files_info,
+    "tidy_files": [t for t in tidy_files],
 })`
 }
 
 type ApexInfo struct {
+	// From the ApexInfo provider
 	SignedOutput           string   `json:"signed_output"`
 	SignedCompressedOutput string   `json:"signed_compressed_output"`
 	UnsignedOutput         string   `json:"unsigned_output"`
@@ -268,6 +301,11 @@
 	BackingLibs            string   `json:"backing_libs"`
 	BundleFile             string   `json:"bundle_file"`
 	InstalledFiles         string   `json:"installed_files"`
+	TidyFiles              []string `json:"tidy_files"`
+
+	// From the ApexMkInfo provider
+	MakeModulesToInstall []string            `json:"make_modules_to_install"`
+	PayloadFilesInfo     []map[string]string `json:"files_info"`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -289,15 +327,38 @@
 }
 
 func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string {
-	return `unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+	return `
 p = providers(target)
 output_path = target.files.to_list()[0].path
+
 unstripped = output_path
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
 if unstripped_tag in p:
-    unstripped = p[unstripped_tag].unstripped.files.to_list()[0].path
-return json_encode({
+    unstripped_info = p[unstripped_tag]
+    unstripped = unstripped_info.unstripped[0].files.to_list()[0].path
+
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+    androidmk_info = p[androidmk_tag]
+    local_static_libs = androidmk_info.local_static_libs
+    local_whole_static_libs = androidmk_info.local_whole_static_libs
+    local_shared_libs = androidmk_info.local_shared_libs
+
+tidy_files = []
+clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+    tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
+
+return json.encode({
     "OutputFile":  output_path,
     "UnstrippedOutput": unstripped,
+    "LocalStaticLibs": [l for l in local_static_libs],
+    "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+    "LocalSharedLibs": [l for l in local_shared_libs],
+    "TidyFiles": [t for t in tidy_files],
 })
 `
 }
@@ -312,8 +373,10 @@
 }
 
 type CcUnstrippedInfo struct {
+	CcAndroidMkInfo
 	OutputFile       string
 	UnstrippedOutput string
+	TidyFiles        []string
 }
 
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 1d30535..7003ce1 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -177,9 +177,11 @@
 	"backing_libs":"path/to/backing.txt",
 	"bundle_file": "dir/bundlefile.zip",
 	"installed_files":"path/to/installed-files.txt",
-	"provides_native_libs":[]
+	"provides_native_libs":[],
+	"make_modules_to_install": ["foo","bar"]
 }`,
 			expectedOutput: ApexInfo{
+				// ApexInfo
 				SignedOutput:      "my.apex",
 				UnsignedOutput:    "my.apex.unsigned",
 				RequiresLibs:      []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
@@ -191,6 +193,9 @@
 				BackingLibs:       "path/to/backing.txt",
 				BundleFile:        "dir/bundlefile.zip",
 				InstalledFiles:    "path/to/installed-files.txt",
+
+				// ApexMkInfo
+				MakeModulesToInstall: []string{"foo", "bar"},
 			},
 		},
 	}
diff --git a/bazel/properties.go b/bazel/properties.go
index 8a6d1b0..40d0ba3 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"reflect"
 	"regexp"
 	"sort"
 	"strings"
@@ -73,6 +74,16 @@
 	}
 }
 
+func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
 // MakeLabelListFromTargetNames creates a LabelList from unqualified target names
 // This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
 func MakeLabelListFromTargetNames(targetNames []string) LabelList {
@@ -152,7 +163,7 @@
 		ll.Includes = append(ll.Includes, other.Includes...)
 	}
 	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
-		ll.Excludes = append(other.Excludes, other.Excludes...)
+		ll.Excludes = append(ll.Excludes, other.Excludes...)
 	}
 }
 
@@ -412,13 +423,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
-	for k := range la.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(la.ConfigurableValues)
 }
 
 // MakeLabelAttribute turns a string into a LabelAttribute
@@ -529,6 +534,37 @@
 	return result, nil
 }
 
+// ToStringListAttribute creates a StringListAttribute from this BoolAttribute,
+// where each bool corresponds to a string list value generated by the provided
+// function.
+// TODO(b/271425661): Generalize this
+func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) {
+	mainVal := valueFunc(ba.Value, NoConfigAxis, "")
+	if !ba.HasConfigurableValues() {
+		return MakeStringListAttribute(mainVal), nil
+	}
+
+	result := StringListAttribute{}
+	if err := ba.Collapse(); err != nil {
+		return result, err
+	}
+
+	for axis, configToBools := range ba.ConfigurableValues {
+		if len(configToBools) < 1 {
+			continue
+		}
+		for config, boolPtr := range configToBools {
+			val := valueFunc(&boolPtr, axis, config)
+			if !reflect.DeepEqual(val, mainVal) {
+				result.SetSelectValue(axis, config, val)
+			}
+		}
+		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
+	}
+
+	return result, nil
+}
+
 // Collapse reduces the configurable axes of the boolean attribute to a single axis.
 // This is necessary for final writing to bp2build, as a configurable boolean
 // attribute can only be comprised by a single select.
@@ -608,13 +644,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
-	for k := range ba.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(ba.ConfigurableValues)
 }
 
 // labelListSelectValues supports config-specific label_list typed Bazel attribute values.
@@ -761,13 +791,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
-	for k := range lla.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(lla.ConfigurableValues)
 }
 
 // Append all values, including os and arch specific ones, from another
@@ -814,6 +838,16 @@
 	return false
 }
 
+// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
+func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
+	for _, values := range lla.ConfigurableValues[axis] {
+		if !values.IsNil() {
+			return true
+		}
+	}
+	return false
+}
+
 // IsEmpty returns true if the attribute has no values under any configuration.
 func (lla LabelListAttribute) IsEmpty() bool {
 	if len(lla.Value.Includes) > 0 {
@@ -878,7 +912,7 @@
 			// then remove all config-specific excludes
 			allLabels := baseLabels.deepCopy()
 			allLabels.Append(val)
-			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
 		}
 
 		// After going through all configs, delete the duplicates in the config
@@ -1135,13 +1169,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
-	for k := range sa.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(sa.ConfigurableValues)
 }
 
 // Collapse reduces the configurable axes of the string attribute to a single axis.
@@ -1343,13 +1371,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
-	for k := range sla.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(sla.ConfigurableValues)
 }
 
 // DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 8729381..cf03eb5 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -231,6 +231,7 @@
 				"all_include",
 				"arm_exclude",
 				"android_exclude",
+				"product_config_exclude",
 			},
 			[]string{"all_exclude"},
 		),
@@ -251,10 +252,10 @@
 				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
 				"b":                        makeLabelList([]string{"b_val"}, []string{}),
 				"c":                        makeLabelList([]string{"c_val"}, []string{}),
-				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
+				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
 			},
 			ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
-				"a": makeLabelList([]string{}, []string{"not_in_value"}),
+				"a": makeLabelList([]string{}, []string{"product_config_exclude"}),
 			},
 		},
 	}
@@ -287,6 +288,10 @@
 			"c":                        makeLabels("c_val"),
 			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
 		},
+		ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+			"a":                        nilLabels,
+			ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
+		},
 	}
 	for _, axis := range attr.SortedConfigurationAxes() {
 		if _, ok := expectedConfiguredIncludes[axis]; !ok {
diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go
index df7cced..6020ee5 100644
--- a/bp2build/aar_conversion_test.go
+++ b/bp2build/aar_conversion_test.go
@@ -133,7 +133,77 @@
 						"exports": `[":static_import_dep"]`,
 					},
 				),
+				MakeNeverlinkDuplicateTarget("android_library", "TestImport"),
 			},
 		},
 	)
 }
+
+func TestConvertAndroidLibraryKotlin(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library with .kt srcs and common_srcs attribute",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Filesystem: map[string]string{
+			"AndroidManifest.xml": "",
+		},
+		Blueprint: `
+android_library {
+        name: "TestLib",
+        srcs: ["a.java", "b.kt"],
+        common_srcs: ["c.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+					"common_srcs":    `["c.kt"]`,
+					"manifest":       `"AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+			MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+		}})
+}
+
+func TestConvertAndroidLibraryKotlinCflags(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library with .kt srcs and kotlincflags ",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Filesystem: map[string]string{
+			"AndroidManifest.xml": "",
+		},
+		Blueprint: `
+android_library {
+        name: "TestLib",
+        srcs: ["a.java", "b.kt"],
+        kotlincflags: ["-flag1", "-flag2"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+					"kotlincflags": `[
+        "-flag1",
+        "-flag2",
+    ]`,
+					"manifest":       `"AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+			MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+		}})
+}
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 2b35521..ef3f124 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -28,6 +28,7 @@
 
 func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("java_library", java.LibraryFactory)
 }
 
 func TestMinimalAndroidApp(t *testing.T) {
@@ -200,3 +201,193 @@
 			}),
 		}})
 }
+
+func TestAndroidAppLibs(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with libs",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+				libs: ["barLib"]
+}
+java_library{
+       name: "barLib",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `[]`,
+				"deps":           `[":barLib-neverlink"]`,
+			}),
+		}})
+}
+
+func TestAndroidAppKotlinSrcs(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with kotlin sources and common_srcs",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem: map[string]string{
+			"res/res.png": "",
+		},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+        srcs: ["a.java", "b.kt"],
+        certificate: ":foocert",
+        manifest: "fooManifest.xml",
+        libs: ["barLib"]
+}
+java_library{
+      name:   "barLib",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+			MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"manifest":       `"fooManifest.xml"`,
+				"resource_files": `["res/res.png"]`,
+				"deps":           `[":barLib-neverlink"]`,
+			}),
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"deps":        `[":foo_kt"]`,
+				"certificate": `":foocert"`,
+				"manifest":    `"fooManifest.xml"`,
+			}),
+		}})
+}
+
+func TestAndroidAppCommonSrcs(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with common_srcs",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem: map[string]string{
+			"res/res.png": "",
+		},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+        srcs: ["a.java"],
+        common_srcs: ["b.kt"],
+        certificate: "foocert",
+        manifest: "fooManifest.xml",
+        libs:        ["barLib"],
+}
+java_library{
+      name:   "barLib",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+			MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+				"srcs":           `["a.java"]`,
+				"common_srcs":    `["b.kt"]`,
+				"manifest":       `"fooManifest.xml"`,
+				"resource_files": `["res/res.png"]`,
+				"deps":           `[":barLib-neverlink"]`,
+			}),
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"deps":             `[":foo_kt"]`,
+				"certificate_name": `"foocert"`,
+				"manifest":         `"fooManifest.xml"`,
+			}),
+		}})
+}
+
+func TestAndroidAppKotlinCflags(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with kotlincflags",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem: map[string]string{
+			"res/res.png": "",
+		},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+        srcs: ["a.java", "b.kt"],
+        certificate: ":foocert",
+        manifest: "fooManifest.xml",
+        kotlincflags: ["-flag1", "-flag2"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"manifest":       `"fooManifest.xml"`,
+				"resource_files": `["res/res.png"]`,
+				"kotlincflags": `[
+        "-flag1",
+        "-flag2",
+    ]`,
+			}),
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"deps":        `[":foo_kt"]`,
+				"certificate": `":foocert"`,
+				"manifest":    `"fooManifest.xml"`,
+			}),
+		}})
+}
+
+func TestAndroidAppMinSdkProvided(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with value for min_sdk_version",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+        sdk_version: "current",
+				min_sdk_version: "24",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `[]`,
+				"manifest_values": `{
+        "minSdkVersion": "24",
+    }`,
+			}),
+		}})
+}
+
+func TestAndroidAppMinSdkDefaultToSdkVersion(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with value for sdk_version",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+        sdk_version: "30",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `[]`,
+				"manifest_values": `{
+        "minSdkVersion": "30",
+    }`,
+			}),
+		}})
+}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 1c0e563..03fb5d4 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -61,7 +61,10 @@
 	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("apex", apex.BundleFactory)
+	ctx.RegisterModuleType("apex_defaults", apex.DefaultsFactory)
 	ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
+	ctx.RegisterModuleType("soong_config_module_type", android.SoongConfigModuleTypeFactory)
+	ctx.RegisterModuleType("soong_config_string_variable", android.SoongConfigStringVariableDummyFactory)
 }
 
 func TestApexBundleSimple(t *testing.T) {
@@ -1188,9 +1191,9 @@
 				"tags":              `["apex_available=myapex"]`,
 			}),
 			MakeBazelTarget("cc_stub_suite", "foo_stub_libs", AttrNameToString{
-				"soname":         `"foo.so"`,
-				"source_library": `":foo"`,
-				"symbol_file":    `"foo.map.txt"`,
+				"soname":               `"foo.so"`,
+				"source_library_label": `"//:foo"`,
+				"symbol_file":          `"foo.map.txt"`,
 				"versions": `[
         "28",
         "29",
@@ -1359,3 +1362,87 @@
 			}),
 		}})
 }
+
+func TestApexBundle_overridePlusProductVars(t *testing.T) {
+	// Reproduction of b/271424349
+	// Tests that overriding an apex that uses product variables correctly copies the product var
+	// selects over to the override.
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "apex - overriding a module that uses product vars",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Blueprint: `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_apex_defaults",
+    module_type: "apex_defaults",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: [
+        "manifest",
+        "min_sdk_version",
+    ],
+}
+
+library_linking_strategy_apex_defaults {
+    name: "higher_min_sdk_when_prefer_static",
+    soong_config_variables: {
+        library_linking_strategy: {
+            // Use the R min_sdk_version
+            prefer_static: {},
+            // Override the R min_sdk_version to min_sdk_version that supports dcla
+            conditions_default: {
+                min_sdk_version: "31",
+            },
+        },
+    },
+}
+
+filegroup {
+	name: "foo-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "foo",
+	defaults: ["higher_min_sdk_when_prefer_static"],
+	min_sdk_version: "30",
+	package_name: "pkg_name",
+	file_contexts: ":foo-file_contexts",
+}
+override_apex {
+	name: "override_foo",
+	base: ":foo",
+	package_name: "override_pkg_name",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "foo", AttrNameToString{
+				"file_contexts": `":foo-file_contexts"`,
+				"manifest":      `"apex_manifest.json"`,
+				"min_sdk_version": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30",
+        "//conditions:default": "31",
+    })`,
+				"package_name": `"pkg_name"`,
+			}), MakeBazelTarget("apex", "override_foo", AttrNameToString{
+				"base_apex_name": `"foo"`,
+				"file_contexts":  `":foo-file_contexts"`,
+				"manifest":       `"apex_manifest.json"`,
+				"min_sdk_version": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30",
+        "//conditions:default": "31",
+    })`,
+				"package_name": `"override_pkg_name"`,
+			}),
+		}})
+}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 86b9b27..d1dfb9d 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -17,21 +17,57 @@
 import (
 	"fmt"
 	"os"
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/shared"
 )
 
+func deleteFilesExcept(ctx *CodegenContext, rootOutputPath android.OutputPath, except []BazelFile) {
+	// Delete files that should no longer be present.
+	bp2buildDirAbs := shared.JoinPath(ctx.topDir, rootOutputPath.String())
+
+	filesToDelete := make(map[string]struct{})
+	err := filepath.Walk(bp2buildDirAbs,
+		func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
+			}
+			if !info.IsDir() {
+				relPath, err := filepath.Rel(bp2buildDirAbs, path)
+				if err != nil {
+					return err
+				}
+				filesToDelete[relPath] = struct{}{}
+			}
+			return nil
+		})
+	if err != nil {
+		fmt.Printf("ERROR reading %s: %s", bp2buildDirAbs, err)
+		os.Exit(1)
+	}
+
+	for _, bazelFile := range except {
+		filePath := filepath.Join(bazelFile.Dir, bazelFile.Basename)
+		delete(filesToDelete, filePath)
+	}
+	for f, _ := range filesToDelete {
+		absPath := shared.JoinPath(bp2buildDirAbs, f)
+		if err := os.RemoveAll(absPath); err != nil {
+			fmt.Printf("ERROR deleting %s: %s", absPath, err)
+			os.Exit(1)
+		}
+	}
+}
+
 // Codegen is the backend of bp2build. The code generator is responsible for
 // writing .bzl files that are equivalent to Android.bp files that are capable
 // of being built with Bazel.
 func Codegen(ctx *CodegenContext) *CodegenMetrics {
 	// This directory stores BUILD files that could be eventually checked-in.
 	bp2buildDir := android.PathForOutput(ctx, "bp2build")
-	if err := android.RemoveAllOutputDir(bp2buildDir); err != nil {
-		fmt.Printf("ERROR: Encountered error while cleaning %s: %s", bp2buildDir, err.Error())
-	}
 
 	res, errs := GenerateBazelTargets(ctx, true)
 	if len(errs) > 0 {
@@ -44,10 +80,19 @@
 	}
 	bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
 	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
+	// files. However, this would regenerate files which were otherwise unchanged
+	// since the last bp2build run, which would have negative incremental
+	// performance implications.
+	deleteFilesExcept(ctx, bp2buildDir, bp2buildFiles)
 
-	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionDirFiles(ctx, res.metrics))
-
+	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)
 	return &res.metrics
 }
 
@@ -55,17 +100,20 @@
 // This includes
 // 1. config value(s) that are hardcoded in Soong
 // 2. product_config variables
-func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) []BazelFile {
+func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) {
 	var ret []BazelFile
 
 	productConfigFiles, err := CreateProductConfigFiles(ctx)
 	if err != nil {
-		fmt.Printf("ERROR: %s", err.Error())
-		os.Exit(1)
+		return nil, err
 	}
 	ret = append(ret, productConfigFiles...)
-	ret = append(ret, soongInjectionFiles(ctx.Config(), metrics)...)
-	return ret
+	injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics)
+	if err != nil {
+		return nil, err
+	}
+	ret = append(ret, injectionFiles...)
+	return ret, 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 e343a05..3eec439 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -97,6 +97,8 @@
 build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
 build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
 build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
+build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
+build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 6c6631a..fde9b69 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -60,6 +60,15 @@
 	}
 }
 
+// PackageName returns the package of the Bazel target.
+// Defaults to root of tree.
+func (t BazelTarget) PackageName() string {
+	if t.packageName == "" {
+		return "."
+	}
+	return t.packageName
+}
+
 // BazelTargets is a typedef for a slice of BazelTarget objects.
 type BazelTargets []BazelTarget
 
@@ -227,7 +236,7 @@
 // the generated attributes are sorted to ensure determinism.
 func propsToAttributes(props map[string]string) string {
 	var attributes string
-	for _, propName := range android.SortedStringKeys(props) {
+	for _, propName := range android.SortedKeys(props) {
 		attributes += fmt.Sprintf("    %s = %s,\n", propName, props[propName])
 	}
 	return attributes
@@ -337,7 +346,10 @@
 			return
 		}
 
-		buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
+		for _, target := range targets {
+			targetDir := target.PackageName()
+			buildFileToTargets[targetDir] = append(buildFileToTargets[targetDir], target)
+		}
 	})
 
 	if len(errs) > 0 {
@@ -454,7 +466,8 @@
 
 	targetName := targetNameWithVariant(ctx, m)
 	return BazelTarget{
-		name: targetName,
+		name:        targetName,
+		packageName: ctx.ModuleDir(m),
 		content: fmt.Sprintf(
 			soongModuleTargetTemplate,
 			targetName,
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
index 992cc1c..e774fdf 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -83,7 +83,7 @@
 func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
 	var loadStmts string
 	var moduleRuleMap string
-	for _, bzlFileName := range android.SortedStringKeys(bzlLoads) {
+	for _, bzlFileName := range android.SortedKeys(bzlLoads) {
 		loadStmt := "load(\"//build/bazel/queryview_rules:"
 		loadStmt += bzlFileName
 		loadStmt += ".bzl\""
@@ -104,7 +104,7 @@
 
 	rules := make(map[string][]rule)
 	// TODO: allow registration of a bzl rule when registring a factory
-	for _, moduleType := range android.SortedStringKeys(moduleTypeFactories) {
+	for _, moduleType := range android.SortedKeys(moduleTypeFactories) {
 		factory := moduleTypeFactories[moduleType]
 		factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
 		pkg := strings.Split(factoryName, ".")[0]
@@ -221,7 +221,7 @@
 	}
 
 	properties := make([]property, 0, len(propertiesByName))
-	for _, key := range android.SortedStringKeys(propertiesByName) {
+	for _, key := range android.SortedKeys(propertiesByName) {
 		properties = append(properties, propertiesByName[key])
 	}
 
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index fe156df..0315732 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -365,7 +365,7 @@
 		{
 			description:   "nocrt: true",
 			soongProperty: `nocrt: true,`,
-			bazelAttr:     AttrNameToString{"link_crt": `False`},
+			bazelAttr:     AttrNameToString{"features": `["-link_crt"]`},
 		},
 		{
 			description:   "nocrt: false",
@@ -408,12 +408,12 @@
 		{
 			description:   "no_libcrt: true",
 			soongProperty: `no_libcrt: true,`,
-			bazelAttr:     AttrNameToString{"use_libcrt": `False`},
+			bazelAttr:     AttrNameToString{"features": `["-use_libcrt"]`},
 		},
 		{
 			description:   "no_libcrt: false",
 			soongProperty: `no_libcrt: false,`,
-			bazelAttr:     AttrNameToString{"use_libcrt": `True`},
+			bazelAttr:     AttrNameToString{},
 		},
 		{
 			description: "no_libcrt: not set",
@@ -868,3 +868,131 @@
 		},
 	})
 }
+
+func TestCcBinaryWithThinLto(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when thin LTO is enabled",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features":       `["android_thin_lto"]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithLtoNever(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when LTO is explicitly disabled",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features":       `["-android_thin_lto"]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithThinLtoArchSpecific(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when LTO differs across arch and os variants",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	target: {
+		android: {
+			lto: {
+				thin: true,
+			},
+		},
+	},
+	arch: {
+		riscv64: {
+			lto: {
+				thin: false,
+			},
+		},
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+        "//conditions:default": [],
+    })`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when LTO disabled by default but enabled on a particular variant",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+	target: {
+		android: {
+			lto: {
+				thin: true,
+				never: false,
+			},
+		},
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_thin_lto"],
+        "//conditions:default": ["-android_thin_lto"],
+    })`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithThinLtoAndWholeProgramVtables(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when thin LTO is enabled with whole_program_vtables",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+	whole_program_vtables: true,
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `[
+        "android_thin_lto",
+        "android_thin_lto_whole_program_vtables",
+    ]`,
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index f924d00..d2c463d 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1308,7 +1308,7 @@
 
 func TestCCLibraryNoCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library - nocrt: true emits attribute",
+		Description:                "cc_library - nocrt: true disables feature",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Filesystem: map[string]string{
@@ -1323,7 +1323,7 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"link_crt": `False`,
+			"features": `["-link_crt"]`,
 			"srcs":     `["impl.cpp"]`,
 		}),
 	},
@@ -1375,7 +1375,13 @@
     include_build_directory: false,
 }
 `,
-		ExpectedErr: fmt.Errorf("module \"foo-lib\": nocrt is not supported for arch variants"),
+		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-link_crt"],
+        "//conditions:default": [],
+    })`,
+			"srcs": `["impl.cpp"]`,
+		}),
 	})
 }
 
@@ -1395,8 +1401,8 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"srcs":       `["impl.cpp"]`,
-			"use_libcrt": `False`,
+			"features": `["-use_libcrt"]`,
+			"srcs":     `["impl.cpp"]`,
 		}),
 	})
 }
@@ -1445,8 +1451,7 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"srcs":       `["impl.cpp"]`,
-			"use_libcrt": `True`,
+			"srcs": `["impl.cpp"]`,
 		}),
 	})
 }
@@ -1475,10 +1480,10 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
 			"srcs": `["impl.cpp"]`,
-			"use_libcrt": `select({
-        "//build/bazel/platforms/arch:arm": False,
-        "//build/bazel/platforms/arch:x86": False,
-        "//conditions:default": None,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+        "//build/bazel/platforms/arch:x86": ["-use_libcrt"],
+        "//conditions:default": [],
     })`,
 		}),
 	})
@@ -1512,17 +1517,15 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"srcs": `["impl.cpp"]`,
-			"use_libcrt": `select({
-        "//build/bazel/platforms/os_arch:android_arm": False,
-        "//build/bazel/platforms/os_arch:android_x86": False,
-        "//build/bazel/platforms/os_arch:darwin_arm64": False,
-        "//build/bazel/platforms/os_arch:darwin_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_glibc_x86": False,
-        "//build/bazel/platforms/os_arch:linux_musl_x86": False,
-        "//build/bazel/platforms/os_arch:windows_x86": False,
-        "//conditions:default": None,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+        "//build/bazel/platforms/arch:x86": ["-use_libcrt"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:darwin": ["-use_libcrt"],
+        "//conditions:default": [],
     })`,
+			"srcs": `["impl.cpp"]`,
 		}),
 	})
 }
@@ -1557,16 +1560,10 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
 			"srcs": `["impl.cpp"]`,
-			"use_libcrt": `select({
-        "//build/bazel/platforms/os_arch:android_arm": False,
-        "//build/bazel/platforms/os_arch:android_x86_64": False,
-        "//build/bazel/platforms/os_arch:darwin_arm64": True,
-        "//build/bazel/platforms/os_arch:darwin_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_bionic_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_glibc_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_musl_x86_64": False,
-        "//build/bazel/platforms/os_arch:windows_x86_64": False,
-        "//conditions:default": None,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+        "//build/bazel/platforms/arch:x86_64": ["-use_libcrt"],
+        "//conditions:default": [],
     })`,
 		}),
 	})
@@ -1806,6 +1803,11 @@
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Blueprint: soongCcLibraryPreamble + `
 cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
     name: "target_linux_bionic_empty",
     target: {
         linux_bionic: {
@@ -1816,7 +1818,10 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", AttrNameToString{
-			"system_dynamic_deps": `[]`,
+			"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
 		}),
 	},
 	)
@@ -1829,6 +1834,11 @@
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Blueprint: soongCcLibraryPreamble + `
 cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
     name: "target_bionic_empty",
     target: {
         bionic: {
@@ -1839,12 +1849,68 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", AttrNameToString{
-			"system_dynamic_deps": `[]`,
+			"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
 		}),
 	},
 	)
 }
 
+func TestCcLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library system_shared_lib empty for musl variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "target_musl_empty",
+    target: {
+        musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("target_musl_empty", AttrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library system_shared_lib empty for linux_musl variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "target_linux_musl_empty",
+    target: {
+        linux_musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("target_linux_musl_empty", AttrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	})
+}
 func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library system_shared_libs set for shared and root",
@@ -2359,7 +2425,10 @@
 				"whole_archive_deps": `[":a_cc_proto_lite"]`,
 			}), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{
 				"srcs": `["a_fg.proto"]`,
-				"tags": `["manual"]`,
+				"tags": `[
+        "apex_available=//apex_available:anyapex",
+        "manual",
+    ]`,
 			}), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{
 				"srcs": `["a_fg.proto"]`,
 			}),
@@ -2398,7 +2467,10 @@
 				"whole_archive_deps": `[":a_cc_proto_lite"]`,
 			}), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{
 				"srcs": `["a_fg.proto"]`,
-				"tags": `["manual"]`,
+				"tags": `[
+        "apex_available=//apex_available:anyapex",
+        "manual",
+    ]`,
 			}), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{
 				"srcs": `["a_fg.proto"]`,
 			}),
@@ -2714,9 +2786,9 @@
 		"stubs_symbol_file": `"a.map.txt"`,
 	})
 	expectedBazelTargets = append(expectedBazelTargets, makeCcStubSuiteTargets("a", AttrNameToString{
-		"soname":            `"a.so"`,
-		"source_library":    `":a"`,
-		"stubs_symbol_file": `"a.map.txt"`,
+		"soname":               `"a.so"`,
+		"source_library_label": `"//foo/bar:a"`,
+		"stubs_symbol_file":    `"a.map.txt"`,
 		"stubs_versions": `[
         "28",
         "29",
@@ -2756,7 +2828,7 @@
 	expectedBazelTargets := []string{
 		MakeBazelTarget(
 			"cc_api_library_headers",
-			"libfoo.systemapi.headers",
+			"libfoo.module-libapi.headers",
 			AttrNameToString{
 				"export_includes": `["dir1"]`,
 			}),
@@ -2773,18 +2845,18 @@
 				"api":          `"libfoo.map.txt"`,
 				"library_name": `"libfoo"`,
 				"api_surfaces": `[
-        "systemapi",
+        "module-libapi",
         "vendorapi",
     ]`,
 				"hdrs": `[
-        ":libfoo.systemapi.headers",
+        ":libfoo.module-libapi.headers",
         ":libfoo.vendorapi.headers",
     ]`,
 			}),
 	}
 	RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
 		Blueprint:            bp,
-		Description:          "cc API contributions to systemapi and vendorapi",
+		Description:          "cc API contributions to module-libapi and vendorapi",
 		ExpectedBazelTargets: expectedBazelTargets,
 	})
 }
@@ -2803,8 +2875,8 @@
 				stubs: {symbol_file: "a.map.txt"},
 			}`,
 			expectedApi:         `"a.map.txt"`,
-			expectedApiSurfaces: `["systemapi"]`,
-			description:         "Library that contributes to systemapi",
+			expectedApiSurfaces: `["module-libapi"]`,
+			description:         "Library that contributes to module-libapi",
 		},
 		{
 			bp: `
@@ -2825,10 +2897,10 @@
 			}`,
 			expectedApi: `"a.map.txt"`,
 			expectedApiSurfaces: `[
-        "systemapi",
+        "module-libapi",
         "vendorapi",
     ]`,
-			description: "Library that contributes to systemapi and vendorapi",
+			description: "Library that contributes to module-libapi and vendorapi",
 		},
 	}
 	for _, testCase := range testCases {
@@ -2966,13 +3038,15 @@
 		},
 	},
 	bazel_module: { bp2build_available: true },
+	apex_available: ["foo"],
 }`,
 		ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{
 			"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": [":barlib_stub_libs_current"],
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
         "//conditions:default": [":barlib"],
     })`,
 			"local_includes": `["."]`,
+			"tags":           `["apex_available=foo"]`,
 		}),
 	})
 }
@@ -3006,6 +3080,7 @@
 	},
 	include_build_directory: false,
 	bazel_module: { bp2build_available: true },
+	apex_available: ["foo"],
 }`,
 		ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{
 			"implementation_dynamic_deps": `select({
@@ -3022,14 +3097,15 @@
         "//build/bazel/platforms/os:linux_musl": [":quxlib"],
         "//build/bazel/platforms/os:windows": [":quxlib"],
         "//build/bazel/rules/apex:android-in_apex": [
-            ":barlib_stub_libs_current",
-            ":quxlib_stub_libs_current",
+            "@api_surfaces//module-libapi/current:barlib",
+            "@api_surfaces//module-libapi/current:quxlib",
         ],
         "//conditions:default": [
             ":barlib",
             ":quxlib",
         ],
     })`,
+			"tags": `["apex_available=foo"]`,
 		}),
 	})
 }
@@ -3256,6 +3332,7 @@
 			MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{
 				"srcs":                `["aidl/A.aidl"]`,
 				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
 			}),
 			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
 				"srcs": `["B.aidl"]`,
@@ -3368,23 +3445,23 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
     })`,
 				"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
     })`,
 				"local_includes": `["."]`,
 			}),
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
     })`,
 				"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
     })`,
 				"local_includes": `["."]`,
 			}),
@@ -3414,16 +3491,16 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
     })`,
 				"dynamic_deps": `select({
-        "//build/bazel/rules/apex:non_apex": [":baz__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":baz__BP2BUILD__MISSING__DEP"],
     })`,
 				"deps": `select({
-        "//build/bazel/rules/apex:non_apex": [":abc__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":abc__BP2BUILD__MISSING__DEP"],
     })`,
 				"local_includes": `["."]`,
 			}),
@@ -3525,42 +3602,57 @@
 	})
 }
 
-func TestCcLibraryWithAidlAndSharedLibs(t *testing.T) {
+func TestCcLibraryWithAidlAndLibs(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_aidl_library depends on shared libs from parent cc_library_static",
+		Description:                "cc_aidl_library depends on libs from parent cc_library_static",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Blueprint: `
 cc_library_static {
-    name: "foo",
-    srcs: [
-        "Foo.aidl",
-    ],
+	name: "foo",
+	srcs: [
+		"Foo.aidl",
+	],
+	static_libs: [
+		"bar-static",
+		"baz-static",
+	],
 	shared_libs: [
-		"bar",
-		"baz",
+		"bar-shared",
+		"baz-shared",
+	],
+	export_static_lib_headers: [
+		"baz-static",
 	],
 	export_shared_lib_headers: [
-		"baz",
+		"baz-shared",
 	],
 }` +
-			simpleModuleDoNotConvertBp2build("cc_library", "bar") +
-			simpleModuleDoNotConvertBp2build("cc_library", "baz"),
+			simpleModuleDoNotConvertBp2build("cc_library_static", "bar-static") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "baz-static") +
+			simpleModuleDoNotConvertBp2build("cc_library", "bar-shared") +
+			simpleModuleDoNotConvertBp2build("cc_library", "baz-shared"),
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
 				"srcs": `["Foo.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
 				"deps": `[":foo_aidl_library"]`,
+				"implementation_deps": `[
+        ":baz-static",
+        ":bar-static",
+    ]`,
 				"implementation_dynamic_deps": `[
-        ":baz",
-        ":bar",
+        ":baz-shared",
+        ":bar-shared",
     ]`,
 			}),
 			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"dynamic_deps":                      `[":baz"]`,
-				"implementation_dynamic_deps":       `[":bar"]`,
+				"deps":                              `[":baz-static"]`,
+				"implementation_deps":               `[":bar-static"]`,
+				"dynamic_deps":                      `[":baz-shared"]`,
+				"implementation_dynamic_deps":       `[":bar-shared"]`,
 				"local_includes":                    `["."]`,
 			}),
 		},
@@ -3574,8 +3666,17 @@
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Blueprint: `
 cc_library_static {
-    name: "foo",
-    srcs: ["foo.cpp"],
+	name: "foo",
+	srcs: ["foo.cpp"],
+}
+cc_library_static {
+	name: "foo-no-tidy",
+	srcs: ["foo.cpp"],
+	tidy: false,
+}
+cc_library_static {
+	name: "foo-tidy",
+	srcs: ["foo.cpp"],
 	tidy: true,
 	tidy_checks: ["check1", "check2"],
 	tidy_checks_as_errors: ["check1error", "check2error"],
@@ -3586,7 +3687,16 @@
 			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"local_includes": `["."]`,
 				"srcs":           `["foo.cpp"]`,
-				"tidy":           `True`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo-no-tidy", AttrNameToString{
+				"local_includes": `["."]`,
+				"srcs":           `["foo.cpp"]`,
+				"tidy":           `"never"`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo-tidy", AttrNameToString{
+				"local_includes": `["."]`,
+				"srcs":           `["foo.cpp"]`,
+				"tidy":           `"local"`,
 				"tidy_checks": `[
         "check1",
         "check2",
@@ -4018,3 +4128,225 @@
 		},
 	})
 }
+
+func TestCcLibraryInApexWithStubSharedLibs(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with in apex with stub shared_libs and export_shared_lib_headers",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "barlib",
+	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+	name: "bazlib",
+	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+    name: "foo",
+	  shared_libs: ["barlib", "bazlib"],
+    export_shared_lib_headers: ["bazlib"],
+    apex_available: [
+        "apex_available:platform",
+    ],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
+        "//conditions:default": [":barlib"],
+    })`,
+				"dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"],
+        "//conditions:default": [":bazlib"],
+    })`,
+				"local_includes": `["."]`,
+				"tags":           `["apex_available=apex_available:platform"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
+        "//conditions:default": [":barlib"],
+    })`,
+				"dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"],
+        "//conditions:default": [":bazlib"],
+    })`,
+				"local_includes": `["."]`,
+				"tags":           `["apex_available=apex_available:platform"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithThinLto(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when thin LTO is enabled",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features":       `["android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithLtoNever(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when LTO is explicitly disabled",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features":       `["-android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["-android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithThinLtoArchSpecific(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when LTO differs across arch and os variants",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	target: {
+		android: {
+			lto: {
+				thin: true,
+			},
+		},
+	},
+	arch: {
+		riscv64: {
+			lto: {
+				thin: false,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+        "//conditions:default": [],
+    })`}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+        "//conditions:default": [],
+    })`}),
+		},
+	})
+}
+
+func TestCcLibraryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when LTO disabled by default but enabled on a particular variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+	target: {
+		android: {
+			lto: {
+				thin: true,
+				never: false,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_thin_lto"],
+        "//conditions:default": ["-android_thin_lto"],
+    })`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_thin_lto"],
+        "//conditions:default": ["-android_thin_lto"],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithThinLtoWholeProgramVtables(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when thin LTO is enabled with whole_program_vtables",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+	whole_program_vtables: true,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features": `[
+        "android_thin_lto",
+        "android_thin_lto_whole_program_vtables",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `[
+        "android_thin_lto",
+        "android_thin_lto_whole_program_vtables",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 017df6f..8e0a728 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -15,7 +15,6 @@
 package bp2build
 
 import (
-	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -405,7 +404,7 @@
 
 func TestCcLibrarySharedNoCrtTrue(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared - nocrt: true emits attribute",
+		Description: "cc_library_shared - nocrt: true disables feature",
 		Filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -419,7 +418,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
-				"link_crt": `False`,
+				"features": `["-link_crt"]`,
 				"srcs":     `["impl.cpp"]`,
 			}),
 		},
@@ -428,7 +427,7 @@
 
 func TestCcLibrarySharedNoCrtFalse(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared - nocrt: false doesn't emit attribute",
+		Description: "cc_library_shared - nocrt: false doesn't disable feature",
 		Filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -469,7 +468,15 @@
     include_build_directory: false,
 }
 `,
-		ExpectedErr: fmt.Errorf("module \"foo_shared\": nocrt is not supported for arch variants"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-link_crt"],
+        "//conditions:default": [],
+    })`,
+				"srcs": `["impl.cpp"]`,
+			}),
+		},
 	})
 }
 
@@ -533,9 +540,9 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: []string{makeCcStubSuiteTargets("a", AttrNameToString{
-			"soname":            `"a.so"`,
-			"source_library":    `":a"`,
-			"stubs_symbol_file": `"a.map.txt"`,
+			"soname":               `"a.so"`,
+			"source_library_label": `"//foo/bar:a"`,
+			"stubs_symbol_file":    `"a.map.txt"`,
 			"stubs_versions": `[
         "28",
         "29",
@@ -549,6 +556,151 @@
 	})
 }
 
+func TestCcLibrarySharedStubs_UseImplementationInSameApex(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_shared stubs",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "a",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	include_build_directory: false,
+	apex_available: ["made_up_apex"],
+}
+cc_library_shared {
+	name: "b",
+	shared_libs: [":a"],
+	include_build_directory: false,
+	apex_available: ["made_up_apex"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+				"implementation_dynamic_deps": `[":a"]`,
+				"tags":                        `["apex_available=made_up_apex"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedStubs_UseStubsInDifferentApex(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_shared stubs",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "a",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	include_build_directory: false,
+	apex_available: ["apex_a"],
+}
+cc_library_shared {
+	name: "b",
+	shared_libs: [":a"],
+	include_build_directory: false,
+	apex_available: ["apex_b"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+        "//conditions:default": [":a"],
+    })`,
+				"tags": `["apex_available=apex_b"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedStubs_IgnorePlatformAvailable(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_shared stubs",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "a",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	include_build_directory: false,
+	apex_available: ["//apex_available:platform", "apex_a"],
+}
+cc_library_shared {
+	name: "b",
+	shared_libs: [":a"],
+	include_build_directory: false,
+	apex_available: ["//apex_available:platform", "apex_b"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+        "//conditions:default": [":a"],
+    })`,
+				"tags": `[
+        "apex_available=//apex_available:platform",
+        "apex_available=apex_b",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedStubs_MultipleApexAvailable(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "a",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	include_build_directory: false,
+	apex_available: ["//apex_available:platform", "apex_a", "apex_b"],
+}
+cc_library_shared {
+	name: "b",
+	shared_libs: [":a"],
+	include_build_directory: false,
+	apex_available: ["//apex_available:platform", "apex_b"],
+}
+
+cc_library_shared {
+	name: "c",
+	shared_libs: [":a"],
+	include_build_directory: false,
+	apex_available: ["//apex_available:platform", "apex_a", "apex_b"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+        "//conditions:default": [":a"],
+    })`,
+				"tags": `[
+        "apex_available=//apex_available:platform",
+        "apex_available=apex_b",
+    ]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "c", AttrNameToString{
+				"implementation_dynamic_deps": `[":a"]`,
+				"tags": `[
+        "apex_available=//apex_available:platform",
+        "apex_available=apex_a",
+        "apex_available=apex_b",
+    ]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedSystemSharedLibsSharedEmpty(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library_shared system_shared_libs empty shared default",
@@ -967,3 +1119,133 @@
 		},
 	})
 }
+
+func TestCcLibrarySharedWithThinLto(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when thin lto is enabled",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithLtoNever(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when thin lto is enabled",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["-android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithThinLtoArchSpecific(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when LTO differs across arch and os variants",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	target: {
+		android: {
+			lto: {
+				thin: true,
+			},
+		},
+	},
+	arch: {
+		riscv64: {
+			lto: {
+				thin: false,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+        "//conditions:default": [],
+    })`}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared with thin lto disabled by default but enabled on a particular variant",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+	target: {
+		android: {
+			lto: {
+				thin: true,
+				never: false,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_thin_lto"],
+        "//conditions:default": ["-android_thin_lto"],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithThinLtoAndWholeProgramVtables(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when thin LTO is enabled with whole_program_vtables",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+	whole_program_vtables: true,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `[
+        "android_thin_lto",
+        "android_thin_lto_whole_program_vtables",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 767f4ad..cd4cf51 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1310,6 +1310,11 @@
 	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
 		Description: "cc_library_static system_shared_lib empty for bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_bionic_empty",
     target: {
@@ -1322,7 +1327,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{
-				"system_dynamic_deps": `[]`,
+				"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
 	})
@@ -1336,6 +1344,11 @@
 	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
 		Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_linux_bionic_empty",
     target: {
@@ -1348,6 +1361,63 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{
+				"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static system_shared_lib empty for musl variant",
+		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "target_musl_empty",
+    target: {
+        musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "target_musl_empty", AttrNameToString{
+				"system_dynamic_deps": `[]`,
+			}),
+		},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static system_shared_lib empty for linux_musl variant",
+		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "target_linux_musl_empty",
+    target: {
+        linux_musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "target_linux_musl_empty", AttrNameToString{
 				"system_dynamic_deps": `[]`,
 			}),
 		},
@@ -1359,6 +1429,11 @@
 		Description: "cc_library_static system_shared_libs set for bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble +
 			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_bionic",
     target: {
@@ -1374,6 +1449,7 @@
 				"system_dynamic_deps": `select({
         "//build/bazel/platforms/os:android": [":libc"],
         "//build/bazel/platforms/os:linux_bionic": [":libc"],
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
         "//conditions:default": [],
     })`,
 			}),
@@ -1387,6 +1463,11 @@
 		Blueprint: soongCcLibraryStaticPreamble +
 			simpleModuleDoNotConvertBp2build("cc_library", "libc") +
 			simpleModuleDoNotConvertBp2build("cc_library", "libm") + `
+cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_linux_bionic",
     system_shared_libs: ["libc"],
@@ -1402,6 +1483,7 @@
 			MakeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{
 				"system_dynamic_deps": `[":libc"] + select({
         "//build/bazel/platforms/os:linux_bionic": [":libm"],
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
         "//conditions:default": [],
     })`,
 			}),
@@ -1435,12 +1517,14 @@
         },
     },
     include_build_directory: false,
+    apex_available: ["foo"],
 }
 
 cc_library_static {
     name: "all",
     shared_libs: ["libc"],
     include_build_directory: false,
+    apex_available: ["foo"],
 }
 
 cc_library_static {
@@ -1448,12 +1532,14 @@
     shared_libs: ["libc"],
     system_shared_libs: [],
     include_build_directory: false,
+    apex_available: ["foo"],
 }
 
 cc_library_static {
     name: "used_with_stubs",
     shared_libs: ["libm"],
     include_build_directory: false,
+    apex_available: ["foo"],
 }
 
 cc_library_static {
@@ -1461,23 +1547,32 @@
     shared_libs: ["libm"],
     system_shared_libs: [],
     include_build_directory: false,
+    apex_available: ["foo"],
 }
 `,
 		ExpectedBazelTargets: []string{
-			MakeBazelTarget("cc_library_static", "all", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "all", AttrNameToString{
+				"tags": `["apex_available=foo"]`,
+			}),
 			MakeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", AttrNameToString{
 				"implementation_dynamic_deps": `[":libc"]`,
 				"system_dynamic_deps":         `[]`,
+				"tags":                        `["apex_available=foo"]`,
 			}),
 			MakeBazelTarget("cc_library_static", "keep_with_stubs", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": [":libm_stub_libs_current"],
+        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:libm"],
         "//conditions:default": [":libm"],
     })`,
 				"system_dynamic_deps": `[]`,
+				"tags":                `["apex_available=foo"]`,
 			}),
-			MakeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{}),
-			MakeBazelTarget("cc_library_static", "used_with_stubs", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{
+				"tags": `["apex_available=foo"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "used_with_stubs", AttrNameToString{
+				"tags": `["apex_available=foo"]`,
+			}),
 		},
 	})
 }
@@ -1807,3 +1902,133 @@
 		},
 	})
 }
+
+func TestCcLibraryStaticWithThinLto(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when thin lto is enabled",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features":       `["android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithLtoNever(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when thin lto is enabled",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features":       `["-android_thin_lto"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithThinLtoArchSpecific(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when LTO differs across arch and os variants",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	target: {
+		android: {
+			lto: {
+				thin: true,
+			},
+		},
+	},
+	arch: {
+		riscv64: {
+			lto: {
+				thin: false,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"],
+        "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"],
+        "//conditions:default": [],
+    })`}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when LTO disabled by default but enabled on a particular variant",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	lto: {
+		never: true,
+	},
+	target: {
+		android: {
+			lto: {
+				thin: true,
+				never: false,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_thin_lto"],
+        "//conditions:default": ["-android_thin_lto"],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithThinLtoAndWholeProgramVtables(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when thin lto is enabled with whole_program_vtables",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	lto: {
+		thin: true,
+	},
+	whole_program_vtables: true,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features": `[
+        "android_thin_lto",
+        "android_thin_lto_whole_program_vtables",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1377c6b..eab84e1 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -58,6 +58,7 @@
     exclude_srcs: ["a/b/exclude.c"],
     sdk_version: "current",
     min_sdk_version: "29",
+	crt: true,
 }
 `,
 		ExpectedBazelTargets: []string{
@@ -76,6 +77,7 @@
 				"system_dynamic_deps": `[]`,
 				"sdk_version":         `"current"`,
 				"min_sdk_version":     `"29"`,
+				"crt":                 "True",
 			}),
 		},
 	})
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
index 2fe158e..c5a6dfd 100644
--- a/bp2build/cc_prebuilt_library_conversion_test.go
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -39,6 +39,10 @@
 				MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
 					"static_library": `"libf.so"`,
 				}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+					"static_library": `"libf.so"`,
+					"alwayslink":     "True",
+				}),
 				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libf.so"`,
 				}),
@@ -71,8 +75,14 @@
         "//build/bazel/platforms/arch:arm": "libg.so",
         "//build/bazel/platforms/arch:arm64": "libf.so",
         "//conditions:default": None,
-    })`,
-				}),
+    })`}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+					"alwayslink": "True",
+					"static_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`}),
 				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `select({
         "//build/bazel/platforms/arch:arm": "libg.so",
@@ -109,6 +119,12 @@
 					"export_includes":        `["testdir/1/"]`,
 					"export_system_includes": `["testdir/2/"]`,
 				}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+					"static_library":         `"libf.so"`,
+					"export_includes":        `["testdir/1/"]`,
+					"export_system_includes": `["testdir/2/"]`,
+					"alwayslink":             "True",
+				}),
 				// TODO(b/229374533): When fixed, update this test
 				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libf.so"`,
@@ -188,6 +204,10 @@
 				MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
 					"static_library": `"libf.so"`,
 				}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+					"static_library": `"libf.so"`,
+					"alwayslink":     "True",
+				}),
 				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libg.so"`,
 				}),
@@ -245,6 +265,10 @@
 //				makeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
 //					"static_library": `"libf.so"`,
 //				}),
+//				makeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_always", attrNameToString{
+//					"static_library": `"libf.so"`,
+//					"alwayslink": "True",
+//				}),
 //			},
 //		})
 //}
diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go
index 6116b00..17da813 100644
--- a/bp2build/cc_prebuilt_library_static_test.go
+++ b/bp2build/cc_prebuilt_library_static_test.go
@@ -39,6 +39,10 @@
 				MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
 					"static_library": `"libf.so"`,
 				}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+					"static_library": `"libf.so"`,
+					"alwayslink":     "True",
+				}),
 			},
 		})
 }
@@ -68,8 +72,14 @@
         "//build/bazel/platforms/arch:arm": "libg.so",
         "//build/bazel/platforms/arch:arm64": "libf.so",
         "//conditions:default": None,
-    })`,
-				}),
+    })`}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+					"alwayslink": "True",
+					"static_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`}),
 			},
 		})
 }
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 4244956..8e17103 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -160,6 +160,12 @@
 // select statements.
 func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
 	var value reflect.Value
+	// configurableAttrs is the list of individual select statements to be
+	// concatenated together. These select statements should be along different
+	// axes. For example, one element may be
+	// `select({"//color:red": "one", "//color:green": "two"})`, and the second
+	// element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
+	// These selects should be sorted by axis identifier.
 	var configurableAttrs []selects
 	var prepend bool
 	var defaultSelectValue *string
@@ -250,7 +256,7 @@
 	}
 
 	var selects string
-	for _, selectKey := range android.SortedStringKeys(selectMap) {
+	for _, selectKey := range android.SortedKeys(selectMap) {
 		if selectKey == bazel.ConditionsDefaultSelectKey {
 			// Handle default condition later.
 			continue
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index e53f29e..6a39e25 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -6,6 +6,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/cc"
 	cc_config "android/soong/cc/config"
 	java_config "android/soong/java/config"
 
@@ -21,20 +22,25 @@
 }
 
 // PRIVATE: Use CreateSoongInjectionDirFiles instead
-func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
+func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
 	var files []BazelFile
 
 	files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
 
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
-	files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+	files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+	files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
 
 	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
 	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
 
 	files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
-	files = append(files, newFile("apex_toolchain", "constants.bzl", apex.BazelApexToolchainVars()))
+	apexToolchainVars, err := apex.BazelApexToolchainVars()
+	if err != nil {
+		return nil, err
+	}
+	files = append(files, newFile("apex_toolchain", "constants.bzl", apexToolchainVars))
 
 	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
 
@@ -50,17 +56,20 @@
 
 	apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
 	files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
+	// TODO(b/269691302)  value of apiLevelsContent is product variable dependent and should be avoided for soong injection
 	files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
 	files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
 
+	files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
+	files = append(files, newFile("allowlists", "env.bzl", android.EnvironmentVarsFile(cfg)))
 	// TODO(b/262781701): Create an alternate soong_build entrypoint for writing out these files only when requested
 	files = append(files, newFile("allowlists", "mixed_build_prod_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelProdMode), "\n")+"\n"))
 	files = append(files, newFile("allowlists", "mixed_build_staging_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelStagingMode), "\n")+"\n"))
 
-	return files
+	return files, nil
 }
 
 func CreateBazelFiles(
@@ -96,7 +105,7 @@
 
 func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
-	for _, dir := range android.SortedStringKeys(buildToTargets) {
+	for _, dir := range android.SortedKeys(buildToTargets) {
 		targets := buildToTargets[dir]
 		targets.sort()
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 43daddc..8c1d2ae 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -84,8 +84,10 @@
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
 	testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
-	files := soongInjectionFiles(testConfig, CreateCodegenMetrics())
-
+	files, err := soongInjectionFiles(testConfig, CreateCodegenMetrics())
+	if err != nil {
+		t.Error(err)
+	}
 	expectedFilePaths := []bazelFilepath{
 		{
 			dir:      "android",
@@ -101,7 +103,11 @@
 		},
 		{
 			dir:      "cc_toolchain",
-			basename: "constants.bzl",
+			basename: "config_constants.bzl",
+		},
+		{
+			dir:      "cc_toolchain",
+			basename: "sanitizer_constants.bzl",
 		},
 		{
 			dir:      "java_toolchain",
@@ -149,6 +155,14 @@
 		},
 		{
 			dir:      "allowlists",
+			basename: GeneratedBuildFileName,
+		},
+		{
+			dir:      "allowlists",
+			basename: "env.bzl",
+		},
+		{
+			dir:      "allowlists",
 			basename: "mixed_build_prod_allowlist.txt",
 		},
 		{
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index e978fb3..7ce559d 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -74,6 +74,7 @@
 			expectedBazelAttrs: AttrNameToString{
 				"srcs":                `["aidl/foo.aidl"]`,
 				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
 			},
 		},
 		{
@@ -85,18 +86,21 @@
 	}`,
 			expectedBazelAttrs: AttrNameToString{
 				"srcs": `["aidl/foo.aidl"]`,
+				"tags": `["apex_available=//apex_available:anyapex"]`,
 			},
 		},
 	}
 
 	for _, test := range testcases {
-		expectedBazelTargets := []string{
-			MakeBazelTargetNoRestrictions("aidl_library", "foo", test.expectedBazelAttrs),
-		}
-		runFilegroupTestCase(t, Bp2buildTestCase{
-			Description:          test.name,
-			Blueprint:            test.bp,
-			ExpectedBazelTargets: expectedBazelTargets,
+		t.Run(test.name, func(t *testing.T) {
+			expectedBazelTargets := []string{
+				MakeBazelTargetNoRestrictions("aidl_library", "foo", test.expectedBazelAttrs),
+			}
+			runFilegroupTestCase(t, Bp2buildTestCase{
+				Description:          test.name,
+				Blueprint:            test.bp,
+				ExpectedBazelTargets: expectedBazelTargets,
+			})
 		})
 	}
 }
@@ -136,7 +140,11 @@
 			MakeBazelTargetNoRestrictions("proto_library", "foo_bp2build_converted", AttrNameToString{
 				"srcs":                `["proto/foo.proto"]`,
 				"strip_import_prefix": `"proto"`,
-				"tags":                `["manual"]`}),
+				"tags": `[
+        "apex_available=//apex_available:anyapex",
+        "manual",
+    ]`,
+			}),
 			MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
 				"srcs": `["proto/foo.proto"]`}),
 		}})
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index e8551e5..1b9777c 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -56,11 +56,9 @@
     java_version: "8",
 }`,
 		ExpectedBazelTargets: []string{
-			MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
-				"srcs":       `["a.java"]`,
-				"main_class": `"com.android.test.MainClass"`,
-				"deps":       `["//other:jni-lib-1"]`,
-				"jvm_flags":  `["-Djava.library.path=$${RUNPATH}other"]`,
+			MakeBazelTarget("java_library", "java-binary-host-1_lib", AttrNameToString{
+				"srcs": `["a.java"]`,
+				"deps": `["//other:jni-lib-1"]`,
 				"javacopts": `[
         "-Xdoclint:all/protected",
         "-source 1.8 -target 1.8",
@@ -68,8 +66,15 @@
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
-    })`,
-			}),
+    })`}),
+			MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"jvm_flags":    `["-Djava.library.path=$${RUNPATH}other"]`,
+				"runtime_deps": `[":java-binary-host-1_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`}),
 		},
 	})
 }
@@ -122,10 +127,202 @@
 }
 `,
 		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-binary-host-libs_lib", AttrNameToString{
+				"srcs": `["a.java"]`,
+				"deps": `[":java-lib-dep-1-neverlink"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
 			MakeBazelTarget("java_binary", "java-binary-host-libs", AttrNameToString{
 				"main_class": `"com.android.test.MainClass"`,
-				"srcs":       `["a.java"]`,
-				"deps":       `[":java-lib-dep-1-neverlink"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+				"runtime_deps": `[":java-binary-host-libs_lib"]`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinSrcs(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with srcs, libs.",
+		Filesystem:  testFs,
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java", "b.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinCommonSrcs(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with common_srcs",
+		Filesystem:  testFs,
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java"],
+    common_srcs: ["b.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+				"srcs":        `["a.java"]`,
+				"common_srcs": `["b.kt"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinWithResourceDir(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with srcs, libs, resource dir  .",
+		Filesystem: map[string]string{
+			"test.mf":        "Main-Class: com.android.test.MainClass",
+			"res/a.res":      "",
+			"res/dir1/b.res": "",
+		},
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java", "b.kt"],
+    java_resource_dirs: ["res"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"resources": `[
+        "res/a.res",
+        "res/dir1/b.res",
+    ]`,
+				"resource_strip_prefix": `"res"`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinWithResources(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with srcs, libs, resources.",
+		Filesystem: map[string]string{
+			"test.mf":   "Main-Class: com.android.test.MainClass",
+			"res/a.res": "",
+			"res/b.res": "",
+		},
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java", "b.kt"],
+    java_resources: ["res/a.res", "res/b.res"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"resources": `[
+        "res/a.res",
+        "res/b.res",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinCflags(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with kotlincflags",
+		Filesystem:  testFs,
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.kt"],
+    kotlincflags: ["-flag1", "-flag2"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
+				"srcs": `["a.kt"]`,
+				"kotlincflags": `[
+        "-flag1",
+        "-flag2",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_lib"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 93a6174..69d0db9 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -493,6 +493,7 @@
         "a.aidl",
         "b.aidl",
     ]`,
+				"tags": `["apex_available=//apex_available:anyapex"]`,
 			}),
 			MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
 				"deps": `[":aidl_files"]`,
@@ -673,7 +674,7 @@
 
 func TestJavaLibraryKotlinSrcs(t *testing.T) {
 	runJavaLibraryTestCase(t, Bp2buildTestCase{
-		Description: "java_library with kotlin  srcs",
+		Description: "java_library with kotlin srcs",
 		Blueprint: `java_library {
     name: "java-lib-1",
     srcs: ["a.java", "b.java", "c.kt"],
@@ -693,9 +694,32 @@
 	})
 }
 
+func TestJavaLibraryKotlincflags(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with kotlincfalgs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: [ "a.kt"],
+    kotlincflags: ["-flag1", "-flag2"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-lib-1", AttrNameToString{
+				"srcs": `["a.kt"]`,
+				"kotlincflags": `[
+        "-flag1",
+        "-flag2",
+    ]`,
+			}),
+			MakeNeverlinkDuplicateTarget("kt_jvm_library", "java-lib-1"),
+		},
+	})
+}
+
 func TestJavaLibraryKotlinCommonSrcs(t *testing.T) {
 	runJavaLibraryTestCase(t, Bp2buildTestCase{
-		Description: "java_library with kotlin  common_srcs",
+		Description: "java_library with kotlin common_srcs",
 		Blueprint: `java_library {
     name: "java-lib-1",
     srcs: ["a.java", "b.java"],
@@ -715,3 +739,69 @@
 		},
 	})
 }
+
+func TestJavaLibraryArchVariantLibs(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with arch variant libs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java"],
+    libs: ["java-lib-2"],
+    target: {
+        android: {
+            libs: ["java-lib-3"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+	java_library{
+		name: "java-lib-2",
+}
+
+	java_library{
+		name: "java-lib-3",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+				"srcs": `["a.java"]`,
+				"deps": `[":java-lib-2-neverlink"] + select({
+        "//build/bazel/platforms/os:android": [":java-lib-3-neverlink"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+			MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
+			MakeBazelTarget("java_library", "java-lib-3", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-3"),
+		},
+	})
+}
+
+func TestJavaLibraryArchVariantSrcsWithExcludes(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with arch variant libs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java", "b.java"],
+    target: {
+        android: {
+            exclude_srcs: ["a.java"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+				"srcs": `["b.java"] + select({
+        "//build/bazel/platforms/os:android": [],
+        "//conditions:default": ["a.java"],
+    })`,
+			}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+		},
+	})
+}
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
index d9049d4..8c6337b 100644
--- a/bp2build/java_plugin_conversion_test.go
+++ b/bp2build/java_plugin_conversion_test.go
@@ -60,7 +60,7 @@
         "//conditions:default": [],
     })`,
 				"deps": `[
-        ":java-lib-1",
+        ":java-lib-1-neverlink",
         ":java-lib-2",
     ]`,
 				"srcs": `[
@@ -101,7 +101,7 @@
         "//conditions:default": [],
     })`,
 				"deps": `[
-        ":java-lib-1",
+        ":java-lib-1-neverlink",
         ":java-lib-2",
     ]`,
 			}),
diff --git a/bp2build/java_sdk_library_conversion_test.go b/bp2build/java_sdk_library_conversion_test.go
new file mode 100644
index 0000000..9ce7446
--- /dev/null
+++ b/bp2build/java_sdk_library_conversion_test.go
@@ -0,0 +1,148 @@
+// 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"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func runJavaSdkLibraryTestCaseWithRegistrationCtxFunc(t *testing.T, tc Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "java_sdk_library"
+	(&tc).ModuleTypeUnderTestFactory = java.SdkLibraryFactory
+	RunBp2BuildTestCase(t, registrationCtxFunc, tc)
+}
+
+func runJavaSdkLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	runJavaSdkLibraryTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {})
+}
+
+func TestJavaSdkLibraryApiSurfaceGeneral(t *testing.T) {
+	runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+		Description: "limited java_sdk_library for api surfaces, general conversion",
+		Filesystem: map[string]string{
+			"build/soong/scripts/gen-java-current-api-files.sh": "",
+			"api/current.txt":               "",
+			"api/system-current.txt":        "",
+			"api/test-current.txt":          "",
+			"api/module-lib-current.txt":    "",
+			"api/system-server-current.txt": "",
+			"api/removed.txt":               "",
+			"api/system-removed.txt":        "",
+			"api/test-removed.txt":          "",
+			"api/module-lib-removed.txt":    "",
+			"api/system-server-removed.txt": "",
+		},
+		Blueprint: `java_sdk_library {
+    name: "java-sdk-lib",
+    srcs: ["a.java"],
+    public: {enabled: true},
+    system: {enabled: true},
+    test: {enabled: true},
+    module_lib: {enabled: true},
+    system_server: {enabled: true},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{
+				"public":        `"api/current.txt"`,
+				"system":        `"api/system-current.txt"`,
+				"test":          `"api/test-current.txt"`,
+				"module_lib":    `"api/module-lib-current.txt"`,
+				"system_server": `"api/system-server-current.txt"`,
+			}),
+		},
+	})
+}
+
+func TestJavaSdkLibraryApiSurfacePublicDefault(t *testing.T) {
+	runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+		Description: "limited java_sdk_library for api surfaces, public prop uses default value",
+		Filesystem: map[string]string{
+			"build/soong/scripts/gen-java-current-api-files.sh": "",
+			"api/current.txt":               "",
+			"api/system-current.txt":        "",
+			"api/test-current.txt":          "",
+			"api/module-lib-current.txt":    "",
+			"api/system-server-current.txt": "",
+			"api/removed.txt":               "",
+			"api/system-removed.txt":        "",
+			"api/test-removed.txt":          "",
+			"api/module-lib-removed.txt":    "",
+			"api/system-server-removed.txt": "",
+		},
+		Blueprint: `java_sdk_library {
+    name: "java-sdk-lib",
+    srcs: ["a.java"],
+    system: {enabled: false},
+    test: {enabled: false},
+    module_lib: {enabled: false},
+    system_server: {enabled: false},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{
+				"public": `"api/current.txt"`,
+			}),
+		},
+	})
+}
+
+func TestJavaSdkLibraryApiSurfacePublicNotEnabled(t *testing.T) {
+	runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+		Description: "limited java_sdk_library for api surfaces, public enable is false",
+		Filesystem: map[string]string{
+			"build/soong/scripts/gen-java-current-api-files.sh": "",
+			"api/current.txt": "",
+			"api/removed.txt": "",
+		},
+		Blueprint: `java_sdk_library {
+   name: "java-sdk-lib",
+   srcs: ["a.java"],
+   public: {enabled: false},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{}),
+		},
+	})
+}
+
+func TestJavaSdkLibraryApiSurfaceNoScopeIsSet(t *testing.T) {
+	runJavaSdkLibraryTestCase(t, Bp2buildTestCase{
+		Description: "limited java_sdk_library for api surfaces, none of the api scopes is set",
+		Filesystem: map[string]string{
+			"build/soong/scripts/gen-java-current-api-files.sh": "",
+			"api/current.txt":        "",
+			"api/system-current.txt": "",
+			"api/test-current.txt":   "",
+			"api/removed.txt":        "",
+			"api/system-removed.txt": "",
+			"api/test-removed.txt":   "",
+		},
+		Blueprint: `java_sdk_library {
+   name: "java-sdk-lib",
+   srcs: ["a.java"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_sdk_library", "java-sdk-lib", AttrNameToString{
+				"public": `"api/current.txt"`,
+				"system": `"api/system-current.txt"`,
+				"test":   `"api/test-current.txt"`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index bd21629..a020650 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -51,7 +51,7 @@
 // Print the codegen metrics to stdout.
 func (metrics *CodegenMetrics) Print() {
 	generatedTargetCount := uint64(0)
-	for _, ruleClass := range android.SortedStringKeys(metrics.serialized.RuleClassCount) {
+	for _, ruleClass := range android.SortedKeys(metrics.serialized.RuleClassCount) {
 		count := metrics.serialized.RuleClassCount[ruleClass]
 		fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
 		generatedTargetCount += count
@@ -148,11 +148,26 @@
 func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) {
 	metrics.serialized.Events = append(metrics.serialized.Events, event)
 }
+
 func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
 	metrics.serialized.UnconvertedModuleCount += 1
 	metrics.serialized.TotalModuleTypeCount[moduleType] += 1
 }
 
+func (metrics *CodegenMetrics) SetSymlinkCount(n uint64) {
+	if m := metrics.serialized.WorkspaceSymlinkCount; m != 0 {
+		fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceSymlinkCount of %d", m)
+	}
+	metrics.serialized.WorkspaceSymlinkCount = n
+}
+
+func (metrics *CodegenMetrics) SetMkDirCount(n uint64) {
+	if m := metrics.serialized.WorkspaceMkDirCount; m != 0 {
+		fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceDirCount of %d", m)
+	}
+	metrics.serialized.WorkspaceMkDirCount = n
+}
+
 func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
 	return metrics.serialized.HandCraftedModuleCount +
 		metrics.serialized.GeneratedModuleCount +
@@ -173,6 +188,10 @@
 )
 
 func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
+	//a package module has empty name
+	if moduleType == "package" {
+		return
+	}
 	// Undo prebuilt_ module name prefix modifications
 	moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
 	metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index 89be767..ba42f34 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -742,6 +742,101 @@
 )`}})
 }
 
+func TestSoongConfigModuleType_UnsetConditionsExcludeLibs(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: ["shared_libs"],
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_lib_a_defaults",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+                shared_libs: [
+                    "lib_a",
+                ],
+            },
+        },
+    },
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_merged_defaults",
+    defaults: ["library_linking_strategy_lib_a_defaults"],
+    host_supported: true,
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+                shared_libs: [
+                    "lib_b",
+                    "lib_c",
+                ],
+            },
+        },
+    },
+    exclude_shared_libs: ["lib_a"],
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary",
+    defaults: ["library_linking_strategy_merged_defaults"],
+    include_build_directory: false,
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary_with_excludes",
+    defaults: ["library_linking_strategy_merged_defaults"],
+    exclude_shared_libs: ["lib_c"],
+    include_build_directory: false,
+}`
+
+	otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_c", bazel_module: { bp2build_available: false } }
+`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem: map[string]string{
+			"foo/bar/Android.bp": otherDeps,
+		},
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary", AttrNameToString{
+				"dynamic_deps": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": [
+            "//foo/bar:lib_b",
+            "//foo/bar:lib_c",
+        ],
+    })`,
+			}),
+			MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary_with_excludes", AttrNameToString{
+				"dynamic_deps": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": ["//foo/bar:lib_b"],
+    })`,
+			}),
+		}})
+}
+
 func TestSoongConfigModuleType_Defaults(t *testing.T) {
 	bp := `
 soong_config_string_variable {
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 81ec7ee..aac5e7d 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -15,17 +15,17 @@
 package bp2build
 
 import (
-	"errors"
 	"fmt"
-	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"sync"
 	"sync/atomic"
 
 	"android/soong/shared"
+	"github.com/google/blueprint/pathtools"
 )
 
 // A tree structure that describes what to do at each directory in the created
@@ -46,62 +46,10 @@
 	topdir  string // $TOPDIR
 
 	// State
-	wg    sync.WaitGroup
-	depCh chan string
-	okay  atomic.Bool // Whether the forest was successfully constructed
-}
-
-// A simple thread pool to limit concurrency on system calls.
-// Necessary because Go spawns a new OS-level thread for each blocking system
-// call. This means that if syscalls are too slow and there are too many of
-// them, the hard limit on OS-level threads can be exhausted.
-type syscallPool struct {
-	shutdownCh []chan<- struct{}
-	workCh     chan syscall
-}
-
-type syscall struct {
-	work func()
-	done chan<- struct{}
-}
-
-func createSyscallPool(count int) *syscallPool {
-	result := &syscallPool{
-		shutdownCh: make([]chan<- struct{}, count),
-		workCh:     make(chan syscall),
-	}
-
-	for i := 0; i < count; i++ {
-		shutdownCh := make(chan struct{})
-		result.shutdownCh[i] = shutdownCh
-		go result.worker(shutdownCh)
-	}
-
-	return result
-}
-
-func (p *syscallPool) do(work func()) {
-	doneCh := make(chan struct{})
-	p.workCh <- syscall{work, doneCh}
-	<-doneCh
-}
-
-func (p *syscallPool) shutdown() {
-	for _, ch := range p.shutdownCh {
-		ch <- struct{}{} // Blocks until the value is received
-	}
-}
-
-func (p *syscallPool) worker(shutdownCh <-chan struct{}) {
-	for {
-		select {
-		case <-shutdownCh:
-			return
-		case work := <-p.workCh:
-			work.work()
-			work.done <- struct{}{}
-		}
-	}
+	wg           sync.WaitGroup
+	depCh        chan string
+	mkdirCount   atomic.Uint64
+	symlinkCount atomic.Uint64
 }
 
 // Ensures that the node for the given path exists in the tree and returns it.
@@ -169,25 +117,13 @@
 		generatedBuildFileContent = packageDefaultVisibilityRegex.ReplaceAll(generatedBuildFileContent, []byte{})
 	}
 
-	outFile, err := os.Create(output)
-	if err != nil {
-		return err
+	newContents := generatedBuildFileContent
+	if newContents[len(newContents)-1] != '\n' {
+		newContents = append(newContents, '\n')
 	}
+	newContents = append(newContents, srcBuildFileContent...)
 
-	_, err = outFile.Write(generatedBuildFileContent)
-	if err != nil {
-		return err
-	}
-
-	if generatedBuildFileContent[len(generatedBuildFileContent)-1] != '\n' {
-		_, err = outFile.WriteString("\n")
-		if err != nil {
-			return err
-		}
-	}
-
-	_, err = outFile.Write(srcBuildFileContent)
-	return err
+	return pathtools.WriteFileIfChanged(output, newContents, 0666)
 }
 
 // Calls readdir() and returns it as a map from the basename of the files in dir
@@ -215,12 +151,35 @@
 }
 
 // Creates a symbolic link at dst pointing to src
-func symlinkIntoForest(topdir, dst, src string) {
-	err := os.Symlink(shared.JoinPath(topdir, src), shared.JoinPath(topdir, dst))
-	if err != nil {
+func symlinkIntoForest(topdir, dst, src string) uint64 {
+	srcPath := shared.JoinPath(topdir, src)
+	dstPath := shared.JoinPath(topdir, dst)
+
+	// Check if a symlink already exists.
+	if dstInfo, err := os.Lstat(dstPath); err != nil {
+		if !os.IsNotExist(err) {
+			fmt.Fprintf(os.Stderr, "Failed to lstat '%s': %s", dst, err)
+			os.Exit(1)
+		}
+	} else {
+		if dstInfo.Mode()&os.ModeSymlink != 0 {
+			// Assume that the link's target is correct, i.e. no manual tampering.
+			// E.g. OUT_DIR could have been previously used with a different source tree check-out!
+			return 0
+		} else {
+			if err := os.RemoveAll(dstPath); err != nil {
+				fmt.Fprintf(os.Stderr, "Failed to remove '%s': %s", dst, err)
+				os.Exit(1)
+			}
+		}
+	}
+
+	// Create symlink.
+	if err := os.Symlink(srcPath, dstPath); err != nil {
 		fmt.Fprintf(os.Stderr, "Cannot create symlink at '%s' pointing to '%s': %s", dst, src, err)
 		os.Exit(1)
 	}
+	return 1
 }
 
 func isDir(path string, fi os.FileInfo) bool {
@@ -251,8 +210,9 @@
 	defer context.wg.Done()
 
 	if instructions != nil && instructions.excluded {
-		// This directory is not needed, bail out
-		return
+		// Excluded paths are skipped at the level of the non-excluded parent.
+		fmt.Fprintf(os.Stderr, "may not specify a root-level exclude directory '%s'", srcDir)
+		os.Exit(1)
 	}
 
 	// We don't add buildFilesDir here because the bp2build files marker files is
@@ -270,29 +230,63 @@
 				renamingBuildFile = true
 				srcDirMap["BUILD.bazel"] = srcDirMap["BUILD"]
 				delete(srcDirMap, "BUILD")
+				if instructions != nil {
+					if _, ok := instructions.children["BUILD"]; ok {
+						instructions.children["BUILD.bazel"] = instructions.children["BUILD"]
+						delete(instructions.children, "BUILD")
+					}
+				}
 			}
 		}
 	}
 
-	allEntries := make(map[string]struct{})
+	allEntries := make([]string, 0, len(srcDirMap)+len(buildFilesMap))
 	for n := range srcDirMap {
-		allEntries[n] = struct{}{}
+		allEntries = append(allEntries, n)
 	}
-
 	for n := range buildFilesMap {
-		allEntries[n] = struct{}{}
+		if _, ok := srcDirMap[n]; !ok {
+			allEntries = append(allEntries, n)
+		}
+	}
+	// Tests read the error messages generated, so ensure their order is deterministic
+	sort.Strings(allEntries)
+
+	fullForestPath := shared.JoinPath(context.topdir, forestDir)
+	createForestDir := false
+	if fi, err := os.Lstat(fullForestPath); err != nil {
+		if os.IsNotExist(err) {
+			createForestDir = true
+		} else {
+			fmt.Fprintf(os.Stderr, "Could not read info for '%s': %s\n", forestDir, err)
+		}
+	} else if fi.Mode()&os.ModeDir == 0 {
+		if err := os.RemoveAll(fullForestPath); err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to remove '%s': %s", forestDir, err)
+			os.Exit(1)
+		}
+		createForestDir = true
+	}
+	if createForestDir {
+		if err := os.MkdirAll(fullForestPath, 0777); err != nil {
+			fmt.Fprintf(os.Stderr, "Could not mkdir '%s': %s\n", forestDir, err)
+			os.Exit(1)
+		}
+		context.mkdirCount.Add(1)
 	}
 
-	err := os.MkdirAll(shared.JoinPath(context.topdir, forestDir), 0777)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Cannot mkdir '%s': %s\n", forestDir, err)
-		os.Exit(1)
-	}
+	// Start with a list of items that already exist in the forest, and remove
+	// each element as it is processed in allEntries. Any remaining items in
+	// forestMapForDeletion must be removed. (This handles files which were
+	// removed since the previous forest generation).
+	forestMapForDeletion := readdirToMap(shared.JoinPath(context.topdir, forestDir))
 
-	for f := range allEntries {
+	for _, f := range allEntries {
 		if f[0] == '.' {
 			continue // Ignore dotfiles
 		}
+		delete(forestMapForDeletion, f)
+		// todo add deletionCount metric
 
 		// The full paths of children in the input trees and in the output tree
 		forestChild := shared.JoinPath(forestDir, f)
@@ -303,13 +297,9 @@
 		buildFilesChild := shared.JoinPath(buildFilesDir, f)
 
 		// Descend in the instruction tree if it exists
-		var instructionsChild *instructionsNode = nil
+		var instructionsChild *instructionsNode
 		if instructions != nil {
-			if f == "BUILD.bazel" && renamingBuildFile {
-				instructionsChild = instructions.children["BUILD"]
-			} else {
-				instructionsChild = instructions.children[f]
-			}
+			instructionsChild = instructions.children[f]
 		}
 
 		srcChildEntry, sExists := srcDirMap[f]
@@ -317,7 +307,7 @@
 
 		if instructionsChild != nil && instructionsChild.excluded {
 			if bExists {
-				symlinkIntoForest(context.topdir, forestChild, buildFilesChild)
+				context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, buildFilesChild))
 			}
 			continue
 		}
@@ -333,7 +323,7 @@
 				go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
 			} else {
 				// Not in the source tree, symlink BUILD file
-				symlinkIntoForest(context.topdir, forestChild, buildFilesChild)
+				context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, buildFilesChild))
 			}
 		} else if !bExists {
 			if sDir && instructionsChild != nil {
@@ -343,7 +333,7 @@
 				go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
 			} else {
 				// Not in the build file tree, symlink source tree, carry on
-				symlinkIntoForest(context.topdir, forestChild, srcChild)
+				context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, srcChild))
 			}
 		} else if sDir && bDir {
 			// Both are directories. Descend.
@@ -356,82 +346,55 @@
 			// The Android.bp file that codegen used to produce `buildFilesChild` is
 			// already a dependency, we can ignore `buildFilesChild`.
 			context.depCh <- srcChild
-			err = mergeBuildFiles(shared.JoinPath(context.topdir, forestChild), srcBuildFile, generatedBuildFile, context.verbose)
-			if err != nil {
+			if err := mergeBuildFiles(shared.JoinPath(context.topdir, forestChild), srcBuildFile, generatedBuildFile, context.verbose); err != nil {
 				fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
 					srcBuildFile, generatedBuildFile, err)
-				context.okay.Store(false)
+				os.Exit(1)
 			}
 		} else {
 			// Both exist and one is a file. This is an error.
 			fmt.Fprintf(os.Stderr,
 				"Conflict in workspace symlink tree creation: both '%s' and '%s' exist and exactly one is a directory\n",
 				srcChild, buildFilesChild)
-			context.okay.Store(false)
-		}
-	}
-}
-
-func removeParallelRecursive(pool *syscallPool, path string, fi os.FileInfo, wg *sync.WaitGroup) {
-	defer wg.Done()
-
-	if fi.IsDir() {
-		children := readdirToMap(path)
-		childrenWg := &sync.WaitGroup{}
-		childrenWg.Add(len(children))
-
-		for child, childFi := range children {
-			go removeParallelRecursive(pool, shared.JoinPath(path, child), childFi, childrenWg)
-		}
-
-		childrenWg.Wait()
-	}
-
-	pool.do(func() {
-		if err := os.Remove(path); err != nil {
-			fmt.Fprintf(os.Stderr, "Cannot unlink '%s': %s\n", path, err)
 			os.Exit(1)
 		}
-	})
-}
-
-func removeParallel(path string) {
-	fi, err := os.Lstat(path)
-	if err != nil {
-		if errors.Is(err, fs.ErrNotExist) {
-			return
-		}
-
-		fmt.Fprintf(os.Stderr, "Cannot lstat '%s': %s\n", path, err)
-		os.Exit(1)
 	}
 
-	wg := &sync.WaitGroup{}
-	wg.Add(1)
+	// Remove all files in the forest that exist in neither the source
+	// tree nor the build files tree. (This handles files which were removed
+	// since the previous forest generation).
+	for f := range forestMapForDeletion {
+		var instructionsChild *instructionsNode
+		if instructions != nil {
+			instructionsChild = instructions.children[f]
+		}
 
-	// Random guess as to the best number of syscalls to run in parallel
-	pool := createSyscallPool(100)
-	removeParallelRecursive(pool, path, fi, wg)
-	pool.shutdown()
-
-	wg.Wait()
+		if instructionsChild != nil && instructionsChild.excluded {
+			// This directory may be excluded because bazel writes to it under the
+			// forest root. Thus this path is intentionally left alone.
+			continue
+		}
+		forestChild := shared.JoinPath(context.topdir, forestDir, f)
+		if err := os.RemoveAll(forestChild); err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to remove '%s/%s': %s", forestDir, f, err)
+			os.Exit(1)
+		}
+	}
 }
 
-// Creates a symlink forest by merging the directory tree at "buildFiles" and
+// PlantSymlinkForest Creates a symlink forest by merging the directory tree at "buildFiles" and
 // "srcDir" while excluding paths listed in "exclude". Returns the set of paths
 // under srcDir on which readdir() had to be called to produce the symlink
 // forest.
-func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) []string {
+func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) (deps []string, mkdirCount, symlinkCount uint64) {
 	context := &symlinkForestContext{
-		verbose: verbose,
-		topdir:  topdir,
-		depCh:   make(chan string),
+		verbose:      verbose,
+		topdir:       topdir,
+		depCh:        make(chan string),
+		mkdirCount:   atomic.Uint64{},
+		symlinkCount: atomic.Uint64{},
 	}
 
-	context.okay.Store(true)
-
-	removeParallel(shared.JoinPath(topdir, forest))
-
 	instructions := instructionsFromExcludePathList(exclude)
 	go func() {
 		context.wg.Add(1)
@@ -440,14 +403,9 @@
 		close(context.depCh)
 	}()
 
-	deps := make([]string, 0)
 	for dep := range context.depCh {
 		deps = append(deps, dep)
 	}
 
-	if !context.okay.Load() {
-		os.Exit(1)
-	}
-
-	return deps
+	return deps, context.mkdirCount.Load(), context.symlinkCount.Load()
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index c340a8f..856b6ee 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -21,6 +21,7 @@
 
 import (
 	"fmt"
+	"sort"
 	"strings"
 	"testing"
 
@@ -227,14 +228,15 @@
 //
 // If ignoreUnexpected=true then it will ignore directories for which there are no expected targets.
 func (b BazelTestResult) CompareAllBazelTargets(t *testing.T, description string, expectedTargets map[string][]string, ignoreUnexpected bool) {
+	t.Helper()
 	actualTargets := b.buildFileToTargets
 
 	// Generate the sorted set of directories to check.
-	dirsToCheck := android.SortedStringKeys(expectedTargets)
+	dirsToCheck := android.SortedKeys(expectedTargets)
 	if !ignoreUnexpected {
 		// This needs to perform an exact match so add the directories in which targets were
 		// produced to the list of directories to check.
-		dirsToCheck = append(dirsToCheck, android.SortedStringKeys(actualTargets)...)
+		dirsToCheck = append(dirsToCheck, android.SortedKeys(actualTargets)...)
 		dirsToCheck = android.SortedUniqueStrings(dirsToCheck)
 	}
 
@@ -258,10 +260,17 @@
 }
 
 func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
+	t.Helper()
 	if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
 		t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
 			description, expectedCount, expectedContents, actualCount, actualTargets)
 	} else {
+		sort.SliceStable(actualTargets, func(i, j int) bool {
+			return actualTargets[i].name < actualTargets[j].name
+		})
+		sort.SliceStable(expectedContents, func(i, j int) bool {
+			return getTargetName(expectedContents[i]) < getTargetName(expectedContents[j])
+		})
 		for i, actualTarget := range actualTargets {
 			if w, g := expectedContents[i], actualTarget.content; w != g {
 				t.Errorf(
@@ -453,7 +462,7 @@
 			}
 		}
 	}
-	productVariableProps := android.ProductVariableProperties(ctx)
+	productVariableProps := android.ProductVariableProperties(ctx, ctx.Module())
 	if props, ok := productVariableProps["String_literal_prop"]; ok {
 		for c, p := range props {
 			if val, ok := p.(*string); ok {
@@ -578,7 +587,7 @@
 	if name != "" {
 		attrStrings = append(attrStrings, fmt.Sprintf(`    name = "%s",`, name))
 	}
-	for _, k := range android.SortedStringKeys(attrs) {
+	for _, k := range android.SortedKeys(attrs) {
 		attrStrings = append(attrStrings, fmt.Sprintf("    %s = %s,", k, attrs[k]))
 	}
 	return fmt.Sprintf(`%s(
@@ -615,16 +624,18 @@
 		return ""
 	}
 	STUB_SUITE_ATTRS := map[string]string{
-		"stubs_symbol_file": "symbol_file",
-		"stubs_versions":    "versions",
-		"soname":            "soname",
-		"source_library":    "source_library",
+		"stubs_symbol_file":    "symbol_file",
+		"stubs_versions":       "versions",
+		"soname":               "soname",
+		"source_library_label": "source_library_label",
 	}
 
 	stubSuiteAttrs := AttrNameToString{}
 	for key, _ := range attrs {
 		if _, stubSuiteAttr := STUB_SUITE_ATTRS[key]; stubSuiteAttr {
 			stubSuiteAttrs[STUB_SUITE_ATTRS[key]] = attrs[key]
+		} else {
+			panic(fmt.Sprintf("unused cc_stub_suite attr %q\n", key))
 		}
 	}
 	return MakeBazelTarget("cc_stub_suite", name+"_stub_libs", stubSuiteAttrs)
@@ -636,3 +647,13 @@
 		"exports":   `[":` + name + `"]`,
 	})
 }
+
+func getTargetName(targetContent string) string {
+	data := strings.Split(targetContent, "name = \"")
+	if len(data) < 2 {
+		return ""
+	} else {
+		endIndex := strings.Index(data[1], "\"")
+		return data[1][:endIndex]
+	}
+}
diff --git a/build_test.bash b/build_test.bash
index eda4beb..defdd82 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -34,9 +34,15 @@
     aosp_riscv64
 )
 
-# To track how long we took to startup. %N isn't supported on Darwin, but
-# that's detected in the Go code, which skips calculating the startup time.
-export TRACE_BEGIN_SOONG=$(date +%s%N)
+# To track how long we took to startup.
+case $(uname -s) in
+  Darwin)
+    export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
+    ;;
+  *)
+    export TRACE_BEGIN_SOONG=$(date +%s%N)
+    ;;
+esac
 
 # Remove BUILD_NUMBER so that incremental builds on build servers don't
 # re-read makefiles every time.
diff --git a/cc/Android.bp b/cc/Android.bp
index 8860f78..5fd9afe 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -52,7 +52,6 @@
         "vndk.go",
         "vndk_prebuilt.go",
 
-        "cflag_artifacts.go",
         "cmakelists.go",
         "compdb.go",
         "compiler.go",
diff --git a/cc/OWNERS b/cc/OWNERS
index ffbf14a..c4d82d2 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -1,4 +1 @@
 per-file ndk_*.go = danalbert@google.com
-per-file tidy*.go = srhines@google.com, chh@google.com
-per-file afdo.go,afdo_test.go,lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
-per-file coverage.go = pirama@google.com, srhines@google.com, allenhair@google.com
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ce35b5c..980dd07 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -124,14 +124,17 @@
 						}
 					}
 				}
-				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
+				if c.Properties.IsSdkVariant {
 					// Make the SDK variant uninstallable so that there are not two rules to install
 					// to the same location.
 					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
-					// dependencies to the .sdk suffix when building a module that uses the SDK.
-					entries.SetString("SOONG_SDK_VARIANT_MODULES",
-						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
+
+					if c.Properties.SdkAndPlatformVariantVisibleToMake {
+						// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
+						// dependencies to the .sdk suffix when building a module that uses the SDK.
+						entries.SetString("SOONG_SDK_VARIANT_MODULES",
+							"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
+					}
 				}
 			},
 		},
diff --git a/cc/api_level.go b/cc/api_level.go
index fdff5cb..a5571f3 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,9 @@
 	"android/soong/android"
 )
 
-func minApiForArch(ctx android.EarlyModuleContext,
+// MinApiLevelForArch returns the ApiLevel for the Android version that
+// first supported the architecture.
+func MinApiForArch(ctx android.EarlyModuleContext,
 	arch android.ArchType) android.ApiLevel {
 
 	switch arch {
@@ -38,7 +40,7 @@
 func nativeApiLevelFromUser(ctx android.BaseModuleContext,
 	raw string) (android.ApiLevel, error) {
 
-	min := minApiForArch(ctx, ctx.Arch().ArchType)
+	min := MinApiForArch(ctx, ctx.Arch().ArchType)
 	if raw == "minimum" {
 		return min, nil
 	}
diff --git a/cc/binary.go b/cc/binary.go
index 54c1abc..097f822 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -151,7 +151,7 @@
 // modules common to most binaries, such as bionic libraries.
 func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
-	if !Bool(binary.baseLinker.Properties.Nocrt) {
+	if binary.baseLinker.Properties.crt() {
 		if binary.static() {
 			deps.CrtBegin = ctx.toolchain().CrtBeginStaticBinary()
 			deps.CrtEnd = ctx.toolchain().CrtEndStaticBinary()
@@ -512,7 +512,7 @@
 		}
 		binary.baseInstaller.subDir = "bootstrap"
 	}
-	binary.baseInstaller.install(ctx, file)
+	binary.baseInstaller.installExecutable(ctx, file)
 
 	var preferredArchSymlinkPath android.OptionalPath
 	for _, symlink := range binary.symlinks {
@@ -577,20 +577,26 @@
 
 func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKey(ctx))
+	info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
 	}
 
-	outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
+	var outputFilePath android.Path = android.PathForBazelOut(ctx, info.OutputFile)
+	if len(info.TidyFiles) > 0 {
+		handler.module.tidyFiles = android.PathsForBazelOut(ctx, info.TidyFiles)
+		outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+	}
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 	handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+	handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
 }
 
 func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
@@ -624,8 +630,6 @@
 		Local_includes:    baseAttrs.localIncludes,
 		Absolute_includes: baseAttrs.absoluteIncludes,
 		Linkopts:          baseAttrs.linkopts,
-		Link_crt:          baseAttrs.linkCrt,
-		Use_libcrt:        baseAttrs.useLibcrt,
 		Use_version_lib:   baseAttrs.useVersionLib,
 		Rtti:              baseAttrs.rtti,
 		Stl:               baseAttrs.stl,
@@ -689,10 +693,7 @@
 
 	Linkopts                 bazel.StringListAttribute
 	Additional_linker_inputs bazel.LabelListAttribute
-
-	Link_crt        bazel.BoolAttribute
-	Use_libcrt      bazel.BoolAttribute
-	Use_version_lib bazel.BoolAttribute
+	Use_version_lib          bazel.BoolAttribute
 
 	Rtti    bazel.BoolAttribute
 	Stl     *string
diff --git a/cc/binary_test.go b/cc/binary_test.go
index 43aff5c..2fac002 100644
--- a/cc/binary_test.go
+++ b/cc/binary_test.go
@@ -15,9 +15,10 @@
 package cc
 
 import (
-	"android/soong/bazel/cquery"
 	"testing"
 
+	"android/soong/bazel/cquery"
+
 	"android/soong/android"
 )
 
@@ -53,6 +54,50 @@
 	unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
 	expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped"
 	android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, binMod)[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_binary", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+}
+
+func TestCcBinaryWithBazelValidations(t *testing.T) {
+	t.Parallel()
+	bp := `
+cc_binary {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+	tidy: true,
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+			"//foo/bar:bar": cquery.CcUnstrippedInfo{
+				OutputFile:       "foo",
+				UnstrippedOutput: "foo.unstripped",
+				TidyFiles:        []string{"foo.c.tidy"},
+			},
+		},
+	}
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureMergeEnv(map[string]string{
+			"ALLOW_LOCAL_TIDY_TRUE": "1",
+		}),
+	).RunTestWithConfig(t, config).TestContext
+
+	binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+	producer := binMod.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm64_armv8-a/validated/foo"}
+	android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+	unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+	expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped"
+	android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
 }
 
 func TestBinaryLinkerScripts(t *testing.T) {
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 6f97260..7c817a2 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -65,13 +65,15 @@
 
 	Native_coverage *bool
 
+	Apex_available []string
+
 	sdkAttributes
 
 	tidyAttributes
 }
 
 type tidyAttributes struct {
-	Tidy                  *bool
+	Tidy                  *string
 	Tidy_flags            []string
 	Tidy_checks           []string
 	Tidy_checks_as_errors []string
@@ -82,7 +84,15 @@
 func (m *Module) convertTidyAttributes(ctx android.BaseMutatorContext, moduleAttrs *tidyAttributes) {
 	for _, f := range m.features {
 		if tidy, ok := f.(*tidyFeature); ok {
-			moduleAttrs.Tidy = tidy.Properties.Tidy
+			var tidyAttr *string
+			if tidy.Properties.Tidy != nil {
+				if *tidy.Properties.Tidy {
+					tidyAttr = proptools.StringPtr("local")
+				} else {
+					tidyAttr = proptools.StringPtr("never")
+				}
+			}
+			moduleAttrs.Tidy = tidyAttr
 			moduleAttrs.Tidy_flags = tidy.Properties.Tidy_flags
 			moduleAttrs.Tidy_checks = tidy.Properties.Tidy_checks
 			moduleAttrs.Tidy_checks_as_errors = tidy.Properties.Tidy_checks_as_errors
@@ -212,7 +222,7 @@
 }
 
 // Parses properties common to static and shared libraries. Also used for prebuilt libraries.
-func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, _ *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
@@ -236,13 +246,16 @@
 	//    empty list -> no values specified
 	attrs.System_dynamic_deps.ForceSpecifyEmptyList = true
 
+	var apexAvailable []string
 	if isStatic {
+		apexAvailable = lib.StaticProperties.Static.Apex_available
 		bp2BuildPropParseHelper(ctx, module, &StaticProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
 			if staticOrSharedProps, ok := props.(*StaticProperties); ok {
 				setAttrs(axis, config, staticOrSharedProps.Static)
 			}
 		})
 	} else {
+		apexAvailable = lib.SharedProperties.Shared.Apex_available
 		bp2BuildPropParseHelper(ctx, module, &SharedProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
 			if staticOrSharedProps, ok := props.(*SharedProperties); ok {
 				setAttrs(axis, config, staticOrSharedProps.Shared)
@@ -255,6 +268,8 @@
 	attrs.Srcs_c = partitionedSrcs[cSrcPartition]
 	attrs.Srcs_as = partitionedSrcs[asSrcPartition]
 
+	attrs.Apex_available = android.ConvertApexAvailableToTags(apexAvailable)
+
 	if !partitionedSrcs[protoSrcPartition].IsEmpty() {
 		// TODO(b/208815215): determine whether this is used and add support if necessary
 		ctx.ModuleErrorf("Migrating static/shared only proto srcs is not currently supported")
@@ -702,7 +717,13 @@
 	compilerAttrs := compilerAttributes{}
 	linkerAttrs := linkerAttributes{}
 
-	for axis, configs := range axisToConfigs {
+	// Iterate through these axes in a deterministic order. This is required
+	// because processing certain dependencies may result in concatenating
+	// elements along other axes. (For example, processing NoConfig may result
+	// in elements being added to InApex). This is thus the only way to ensure
+	// that the order of entries in each list is in a predictable order.
+	for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
+		configs := axisToConfigs[axis]
 		for cfg := range configs {
 			var allHdrs []string
 			if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
@@ -718,7 +739,7 @@
 			if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
 				exportHdrs = baseLinkerProps.Export_generated_headers
 
-				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, cfg, baseLinkerProps)
+				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module, axis, cfg, baseLinkerProps)
 			}
 			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
 			implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
@@ -760,7 +781,7 @@
 		nativeCoverage = BoolPtr(false)
 	}
 
-	productVariableProps := android.ProductVariableProperties(ctx)
+	productVariableProps := android.ProductVariableProperties(ctx, ctx.Module())
 
 	(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
 	(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
@@ -811,8 +832,11 @@
 	compilerAttrs.hdrs.Prepend = true
 
 	features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
+	features = features.Append(bp2buildLtoFeatures(ctx, module))
 	features.DeduplicateAxesFromBase()
 
+	addMuslSystemDynamicDeps(ctx, linkerAttrs)
+
 	return baseAttributes{
 		compilerAttrs,
 		linkerAttrs,
@@ -823,6 +847,16 @@
 	}
 }
 
+// As a workaround for b/261657184, we are manually adding the default value
+// of system_dynamic_deps for the linux_musl os.
+// TODO: Solve this properly
+func addMuslSystemDynamicDeps(ctx android.Bp2buildMutatorContext, attrs linkerAttributes) {
+	systemDynamicDeps := attrs.systemDynamicDeps.SelectValue(bazel.OsConfigurationAxis, "linux_musl")
+	if attrs.systemDynamicDeps.HasAxisSpecificValues(bazel.OsConfigurationAxis) && systemDynamicDeps.IsNil() {
+		attrs.systemDynamicDeps.SetSelectValue(bazel.OsConfigurationAxis, "linux_musl", android.BazelLabelForModuleDeps(ctx, config.MuslDefaultSharedLibraries))
+	}
+}
+
 type fdoProfileAttributes struct {
 	Absolute_path_profile string
 }
@@ -873,6 +907,9 @@
 			return false
 		})
 
+		apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+		sdkAttrs := bp2BuildParseSdkAttributes(m)
+
 		if !aidlSrcs.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
 			ctx.CreateBazelTargetModule(
@@ -883,6 +920,7 @@
 				android.CommonAttributes{Name: aidlLibName},
 				&aidlLibraryAttributes{
 					Srcs: aidlSrcs,
+					Tags: apexAvailableTags,
 				},
 			)
 			aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
@@ -890,15 +928,12 @@
 
 		if !aidlLibs.IsEmpty() {
 			ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
-			// Since cc_aidl_library only needs the dynamic deps (aka shared libs) from the parent cc library for compiling,
-			// we err on the side of not re-exporting the headers of the dynamic deps from cc_aidl_lirary
-			// because the parent cc library already has all the dynamic deps
-			implementationDynamicDeps := bazel.MakeLabelListAttribute(
-				bazel.AppendBazelLabelLists(
-					linkerAttrs.dynamicDeps.Value,
-					linkerAttrs.implementationDynamicDeps.Value,
-				),
-			)
+			// Since parent cc_library already has these dependencies, we can add them as implementation
+			// deps so that they don't re-export
+			implementationDeps := linkerAttrs.deps.Clone()
+			implementationDeps.Append(linkerAttrs.implementationDeps)
+			implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
+			implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
 
 			ctx.CreateBazelTargetModule(
 				bazel.BazelTargetModuleProperties{
@@ -908,7 +943,10 @@
 				android.CommonAttributes{Name: ccAidlLibrarylabel},
 				&ccAidlLibraryAttributes{
 					Deps:                        aidlLibs,
-					Implementation_dynamic_deps: implementationDynamicDeps,
+					Implementation_deps:         *implementationDeps,
+					Implementation_dynamic_deps: *implementationDynamicDeps,
+					Tags:                        apexAvailableTags,
+					sdkAttributes:               sdkAttrs,
 				},
 			)
 			label := &bazel.LabelAttribute{
@@ -947,8 +985,6 @@
 	systemDynamicDeps                bazel.LabelListAttribute
 	usedSystemDynamicDepAsDynamicDep map[string]bool
 
-	linkCrt                       bazel.BoolAttribute
-	useLibcrt                     bazel.BoolAttribute
 	useVersionLib                 bazel.BoolAttribute
 	linkopts                      bazel.StringListAttribute
 	additionalLinkerInputs        bazel.LabelListAttribute
@@ -967,37 +1003,26 @@
 
 // resolveTargetApex re-adds the shared and static libs in target.apex.exclude_shared|static_libs props to non-apex variant
 // since all libs are already excluded by default
-func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, isBinary bool, props *BaseLinkerProperties) {
-	sharedLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
-		ctx,
-		true,
-		props.Target.Apex.Exclude_shared_libs,
-		props.Export_shared_lib_headers,
-		bazelLabelForSharedDeps,
-	)
-	dynamicDeps := la.dynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	implDynamicDeps := la.implementationDynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	(&dynamicDeps).Append(sharedLibsForNonApex.export)
-	(&implDynamicDeps).Append(sharedLibsForNonApex.implementation)
-	la.dynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, dynamicDeps)
-	la.implementationDynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDynamicDeps)
+func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, props *BaseLinkerProperties) {
+	excludeSharedLibs := bazelLabelForSharedDeps(ctx, props.Target.Apex.Exclude_shared_libs)
+	sharedExcludes := bazel.LabelList{Excludes: excludeSharedLibs.Includes}
+	sharedExcludesLabelList := bazel.LabelListAttribute{}
+	sharedExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, sharedExcludes)
 
-	staticLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
-		ctx,
-		!isBinary,
-		props.Target.Apex.Exclude_static_libs,
-		props.Export_static_lib_headers,
-		bazelLabelForSharedDeps,
-	)
-	deps := la.deps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	implDeps := la.implementationDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	(&deps).Append(staticLibsForNonApex.export)
-	(&implDeps).Append(staticLibsForNonApex.implementation)
-	la.deps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, deps)
-	la.implementationDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDeps)
+	la.dynamicDeps.Append(sharedExcludesLabelList)
+	la.implementationDynamicDeps.Append(sharedExcludesLabelList)
+
+	excludeStaticLibs := bazelLabelForStaticDeps(ctx, props.Target.Apex.Exclude_static_libs)
+	staticExcludes := bazel.LabelList{Excludes: excludeStaticLibs.Includes}
+	staticExcludesLabelList := bazel.LabelListAttribute{}
+	staticExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, staticExcludes)
+
+	la.deps.Append(staticExcludesLabelList)
+	la.implementationDeps.Append(staticExcludesLabelList)
 }
 
-func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
+func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, module *Module, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
+	isBinary := module.Binary()
 	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
 	var axisFeatures []string
 
@@ -1028,8 +1053,7 @@
 		ctx,
 		!isBinary,
 		staticLibs,
-		// Exclude static libs in Exclude_static_libs and Target.Apex.Exclude_static_libs props
-		append(props.Exclude_static_libs, props.Target.Apex.Exclude_static_libs...),
+		props.Exclude_static_libs,
 		props.Export_static_lib_headers,
 		bazelLabelForStaticDepsExcludes,
 	)
@@ -1068,53 +1092,23 @@
 		ctx,
 		!isBinary,
 		sharedLibs,
-		// Exclude shared libs in Exclude_shared_libs and Target.Apex.Exclude_shared_libs props
-		append(props.Exclude_shared_libs, props.Target.Apex.Exclude_shared_libs...),
+		props.Exclude_shared_libs,
 		props.Export_shared_lib_headers,
 		bazelLabelForSharedDepsExcludes,
 	)
 	la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
 	la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
-	la.resolveTargetApexProp(ctx, isBinary, props)
+	la.resolveTargetApexProp(ctx, props)
 
 	if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
-		// If a dependency in la.implementationDynamicDeps has stubs, its stub variant should be
-		// used when the dependency is linked in a APEX. The dependencies in NoConfigAxis and
-		// OsConfigurationAxis/OsAndroid are grouped by having stubs or not, so Bazel select()
-		// statement can be used to choose source/stub variants of them.
-		depsWithStubs := []bazel.Label{}
-		for _, l := range sharedDeps.implementation.Includes {
-			dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
-			if m, ok := dep.(*Module); ok && m.HasStubsVariants() {
-				depsWithStubs = append(depsWithStubs, l)
-			}
-		}
-		if len(depsWithStubs) > 0 {
-			implDynamicDeps := bazel.SubtractBazelLabelList(sharedDeps.implementation, bazel.MakeLabelList(depsWithStubs))
-			la.implementationDynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
-
-			stubLibLabels := []bazel.Label{}
-			for _, l := range depsWithStubs {
-				l.Label = l.Label + stubsSuffix
-				stubLibLabels = append(stubLibLabels, l)
-			}
-			inApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
-			nonApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
-			defaultSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
-			if axis == bazel.NoConfigAxis {
-				(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
-				(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-				(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
-			} else if config == bazel.OsAndroid {
-				(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
-				(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
-			}
-		}
+		// If a dependency in la.implementationDynamicDeps or la.dynamicDeps has stubs, its
+		// stub variant should be used when the dependency is linked in a APEX. The
+		// dependencies in NoConfigAxis and OsConfigurationAxis/OsAndroid are grouped by
+		// having stubs or not, so Bazel select() statement can be used to choose
+		// source/stub variants of them.
+		apexAvailable := module.ApexAvailable()
+		setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.export, &la.dynamicDeps, 0)
+		setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.implementation, &la.implementationDynamicDeps, 1)
 	}
 
 	if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
@@ -1134,6 +1128,13 @@
 		}
 	}
 
+	if !props.libCrt() {
+		axisFeatures = append(axisFeatures, "-use_libcrt")
+	}
+	if !props.crt() {
+		axisFeatures = append(axisFeatures, "-link_crt")
+	}
+
 	// This must happen before the addition of flags for Version Script and
 	// Dynamic List, as these flags must be split on spaces and those must not
 	linkerFlags = parseCommandLineFlags(linkerFlags, filterOutClangUnknownCflags)
@@ -1153,16 +1154,6 @@
 
 	la.additionalLinkerInputs.SetSelectValue(axis, config, additionalLinkerInputs)
 	la.linkopts.SetSelectValue(axis, config, linkerFlags)
-	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
-
-	// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
-	if props.crt() != nil {
-		if axis == bazel.NoConfigAxis {
-			la.linkCrt.SetSelectValue(axis, config, props.crt())
-		} else if axis == bazel.ArchConfigurationAxis {
-			ctx.ModuleErrorf("nocrt is not supported for arch variants")
-		}
-	}
 
 	if axisFeatures != nil {
 		la.features.SetSelectValue(axis, config, axisFeatures)
@@ -1174,6 +1165,61 @@
 	}
 }
 
+var (
+	apiSurfaceModuleLibCurrentPackage = "@api_surfaces//" + android.ModuleLibApi.String() + "/current:"
+)
+
+func availableToSameApexes(a, b []string) bool {
+	if len(a) == 0 && len(b) == 0 {
+		return true
+	}
+	differ, _, _ := android.ListSetDifference(a, b)
+	return !differ
+}
+
+func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
+	config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int) {
+
+	depsWithStubs := []bazel.Label{}
+	for _, l := range dynamicLibs.Includes {
+		dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
+		if d, ok := dep.(*Module); ok && d.HasStubsVariants() {
+			depApexAvailable := d.ApexAvailable()
+			if !availableToSameApexes(apexAvailable, depApexAvailable) {
+				depsWithStubs = append(depsWithStubs, l)
+			}
+		}
+	}
+	if len(depsWithStubs) > 0 {
+		implDynamicDeps := bazel.SubtractBazelLabelList(dynamicLibs, bazel.MakeLabelList(depsWithStubs))
+		dynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
+
+		stubLibLabels := []bazel.Label{}
+		for _, l := range depsWithStubs {
+			stubLabelInApiSurfaces := bazel.Label{
+				Label: apiSurfaceModuleLibCurrentPackage + strings.TrimPrefix(l.OriginalModuleName, ":"),
+			}
+			stubLibLabels = append(stubLibLabels, stubLabelInApiSurfaces)
+		}
+		inApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
+		nonApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
+		defaultSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
+		if axis == bazel.NoConfigAxis {
+			(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+			(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+			(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
+		} else if config == bazel.OsAndroid {
+			(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+			(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+		}
+	}
+}
+
 func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
 	bp2BuildPropParseHelper(ctx, module, &StripProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
 		if stripProperties, ok := props.(*StripProperties); ok {
@@ -1257,7 +1303,7 @@
 	// result in duplicate library errors for bionic OSes. Here, we explicitly exclude those libraries
 	// from bionic OSes and the no config case as these libraries only build for bionic OSes.
 	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
-		toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
+		toRemove := bazelLabelForSharedDeps(ctx, android.SortedKeys(la.usedSystemDynamicDepAsDynamicDep))
 		la.dynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
@@ -1269,8 +1315,10 @@
 		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, toRemove)
 		stubsToRemove := make([]bazel.Label, 0, len(la.usedSystemDynamicDepAsDynamicDep))
 		for _, lib := range toRemove.Includes {
-			lib.Label += stubsSuffix
-			stubsToRemove = append(stubsToRemove, lib)
+			stubLabelInApiSurfaces := bazel.Label{
+				Label: apiSurfaceModuleLibCurrentPackage + lib.OriginalModuleName,
+			}
+			stubsToRemove = append(stubsToRemove, stubLabelInApiSurfaces)
 		}
 		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.MakeLabelList(stubsToRemove))
 	}
@@ -1453,3 +1501,49 @@
 	})
 	return sanitizerFeatures
 }
+
+func bp2buildLtoFeatures(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute {
+	lto_feature_name := "android_thin_lto"
+	ltoBoolFeatures := bazel.BoolAttribute{}
+	bp2BuildPropParseHelper(ctx, m, &LTOProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+		if ltoProps, ok := props.(*LTOProperties); ok {
+			thinProp := ltoProps.Lto.Thin != nil && *ltoProps.Lto.Thin
+			thinPropSetToFalse := ltoProps.Lto.Thin != nil && !*ltoProps.Lto.Thin
+			neverProp := ltoProps.Lto.Never != nil && *ltoProps.Lto.Never
+			if thinProp {
+				ltoBoolFeatures.SetSelectValue(axis, config, BoolPtr(true))
+				return
+			}
+			if neverProp || thinPropSetToFalse {
+				if thinProp {
+					ctx.ModuleErrorf("lto.thin and lto.never are mutually exclusive but were specified together")
+				} else {
+					ltoBoolFeatures.SetSelectValue(axis, config, BoolPtr(false))
+				}
+				return
+			}
+		}
+		ltoBoolFeatures.SetSelectValue(axis, config, nil)
+	})
+
+	props := m.GetArchVariantProperties(ctx, &LTOProperties{})
+	ltoStringFeatures, err := ltoBoolFeatures.ToStringListAttribute(func(boolPtr *bool, axis bazel.ConfigurationAxis, config string) []string {
+		if boolPtr == nil {
+			return []string{}
+		}
+		if !*boolPtr {
+			return []string{"-" + lto_feature_name}
+		}
+		features := []string{lto_feature_name}
+		if ltoProps, ok := props[axis][config].(*LTOProperties); ok {
+			if ltoProps.Whole_program_vtables != nil && *ltoProps.Whole_program_vtables {
+				features = append(features, "android_thin_lto_whole_program_vtables")
+			}
+		}
+		return features
+	})
+	if err != nil {
+		ctx.ModuleErrorf("Error processing LTO attributes: %s", err)
+	}
+	return ltoStringFeatures
+}
diff --git a/cc/builder.go b/cc/builder.go
index 0629406..fef00d4 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -519,6 +519,13 @@
 	cppflags += " ${config.NoOverrideGlobalCflags}"
 	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
 
+	if flags.toolchain.Is64Bit() {
+		cflags += " ${config.NoOverride64GlobalCflags}"
+		toolingCflags += " ${config.NoOverride64GlobalCflags}"
+		cppflags += " ${config.NoOverride64GlobalCflags}"
+		toolingCppflags += " ${config.NoOverride64GlobalCflags}"
+	}
+
 	modulePath := android.PathForModuleSrc(ctx).String()
 	if android.IsThirdPartyPath(modulePath) {
 		cflags += " ${config.NoOverrideExternalGlobalCflags}"
diff --git a/cc/cc.go b/cc/cc.go
index cb425c3..b029d71 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,6 +28,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
@@ -1065,6 +1066,31 @@
 	return false
 }
 
+func (c *Module) IsFuzzModule() bool {
+	if _, ok := c.compiler.(*fuzzBinary); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) FuzzModuleStruct() fuzz.FuzzModule {
+	return c.FuzzModule
+}
+
+func (c *Module) FuzzPackagedModule() fuzz.FuzzPackagedModule {
+	if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
+		return fuzzer.fuzzPackagedModule
+	}
+	panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) FuzzSharedLibraries() android.Paths {
+	if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
+		return fuzzer.sharedLibraries
+	}
+	panic(fmt.Errorf("FuzzSharedLibraries called on non-fuzz module: %q", c.BaseModuleName()))
+}
+
 func (c *Module) NonCcVariants() bool {
 	return false
 }
@@ -1445,6 +1471,8 @@
 }
 
 func InstallToBootstrap(name string, config android.Config) bool {
+	// NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
+	// if this list is updated.
 	if name == "libclang_rt.hwasan" {
 		return true
 	}
@@ -1859,6 +1887,10 @@
 var (
 	mixedBuildSupportedCcTest = []string{
 		"adbd_test",
+		"adb_crypto_test",
+		"adb_pairing_auth_test",
+		"adb_pairing_connection_test",
+		"adb_tls_connection_test",
 	}
 )
 
@@ -1866,26 +1898,43 @@
 // in any of the --bazel-mode(s). This filters at the module level and takes
 // precedence over the allowlists in allowlists/allowlists.go.
 func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
-	if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) {
+	_, isForTesting := ctx.Config().BazelContext.(android.MockBazelContext)
+	if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) && !isForTesting {
 		// Per-module rollout of mixed-builds for cc_test modules.
 		return false
 	}
 
 	// TODO(b/261058727): Remove this (enable mixed builds for modules with UBSan)
-	ubsanEnabled := c.sanitize != nil &&
-		((c.sanitize.Properties.Sanitize.Integer_overflow != nil && *c.sanitize.Properties.Sanitize.Integer_overflow) ||
-			c.sanitize.Properties.Sanitize.Misc_undefined != nil)
-	return c.bazelHandler != nil && !ubsanEnabled
+	// Currently we can only support ubsan when minimum runtime is used.
+	return c.bazelHandler != nil && (!isUbsanEnabled(c) || c.MinimalRuntimeNeeded())
+}
+
+func isUbsanEnabled(c *Module) bool {
+	if c.sanitize == nil {
+		return false
+	}
+	sanitizeProps := &c.sanitize.Properties.SanitizeMutated
+	return Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+}
+
+func GetApexConfigKey(ctx android.BaseModuleContext) *android.ApexConfigKey {
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	if !apexInfo.IsForPlatform() {
+		if !ctx.Config().BazelContext.IsModuleDclaAllowed(ctx.Module().Name()) {
+			return nil
+		}
+		apexKey := android.ApexConfigKey{
+			WithinApex:     true,
+			ApexSdkVersion: findApexSdkVersion(ctx, apexInfo).String(),
+		}
+		return &apexKey
+	}
+
+	return nil
 }
 
 func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
 	bazelModuleLabel := c.getBazelModuleLabel(ctx)
-
-	bazelCtx := ctx.Config().BazelContext
-	if ccInfo, err := bazelCtx.GetCcInfo(bazelModuleLabel, android.GetConfigKey(ctx)); err == nil {
-		c.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
-	}
-
 	c.bazelHandler.ProcessBazelQueryResponse(ctx, bazelModuleLabel)
 
 	c.Properties.SubName = GetSubnameProperty(ctx, c)
@@ -2081,6 +2130,12 @@
 	}
 }
 
+func (c *Module) setAndroidMkVariablesFromCquery(info cquery.CcAndroidMkInfo) {
+	c.Properties.AndroidMkSharedLibs = info.LocalSharedLibs
+	c.Properties.AndroidMkStaticLibs = info.LocalStaticLibs
+	c.Properties.AndroidMkWholeStaticLibs = info.LocalWholeStaticLibs
+}
+
 func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
 	if c.cachedToolchain == nil {
 		c.cachedToolchain = config.FindToolchainWithContext(ctx)
@@ -2212,6 +2267,13 @@
 		if err != nil {
 			ctx.PropertyErrorf("min_sdk_version", err.Error())
 		}
+
+		// Raise the minSdkVersion to the minimum supported for the architecture.
+		minApiForArch := MinApiForArch(ctx, m.Target().Arch.ArchType)
+		if apiLevel.LessThan(minApiForArch) {
+			apiLevel = minApiForArch
+		}
+
 		return []blueprint.Variation{
 			{Mutator: "sdk", Variation: "sdk"},
 			{Mutator: "version", Variation: apiLevel.String()},
@@ -2354,6 +2416,27 @@
 	return nonVariantLibs, variantLibs
 }
 
+func (c *Module) shouldUseApiSurface() bool {
+	if c.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
+		if GetImageVariantType(c) == vendorImageVariant || GetImageVariantType(c) == productImageVariant {
+			// LLNDK Variant
+			return true
+		}
+
+		if c.Properties.IsSdkVariant {
+			// NDK Variant
+			return true
+		}
+
+		if c.isImportedApiLibrary() {
+			// API Library should depend on API headers
+			return true
+		}
+	}
+
+	return false
+}
+
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if !c.Enabled() {
 		return
@@ -2373,7 +2456,7 @@
 	apiNdkLibs := []string{}
 	apiLateNdkLibs := []string{}
 
-	if ctx.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
+	if c.shouldUseApiSurface() {
 		deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config())
 		deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
 		deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
@@ -2404,7 +2487,7 @@
 		}
 
 		// Check header lib replacement from API surface first, and then check again with VSDK
-		if ctx.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
+		if c.shouldUseApiSurface() {
 			lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
 		}
 		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
@@ -2423,20 +2506,11 @@
 	if c.isNDKStubLibrary() {
 		// NDK stubs depend on their implementation because the ABI dumps are
 		// generated from the implementation library.
-		apiImportName := c.BaseModuleName() + multitree.GetApiImportSuffix()
 
-		// If original library exists as imported API, set dependency on the imported library
-		if actx.OtherModuleExists(apiImportName) {
-			actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
-				c.ImageVariation(),
-				blueprint.Variation{Mutator: "link", Variation: "shared"},
-			), stubImplementation, apiImportName)
-		} else {
-			actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
-				c.ImageVariation(),
-				blueprint.Variation{Mutator: "link", Variation: "shared"},
-			), stubImplementation, c.BaseModuleName())
-		}
+		actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
+			c.ImageVariation(),
+			blueprint.Variation{Mutator: "link", Variation: "shared"},
+		), stubImplementation, c.BaseModuleName())
 	}
 
 	for _, lib := range deps.WholeStaticLibs {
@@ -2488,12 +2562,22 @@
 		}
 
 		name, version := StubsLibNameAndVersion(lib)
+		if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) {
+			name = apiLibraryName
+		}
 		sharedLibNames = append(sharedLibNames, name)
 
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
+
+		if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
+			AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
+		}
+
+		if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
+			AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, apiLibraryName, version, false)
+		}
 	}
 
 	for _, lib := range deps.LateStaticLibs {
@@ -2801,6 +2885,23 @@
 	}
 }
 
+func findApexSdkVersion(ctx android.BaseModuleContext, apexInfo android.ApexInfo) android.ApiLevel {
+	// For the dependency from platform to apex, use the latest stubs
+	apexSdkVersion := android.FutureApiLevel
+	if !apexInfo.IsForPlatform() {
+		apexSdkVersion = apexInfo.MinSdkVersion
+	}
+
+	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
+		// In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
+		// so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
+		// (b/144430859)
+		apexSdkVersion = android.FutureApiLevel
+	}
+
+	return apexSdkVersion
+}
+
 // Convert dependencies to paths.  Returns a PathDeps containing paths
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
@@ -2816,24 +2917,61 @@
 		depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders, exporter.GeneratedHeaders...)
 	}
 
-	// For the dependency from platform to apex, use the latest stubs
-	c.apexSdkVersion = android.FutureApiLevel
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	if !apexInfo.IsForPlatform() {
-		c.apexSdkVersion = apexInfo.MinSdkVersion
-	}
+	c.apexSdkVersion = findApexSdkVersion(ctx, apexInfo)
 
-	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
-		// In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
-		// so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
-		// (b/144430859)
-		c.apexSdkVersion = android.FutureApiLevel
+	skipModuleList := map[string]bool{}
+
+	var apiImportInfo multitree.ApiImportInfo
+	hasApiImportInfo := false
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if dep.Name() == "api_imports" {
+			apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+			hasApiImportInfo = true
+		}
+	})
+
+	if hasApiImportInfo {
+		targetStubModuleList := map[string]string{}
+		targetOrigModuleList := map[string]string{}
+
+		// Search for dependency which both original module and API imported library with APEX stub exists
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			depName := ctx.OtherModuleName(dep)
+			if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
+				targetStubModuleList[apiLibrary] = depName
+			}
+		})
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			depName := ctx.OtherModuleName(dep)
+			if origLibrary, ok := targetStubModuleList[depName]; ok {
+				targetOrigModuleList[origLibrary] = depName
+			}
+		})
+
+		// Decide which library should be used between original and API imported library
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			depName := ctx.OtherModuleName(dep)
+			if apiLibrary, ok := targetOrigModuleList[depName]; ok {
+				if ShouldUseStubForApex(ctx, dep) {
+					skipModuleList[depName] = true
+				} else {
+					skipModuleList[apiLibrary] = true
+				}
+			}
+		})
 	}
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
+		if _, ok := skipModuleList[depName]; ok {
+			// skip this module because original module or API imported module matching with this should be used instead.
+			return
+		}
+
 		if depTag == android.DarwinUniversalVariantTag {
 			depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
 			return
@@ -3169,21 +3307,8 @@
 	return depPaths
 }
 
-// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
-// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
-// dependency. The stub variant is selected when the dependency crosses a boundary where each side
-// has different level of updatability. For example, if a library foo in an APEX depends on a
-// library bar which provides stable interface and exists in the platform, foo uses the stub variant
-// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
-// same APEX as foo, the non-stub variant of bar is used.
-func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool {
 	depName := ctx.OtherModuleName(dep)
-	depTag := ctx.OtherModuleDependencyTag(dep)
-	libDepTag, ok := depTag.(libraryDependencyTag)
-	if !ok || !libDepTag.shared() {
-		panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
-	}
-
 	thisModule, ok := ctx.Module().(android.ApexModule)
 	if !ok {
 		panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
@@ -3198,62 +3323,93 @@
 		bootstrap = linkable.Bootstrap()
 	}
 
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+
+	useStubs := false
+
+	if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
+		if !apexInfo.IsForPlatform() {
+			// For platform libraries, use current version of LLNDK
+			// If this is for use_vendor apex we will apply the same rules
+			// of apex sdk enforcement below to choose right version.
+			useStubs = true
+		}
+	} else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
+		// If not building for APEX or the containing APEX allows the use of
+		// platform APIs, use stubs only when it is from an APEX (and not from
+		// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
+		// bootstrap modules, always link to non-stub variant
+		isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
+
+		isApexImportedApiLibrary := false
+
+		if cc, ok := dep.(*Module); ok {
+			if apiLibrary, ok := cc.linker.(*apiLibraryDecorator); ok {
+				if apiLibrary.hasApexStubs() {
+					isApexImportedApiLibrary = true
+				}
+			}
+		}
+
+		useStubs = (isNotInPlatform || isApexImportedApiLibrary) && !bootstrap
+
+		if useStubs {
+			// Another exception: if this module is a test for an APEX, then
+			// it is linked with the non-stub variant of a module in the APEX
+			// as if this is part of the APEX.
+			testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+			for _, apexContents := range testFor.ApexContents {
+				if apexContents.DirectlyInApex(depName) {
+					useStubs = false
+					break
+				}
+			}
+		}
+		if useStubs {
+			// Yet another exception: If this module and the dependency are
+			// available to the same APEXes then skip stubs between their
+			// platform variants. This complements the test_for case above,
+			// which avoids the stubs on a direct APEX library dependency, by
+			// avoiding stubs for indirect test dependencies as well.
+			//
+			// TODO(b/183882457): This doesn't work if the two libraries have
+			// only partially overlapping apex_available. For that test_for
+			// modules would need to be split into APEX variants and resolved
+			// separately for each APEX they have access to.
+			if !isApexImportedApiLibrary && android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
+				useStubs = false
+			}
+		}
+	} else {
+		// If building for APEX, use stubs when the parent is in any APEX that
+		// the child is not in.
+		useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
+	}
+
+	return useStubs
+}
+
+// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
+// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
+// dependency. The stub variant is selected when the dependency crosses a boundary where each side
+// has different level of updatability. For example, if a library foo in an APEX depends on a
+// library bar which provides stable interface and exists in the platform, foo uses the stub variant
+// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
+// same APEX as foo, the non-stub variant of bar is used.
+func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	libDepTag, ok := depTag.(libraryDependencyTag)
+	if !ok || !libDepTag.shared() {
+		panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
+	}
+
 	sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
 	depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
 	sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 
 	if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
-		useStubs := false
-
-		if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
-			if !apexInfo.IsForPlatform() {
-				// For platform libraries, use current version of LLNDK
-				// If this is for use_vendor apex we will apply the same rules
-				// of apex sdk enforcement below to choose right version.
-				useStubs = true
-			}
-		} else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
-			// If not building for APEX or the containing APEX allows the use of
-			// platform APIs, use stubs only when it is from an APEX (and not from
-			// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
-			// bootstrap modules, always link to non-stub variant
-			useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap
-			if useStubs {
-				// Another exception: if this module is a test for an APEX, then
-				// it is linked with the non-stub variant of a module in the APEX
-				// as if this is part of the APEX.
-				testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
-				for _, apexContents := range testFor.ApexContents {
-					if apexContents.DirectlyInApex(depName) {
-						useStubs = false
-						break
-					}
-				}
-			}
-			if useStubs {
-				// Yet another exception: If this module and the dependency are
-				// available to the same APEXes then skip stubs between their
-				// platform variants. This complements the test_for case above,
-				// which avoids the stubs on a direct APEX library dependency, by
-				// avoiding stubs for indirect test dependencies as well.
-				//
-				// TODO(b/183882457): This doesn't work if the two libraries have
-				// only partially overlapping apex_available. For that test_for
-				// modules would need to be split into APEX variants and resolved
-				// separately for each APEX they have access to.
-				if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
-					useStubs = false
-				}
-			}
-		} else {
-			// If building for APEX, use stubs when the parent is in any APEX that
-			// the child is not in.
-			useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
-		}
-
 		// when to use (unspecified) stubs, use the latest one.
-		if useStubs {
+		if ShouldUseStubForApex(ctx, dep) {
 			stubs := sharedLibraryStubsInfo.SharedStubLibraries
 			toUse := stubs[len(stubs)-1]
 			sharedLibraryInfo = toUse.SharedLibraryInfo
@@ -3310,7 +3466,6 @@
 	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
 
 	if ccDepModule != nil {
-		// TODO(ivanlozano) Support snapshots for Rust-produced C library variants.
 		// Use base module name for snapshots when exporting to Makefile.
 		if snapshotPrebuilt, ok := ccDepModule.linker.(SnapshotInterface); ok {
 			baseName := ccDepModule.BaseModuleName()
@@ -3693,7 +3848,7 @@
 	// This allows introducing new architectures in the platform that
 	// need to be included in apexes that normally require an older
 	// min_sdk_version.
-	minApiForArch := minApiForArch(ctx, c.Target().Arch.ArchType)
+	minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
 	if sdkVersion.LessThan(minApiForArch) {
 		sdkVersion = minApiForArch
 	}
@@ -3715,6 +3870,7 @@
 	// When a vendor APEX needs a VNDK lib in it (use_vndk_as_stable: false), it should be a unique
 	// APEX variation. Otherwise, another vendor APEX with use_vndk_as_stable:true may use a wrong
 	// variation of the VNDK lib because APEX variations are merged/grouped.
+	// TODO(b/274401041) Find a way to merge APEX variations for vendor apexes.
 	return c.UseVndk() && c.IsVndk()
 }
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 6dfd395..b986511 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -25,8 +25,13 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/bazel/cquery"
 )
 
+func init() {
+	registerTestMutators(android.InitRegistrationContext)
+}
+
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
@@ -40,6 +45,36 @@
 	}),
 )
 
+var ccLibInApex = "cc_lib_in_apex"
+var apexVariationName = "apex28"
+var apexVersion = "28"
+
+func registerTestMutators(ctx android.RegistrationContext) {
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("apex", testApexMutator).Parallel()
+		ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
+	})
+}
+
+func mixedBuildsPrepareMutator(ctx android.BottomUpMutatorContext) {
+	if m := ctx.Module(); m.Enabled() {
+		if mixedBuildMod, ok := m.(android.MixedBuildBuildable); ok {
+			if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) {
+				mixedBuildMod.QueueBazelCall(ctx)
+			}
+		}
+	}
+}
+
+func testApexMutator(mctx android.BottomUpMutatorContext) {
+	modules := mctx.CreateVariations(apexVariationName)
+	apexInfo := android.ApexInfo{
+		ApexVariationName: apexVariationName,
+		MinSdkVersion:     android.ApiLevelForTest(apexVersion),
+	}
+	mctx.SetVariationProvider(modules[0], android.ApexInfoProvider, apexInfo)
+}
+
 // testCcWithConfig runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
@@ -3054,6 +3089,253 @@
 	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
 }
 
+func TestLibDepAndroidMkExportInMixedBuilds(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "static_dep",
+		}
+		cc_library {
+			name: "whole_static_dep",
+		}
+		cc_library {
+			name: "shared_dep",
+		}
+		cc_library {
+			name: "lib",
+			bazel_module: { label: "//:lib" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+		cc_test {
+			name: "test",
+			bazel_module: { label: "//:test" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+			gtest: false,
+		}
+		cc_binary {
+			name: "binary",
+			bazel_module: { label: "//:binary" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+		cc_library_headers {
+			name: "lib_headers",
+			bazel_module: { label: "//:lib_headers" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+		cc_prebuilt_library {
+			name: "lib_prebuilt",
+			bazel_module: { label: "//:lib_prebuilt" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+	`
+
+	testCases := []struct {
+		name          string
+		moduleName    string
+		variant       string
+		androidMkInfo cquery.CcAndroidMkInfo
+	}{
+		{
+			name:       "shared lib",
+			moduleName: "lib",
+			variant:    "android_arm64_armv8-a_shared",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "static lib",
+			moduleName: "lib",
+			variant:    "android_arm64_armv8-a_static",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_test arm64",
+			moduleName: "test",
+			variant:    "android_arm64_armv8-a",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_test arm",
+			moduleName: "test",
+			variant:    "android_arm_armv7-a-neon",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_binary",
+			moduleName: "binary",
+			variant:    "android_arm64_armv8-a",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_library_headers",
+			moduleName: "lib_headers",
+			variant:    "android_arm64_armv8-a",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "prebuilt lib static",
+			moduleName: "lib_prebuilt",
+			variant:    "android_arm64_armv8-a_static",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "prebuilt lib shared",
+			moduleName: "lib_prebuilt",
+			variant:    "android_arm64_armv8-a_shared",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+	}
+
+	outputBaseDir := "out/bazel"
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				prepareForCcTest,
+				android.FixtureModifyConfig(func(config android.Config) {
+					config.BazelContext = android.MockBazelContext{
+						OutputBaseDir: outputBaseDir,
+						LabelToCcInfo: map[string]cquery.CcInfo{
+							"//:lib": cquery.CcInfo{
+								CcAndroidMkInfo:      tc.androidMkInfo,
+								RootDynamicLibraries: []string{""},
+							},
+							"//:lib_bp2build_cc_library_static": cquery.CcInfo{
+								CcAndroidMkInfo:    tc.androidMkInfo,
+								RootStaticArchives: []string{""},
+							},
+							"//:lib_headers": cquery.CcInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+								OutputFiles:     []string{""},
+							},
+							"//:lib_prebuilt": cquery.CcInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+							"//:lib_prebuilt_bp2build_cc_library_static": cquery.CcInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+						},
+						LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+							"//:test": cquery.CcUnstrippedInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+							"//:binary": cquery.CcUnstrippedInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+						},
+					}
+				}),
+			).RunTestWithBp(t, bp)
+			ctx := result.TestContext
+
+			module := ctx.ModuleForTests(tc.moduleName, tc.variant).Module().(*Module)
+			entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+			if !reflect.DeepEqual(module.Properties.AndroidMkStaticLibs, tc.androidMkInfo.LocalStaticLibs) {
+				t.Errorf("incorrect static_libs"+
+					"\nactual:   %v"+
+					"\nexpected: %v",
+					module.Properties.AndroidMkStaticLibs,
+					tc.androidMkInfo.LocalStaticLibs,
+				)
+			}
+			staticDepsDiffer, missingStaticDeps, additionalStaticDeps := android.ListSetDifference(
+				entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+				tc.androidMkInfo.LocalStaticLibs,
+			)
+			if staticDepsDiffer {
+				t.Errorf(
+					"expected LOCAL_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+					tc.androidMkInfo.LocalStaticLibs,
+					entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+					missingStaticDeps,
+					additionalStaticDeps,
+				)
+			}
+
+			if !reflect.DeepEqual(module.Properties.AndroidMkWholeStaticLibs, tc.androidMkInfo.LocalWholeStaticLibs) {
+				t.Errorf("expected module.Properties.AndroidMkWholeStaticLibs to be %q, but was %q",
+					tc.androidMkInfo.LocalWholeStaticLibs,
+					module.Properties.AndroidMkWholeStaticLibs,
+				)
+			}
+			wholeStaticDepsDiffer, missingWholeStaticDeps, additionalWholeStaticDeps := android.ListSetDifference(
+				entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+				tc.androidMkInfo.LocalWholeStaticLibs,
+			)
+			if wholeStaticDepsDiffer {
+				t.Errorf(
+					"expected LOCAL_WHOLE_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+					tc.androidMkInfo.LocalWholeStaticLibs,
+					entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+					missingWholeStaticDeps,
+					additionalWholeStaticDeps,
+				)
+			}
+
+			if !reflect.DeepEqual(module.Properties.AndroidMkSharedLibs, tc.androidMkInfo.LocalSharedLibs) {
+				t.Errorf("incorrect shared_libs"+
+					"\nactual:   %v"+
+					"\nexpected: %v",
+					module.Properties.AndroidMkSharedLibs,
+					tc.androidMkInfo.LocalSharedLibs,
+				)
+			}
+			sharedDepsDiffer, missingSharedDeps, additionalSharedDeps := android.ListSetDifference(
+				entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+				tc.androidMkInfo.LocalSharedLibs,
+			)
+			if sharedDepsDiffer {
+				t.Errorf(
+					"expected LOCAL_SHARED_LIBRARIES to be %q but was %q; missing %q; extra %q",
+					tc.androidMkInfo.LocalSharedLibs,
+					entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+					missingSharedDeps,
+					additionalSharedDeps,
+				)
+			}
+		})
+	}
+}
+
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
@@ -3290,6 +3572,114 @@
 	}
 }
 
+func TestStubsForLibraryInMultipleApexes(t *testing.T) {
+	// TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
+	// reenabled.
+	t.Skip()
+	t.Parallel()
+	ctx := testCc(t, `
+		cc_library_shared {
+			name: "libFoo",
+			srcs: ["foo.c"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+				versions: ["current"],
+			},
+			apex_available: ["bar", "a1"],
+		}
+
+		cc_library_shared {
+			name: "libBar",
+			srcs: ["bar.c"],
+			shared_libs: ["libFoo"],
+			apex_available: ["a1"],
+		}
+
+		cc_library_shared {
+			name: "libA1",
+			srcs: ["a1.c"],
+			shared_libs: ["libFoo"],
+			apex_available: ["a1"],
+		}
+
+		cc_library_shared {
+			name: "libBarA1",
+			srcs: ["bara1.c"],
+			shared_libs: ["libFoo"],
+			apex_available: ["bar", "a1"],
+		}
+
+		cc_library_shared {
+			name: "libAnyApex",
+			srcs: ["anyApex.c"],
+			shared_libs: ["libFoo"],
+			apex_available: ["//apex_available:anyapex"],
+		}
+
+		cc_library_shared {
+			name: "libBaz",
+			srcs: ["baz.c"],
+			shared_libs: ["libFoo"],
+			apex_available: ["baz"],
+		}
+
+		cc_library_shared {
+			name: "libQux",
+			srcs: ["qux.c"],
+			shared_libs: ["libFoo"],
+			apex_available: ["qux", "bar"],
+		}`)
+
+	variants := ctx.ModuleVariantsForTests("libFoo")
+	expectedVariants := []string{
+		"android_arm64_armv8-a_shared",
+		"android_arm64_armv8-a_shared_current",
+		"android_arm_armv7-a-neon_shared",
+		"android_arm_armv7-a-neon_shared_current",
+	}
+	variantsMismatch := false
+	if len(variants) != len(expectedVariants) {
+		variantsMismatch = true
+	} else {
+		for _, v := range expectedVariants {
+			if !inList(v, variants) {
+				variantsMismatch = false
+			}
+		}
+	}
+	if variantsMismatch {
+		t.Errorf("variants of libFoo expected:\n")
+		for _, v := range expectedVariants {
+			t.Errorf("%q\n", v)
+		}
+		t.Errorf(", but got:\n")
+		for _, v := range variants {
+			t.Errorf("%q\n", v)
+		}
+	}
+
+	linkAgainstFoo := []string{"libBarA1"}
+	linkAgainstFooStubs := []string{"libBar", "libA1", "libBaz", "libQux", "libAnyApex"}
+
+	libFooPath := "libFoo/android_arm64_armv8-a_shared/libFoo.so"
+	for _, lib := range linkAgainstFoo {
+		libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+		libFlags := libLinkRule.Args["libFlags"]
+		if !strings.Contains(libFlags, libFooPath) {
+			t.Errorf("%q: %q is not found in %q", lib, libFooPath, libFlags)
+		}
+	}
+
+	libFooStubPath := "libFoo/android_arm64_armv8-a_shared_current/libFoo.so"
+	for _, lib := range linkAgainstFooStubs {
+		libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+		libFlags := libLinkRule.Args["libFlags"]
+		if !strings.Contains(libFlags, libFooStubPath) {
+			t.Errorf("%q: %q is not found in %q", lib, libFooStubPath, libFlags)
+		}
+	}
+}
+
 func TestVersioningMacro(t *testing.T) {
 	t.Parallel()
 	for _, tc := range []struct{ moduleName, expected string }{
@@ -3648,7 +4038,7 @@
 
 func assertMapKeys(t *testing.T, m map[string]string, expected []string) {
 	t.Helper()
-	assertArrayString(t, android.SortedStringKeys(m), expected)
+	assertArrayString(t, android.SortedKeys(m), expected)
 }
 
 func TestDefaults(t *testing.T) {
@@ -4483,6 +4873,39 @@
 
 }
 
+func TestAddnoOverride64GlobalCflags(t *testing.T) {
+	t.Parallel()
+	ctx := testCc(t, `
+		cc_library_shared {
+			name: "libclient",
+			srcs: ["foo.c"],
+			shared_libs: ["libfoo#1"],
+		}
+
+		cc_library_shared {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			export_shared_lib_headers: ["libbar"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+				versions: ["1", "2", "3"],
+			},
+		}
+
+		cc_library_shared {
+			name: "libbar",
+			export_include_dirs: ["include/libbar"],
+			srcs: ["foo.c"],
+		}`)
+
+	cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+
+	if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") {
+		t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags)
+	}
+}
+
 func TestCcBuildBrokenClangProperty(t *testing.T) {
 	t.Parallel()
 	tests := []struct {
@@ -4625,3 +5048,56 @@
 		})
 	}
 }
+
+func TestDclaLibraryInApex(t *testing.T) {
+	t.Parallel()
+	bp := `
+	cc_library_shared {
+		name: "cc_lib_in_apex",
+		srcs: ["foo.cc"],
+    apex_available: ["myapex"],
+		bazel_module: { label: "//foo/bar:bar" },
+	}`
+	label := "//foo/bar:bar"
+	arch64 := "arm64_armv8-a"
+	arch32 := "arm_armv7-a-neon"
+	apexCfgKey := android.ApexConfigKey{
+		WithinApex:     true,
+		ApexSdkVersion: "28",
+	}
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureRegisterWithContext(registerTestMutators),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: "outputbase",
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					android.BuildMockBazelContextResultKey(label, arch32, android.Android, apexCfgKey): cquery.CcInfo{
+						RootDynamicLibraries: []string{"foo.so"},
+					},
+					android.BuildMockBazelContextResultKey(label, arch64, android.Android, apexCfgKey): cquery.CcInfo{
+						RootDynamicLibraries: []string{"foo.so"},
+					},
+				},
+				BazelRequests: make(map[string]bool),
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	// Test if the bazel request is queued correctly
+	key := android.BuildMockBazelContextRequestKey(label, cquery.GetCcInfo, arch32, android.Android, apexCfgKey)
+	if !ctx.Config().BazelContext.(android.MockBazelContext).BazelRequests[key] {
+		t.Errorf("Bazel request was not queued: %s", key)
+	}
+
+	sharedFoo := ctx.ModuleForTests(ccLibInApex, "android_arm_armv7-a-neon_shared_"+apexVariationName).Module()
+	producer := sharedFoo.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+}
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
deleted file mode 100644
index be46fc0..0000000
--- a/cc/cflag_artifacts.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package cc
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-)
-
-func init() {
-	android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
-}
-
-var (
-	TrackedCFlags = []string{
-		"-Wall",
-		"-Werror",
-		"-Wextra",
-		"-Wthread-safety",
-		"-O3",
-	}
-
-	TrackedCFlagsDir = []string{
-		"device/google/",
-		"vendor/google/",
-	}
-)
-
-const FileBP = 50
-
-// Stores output files.
-type cflagArtifactsText struct {
-	interOutputs map[string]android.WritablePaths
-	outputs      android.WritablePaths
-}
-
-// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
-// filter.
-func allowedDir(subdir string) bool {
-	subdir += "/"
-	return android.HasAnyPrefix(subdir, TrackedCFlagsDir)
-}
-
-func (s *cflagArtifactsText) genFlagFilename(flag string) string {
-	return fmt.Sprintf("module_cflags%s.txt", flag)
-}
-
-// incrementFile is used to generate an output path object with the passed in flag
-// and part number.
-// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
-func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
-	flag string, part int) (string, android.OutputPath) {
-
-	filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
-	filepath := android.PathForOutput(ctx, "cflags", filename)
-	s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
-	return filename, filepath
-}
-
-// GenCFlagArtifactParts is used to generate the build rules which produce the
-// intermediary files for each desired C Flag artifact
-// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
-func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
-	flag string, using bool, modules []string, part int) int {
-
-	cleanedName := strings.Replace(flag, "=", "_", -1)
-	filename, filepath := s.incrementFile(ctx, cleanedName, part)
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().Textf("rm -f %s", filepath.String())
-
-	if using {
-		rule.Command().
-			Textf("echo '# Modules using %s'", flag).
-			FlagWithOutput(">> ", filepath)
-	} else {
-		rule.Command().
-			Textf("echo '# Modules not using %s'", flag).
-			FlagWithOutput(">> ", filepath)
-	}
-
-	length := len(modules)
-
-	if length == 0 {
-		rule.Build(filename, "gen "+filename)
-		part++
-	}
-
-	// Following loop splits the module list for each tracked C Flag into
-	// chunks of length FileBP (file breakpoint) and generates a partial artifact
-	// (intermediary file) build rule for each split.
-	moduleShards := android.ShardStrings(modules, FileBP)
-	for index, shard := range moduleShards {
-		rule.Command().
-			Textf("for m in %s; do echo $m",
-				strings.Join(proptools.ShellEscapeList(shard), " ")).
-			FlagWithOutput(">> ", filepath).
-			Text("; done")
-		rule.Build(filename, "gen "+filename)
-
-		if index+1 != len(moduleShards) {
-			filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
-			rule = android.NewRuleBuilder(pctx, ctx)
-			rule.Command().Textf("rm -f %s", filepath.String())
-		}
-	}
-
-	return part + len(moduleShards)
-}
-
-// GenCFlagArtifacts is used to generate build rules which combine the
-// intermediary files of a specific tracked flag into a single C Flag artifact
-// for each tracked flag.
-// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
-func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
-	// Scans through s.interOutputs and creates a build rule for each tracked C
-	// Flag that concatenates the associated intermediary file into a single
-	// artifact.
-	for _, flag := range TrackedCFlags {
-		// Generate build rule to combine related intermediary files into a
-		// C Flag artifact
-		rule := android.NewRuleBuilder(pctx, ctx)
-		filename := s.genFlagFilename(flag)
-		outputpath := android.PathForOutput(ctx, "cflags", filename)
-		rule.Command().
-			Text("cat").
-			Inputs(s.interOutputs[flag].Paths()).
-			FlagWithOutput("> ", outputpath)
-		rule.Build(filename, "gen "+filename)
-		s.outputs = append(s.outputs, outputpath)
-	}
-}
-
-func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
-	modulesWithCFlag := make(map[string][]string)
-
-	// Scan through all modules, selecting the ones that are part of the filter,
-	// and then storing into a map which tracks whether or not tracked C flag is
-	// used or not.
-	ctx.VisitAllModules(func(module android.Module) {
-		if ccModule, ok := module.(*Module); ok {
-			if allowedDir(ctx.ModuleDir(ccModule)) {
-				cflags := ccModule.flags.Local.CFlags
-				cppflags := ccModule.flags.Local.CppFlags
-				module := fmt.Sprintf("%s:%s (%s)",
-					ctx.BlueprintFile(ccModule),
-					ctx.ModuleName(ccModule),
-					ctx.ModuleSubDir(ccModule))
-				for _, flag := range TrackedCFlags {
-					if inList(flag, cflags) || inList(flag, cppflags) {
-						modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
-					} else {
-						modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
-					}
-				}
-			}
-		}
-	})
-
-	// Traversing map and setting up rules to produce intermediary files which
-	// contain parts of each expected C Flag artifact.
-	for _, flag := range TrackedCFlags {
-		sort.Strings(modulesWithCFlag[flag])
-		part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
-		sort.Strings(modulesWithCFlag["!"+flag])
-		s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
-	}
-
-	// Combine intermediary files into a single C Flag artifact.
-	s.GenCFlagArtifacts(ctx)
-}
-
-func cflagArtifactsTextFactory() android.Singleton {
-	return &cflagArtifactsText{
-		interOutputs: make(map[string]android.WritablePaths),
-	}
-}
-
-func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
-}
diff --git a/cc/check.go b/cc/check.go
index 3d290a9..58ff5b2 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -57,6 +57,10 @@
 				} else if strings.HasPrefix("../", path) {
 					ctx.PropertyErrorf(prop, "Path must not start with `../`: `%s`. Use include_dirs to -include from a different directory", flag)
 				}
+			} else if args[0] == "-mllvm" {
+				if len(args) > 2 {
+					ctx.PropertyErrorf(prop, "`-mllvm` only takes one argument: `%s`", flag)
+				}
 			} else if strings.HasPrefix(flag, "-D") && strings.Contains(flag, "=") {
 				// Do nothing in this case.
 				// For now, we allow space characters in -DNAME=def form to allow use cases
diff --git a/cc/compiler.go b/cc/compiler.go
index a751754..88985b6 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -416,11 +416,6 @@
 
 	if ctx.apexVariationName() != "" {
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
-		if ctx.Device() {
-			flags.Global.CommonFlags = append(flags.Global.CommonFlags,
-				fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
-					ctx.apexSdkVersion().FinalOrFutureInt()))
-		}
 	}
 
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index d7f9618..28f3682 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -41,6 +41,11 @@
 		"armv8-2a-dotprod": []string{
 			"-march=armv8.2-a+dotprod",
 		},
+		"armv9-a": []string{
+			"-march=armv8.2-a+dotprod",
+			"-mbranch-protection=standard",
+			"-fno-stack-protector",
+		},
 	}
 
 	arm64Ldflags = []string{
@@ -101,6 +106,7 @@
 	exportedVars.ExportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
 	exportedVars.ExportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
 	exportedVars.ExportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
+	exportedVars.ExportStringListStaticVariable("Arm64Armv9ACflags", arm64ArchVariantCflags["armv9-a"])
 
 	exportedVars.ExportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
 	exportedVars.ExportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
@@ -117,6 +123,7 @@
 		"armv8-a-branchprot": "${config.Arm64Armv8ABranchProtCflags}",
 		"armv8-2a":           "${config.Arm64Armv82ACflags}",
 		"armv8-2a-dotprod":   "${config.Arm64Armv82ADotprodCflags}",
+		"armv9-a":            "${config.Arm64Armv9ACflags}",
 	}
 
 	arm64CpuVariantCflagsVar = map[string]string{
@@ -193,6 +200,7 @@
 	case "armv8-a-branchprot":
 	case "armv8-2a":
 	case "armv8-2a-dotprod":
+	case "armv9-a":
 		// Nothing extra for armv8-a/armv8-2a
 	default:
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 981d1ea..0704550 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -97,6 +97,15 @@
 			// better solution comes around. See Bug 27340895
 			"-D__ARM_FEATURE_LPAE=1",
 		},
+		"cortex-a32": []string{
+			"-mcpu=cortex-a32",
+			"-mfpu=neon-vfpv4",
+			// Fake an ARM compiler flag as these processors support LPAE which clang
+			// don't advertise.
+			// TODO This is a hack and we need to add it for each processor that supports LPAE until some
+			// better solution comes around. See Bug 27340895
+			"-D__ARM_FEATURE_LPAE=1",
+		},
 		"cortex-a53": []string{
 			"-mcpu=cortex-a53",
 			"-mfpu=neon-fp-armv8",
@@ -204,6 +213,7 @@
 	exportedVars.ExportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
 	exportedVars.ExportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
 	exportedVars.ExportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
+	exportedVars.ExportStringListStaticVariable("ArmCortexA32Cflags", armCpuVariantCflags["cortex-a32"])
 	exportedVars.ExportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
 	exportedVars.ExportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
 	exportedVars.ExportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
@@ -224,6 +234,7 @@
 		"cortex-a8":      "${config.ArmCortexA8Cflags}",
 		"cortex-a9":      "${config.ArmGenericCflags}",
 		"cortex-a15":     "${config.ArmCortexA15Cflags}",
+		"cortex-a32":     "${config.ArmCortexA32Cflags}",
 		"cortex-a53":     "${config.ArmCortexA53Cflags}",
 		"cortex-a53.a57": "${config.ArmCortexA53Cflags}",
 		"cortex-a55":     "${config.ArmCortexA55Cflags}",
diff --git a/cc/config/global.go b/cc/config/global.go
index e78839b..4d703e4 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -193,6 +193,7 @@
 
 	noOverrideGlobalCflags = []string{
 		"-Werror=bool-operation",
+		"-Werror=format-insufficient-args",
 		"-Werror=implicit-int-float-conversion",
 		"-Werror=int-in-bool-context",
 		"-Werror=int-to-pointer-cast",
@@ -207,10 +208,7 @@
 		"-Werror=fortify-source",
 
 		"-Werror=address-of-temporary",
-		// Bug: http://b/29823425 Disable -Wnull-dereference until the
-		// new cases detected by this warning in Clang r271374 are
-		// fixed.
-		//"-Werror=null-dereference",
+		"-Werror=null-dereference",
 		"-Werror=return-type",
 
 		// http://b/72331526 Disable -Wtautological-* until the instances detected by these
@@ -236,8 +234,6 @@
 		// New warnings to be fixed after clang-r433403
 		"-Wno-error=unused-but-set-variable",  // http://b/197240255
 		"-Wno-error=unused-but-set-parameter", // http://b/197240255
-		// New warnings to be fixed after clang-r458507
-		"-Wno-error=unqualified-std-cast-call", // http://b/239662094
 		// New warnings to be fixed after clang-r468909
 		"-Wno-error=deprecated-builtins", // http://b/241601211
 		"-Wno-error=deprecated",          // in external/googletest/googletest
@@ -247,18 +243,19 @@
 		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
 	}
 
+	noOverride64GlobalCflags = []string{}
+
 	noOverrideExternalGlobalCflags = []string{
-		// http://b/148815709
+		// http://b/191699019
+		"-Wno-format-insufficient-args",
 		"-Wno-sizeof-array-div",
-		// http://b/197240255
 		"-Wno-unused-but-set-variable",
 		"-Wno-unused-but-set-parameter",
-		// http://b/215753485
+		"-Wno-unqualified-std-cast-call",
 		"-Wno-bitwise-instead-of-logical",
-		// http://b/232926688
 		"-Wno-misleading-indentation",
-		// http://b/241941550
 		"-Wno-array-parameter",
+		"-Wno-gnu-offsetof-extensions",
 	}
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -277,7 +274,6 @@
 
 		// http://b/145211477
 		"-Wno-pointer-compare",
-		// http://b/145211022
 		"-Wno-final-dtor-non-final-class",
 
 		// http://b/165945989
@@ -309,8 +305,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r475365b"
-	ClangDefaultShortVersion = "16.0.2"
+	ClangDefaultVersion      = "clang-r487747"
+	ClangDefaultShortVersion = "17"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -369,10 +365,6 @@
 			flags = append(flags, "-Wno-unused-command-line-argument")
 		}
 
-		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
-			flags = append(flags, llvmNextExtraCommonGlobalCflags...)
-		}
-
 		if ctx.Config().IsEnvTrue("ALLOW_UNKNOWN_WARNING_OPTION") {
 			flags = append(flags, "-Wno-error=unknown-warning-option")
 		}
@@ -388,8 +380,18 @@
 		return strings.Join(deviceGlobalCflags, " ")
 	})
 
+	// Export the static default NoOverrideGlobalCflags to Bazel.
+	exportedVars.ExportStringList("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+	pctx.VariableFunc("NoOverrideGlobalCflags", func(ctx android.PackageVarContext) string {
+		flags := noOverrideGlobalCflags
+		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+			flags = append(noOverrideGlobalCflags, llvmNextExtraCommonGlobalCflags...)
+		}
+		return strings.Join(flags, " ")
+	})
+
+	exportedVars.ExportStringListStaticVariable("NoOverride64GlobalCflags", noOverride64GlobalCflags)
 	exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
-	exportedVars.ExportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
 	exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
 	exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
 	exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
@@ -415,16 +417,13 @@
 	exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes)
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
 
-	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
-	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
-
 	pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase)
-	pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
+	exportedVars.ExportStringStaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
 	pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
 	pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
 
-	pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
-	pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib64/clang/${ClangShortVersion}/lib/linux")
+	exportedVars.ExportStringStaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
+	pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib/clang/${ClangShortVersion}/lib/linux")
 
 	// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
 	// being used for the rest of the build process.
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 67208b2..35c57f9 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -26,16 +26,22 @@
 		// Help catch common 32/64-bit errors.
 		"-Werror=implicit-function-declaration",
 		"-fno-emulated-tls",
+		// For -fsanitize=shadow-call-stack.
+		"-ffixed-x18",
 	}
 
 	riscv64ArchVariantCflags = map[string][]string{}
 
 	riscv64Ldflags = []string{
 		"-Wl,--hash-style=gnu",
+		// For -fsanitize=shadow-call-stack.
+		"-ffixed-x18",
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
-		"-Wl,-z,max-page-size=4096")
+		"-Wl,-z,max-page-size=4096",
+		"-Wl,-plugin-opt,-emulated-tls=0",
+	)
 
 	riscv64Cppflags = []string{}
 
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 1180da4..d55a13d 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -40,6 +40,8 @@
 		"-cert-err33-c",
 		// http://b/241125373
 		"-bugprone-unchecked-optional-access",
+		// http://b/265438407
+		"-misc-use-anonymous-namespace",
 	}
 
 	// Some clang-tidy checks are included in some tidy_checks_as_errors lists,
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 740405e..93aa82e 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -112,7 +112,7 @@
 	muslCrtBeginSharedBinary, muslCrtEndSharedBinary   = []string{"libc_musl_crtbegin_dynamic"}, []string{"libc_musl_crtend"}
 	muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
 
-	muslDefaultSharedLibraries = []string{"libc_musl"}
+	MuslDefaultSharedLibraries = []string{"libc_musl"}
 )
 
 const (
@@ -331,7 +331,7 @@
 func (toolchainMusl) CrtEndSharedBinary() []string    { return muslCrtEndSharedBinary }
 func (toolchainMusl) CrtEndSharedLibrary() []string   { return muslCrtEndSharedLibrary }
 
-func (toolchainMusl) DefaultSharedLibraries() []string { return muslDefaultSharedLibraries }
+func (toolchainMusl) DefaultSharedLibraries() []string { return MuslDefaultSharedLibraries }
 
 func (toolchainMusl) Cflags() string {
 	return "${config.LinuxMuslCflags}"
diff --git a/cc/coverage.go b/cc/coverage.go
index a7356f8..c0f6973 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -219,10 +219,14 @@
 	return properties
 }
 
-// Coverage is an interface for non-CC modules to implement to be mutated for coverage
-type Coverage interface {
+type UseCoverage interface {
 	android.Module
 	IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
+}
+
+// Coverage is an interface for non-CC modules to implement to be mutated for coverage
+type Coverage interface {
+	UseCoverage
 	SetPreventInstall()
 	HideFromMake()
 	MarkAsCoverageVariant(bool)
@@ -261,6 +265,11 @@
 
 		m[1].(Coverage).MarkAsCoverageVariant(true)
 		m[1].(Coverage).EnableCoverageIfNeeded()
+	} else if cov, ok := mctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+		// Module itself doesn't have to have "cov" variant, but it should use "cov" variants of
+		// deps.
+		mctx.CreateVariations("cov")
+		mctx.AliasVariation("cov")
 	}
 }
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 7113d87..7aa8b91 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -212,7 +212,7 @@
 	return true
 }
 
-func sharedLibraryInstallLocation(
+func SharedLibraryInstallLocation(
 	libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
 	installLocation := "$(PRODUCT_OUT)/data"
 	if isHost {
@@ -224,7 +224,7 @@
 }
 
 // Get the device-only shared library symbols install directory.
-func sharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
+func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
 	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
 }
 
@@ -237,59 +237,64 @@
 		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
 	fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
 
-	fuzzBin.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzBin.fuzzPackagedModule.FuzzProperties.Corpus)
-	builder := android.NewRuleBuilder(pctx, ctx)
-	intermediateDir := android.PathForModuleOut(ctx, "corpus")
-	for _, entry := range fuzzBin.fuzzPackagedModule.Corpus {
-		builder.Command().Text("cp").
-			Input(entry).
-			Output(intermediateDir.Join(ctx, entry.Base()))
-	}
-	builder.Build("copy_corpus", "copy corpus")
-	fuzzBin.fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
-
-	fuzzBin.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzBin.fuzzPackagedModule.FuzzProperties.Data)
-	builder = android.NewRuleBuilder(pctx, ctx)
-	intermediateDir = android.PathForModuleOut(ctx, "data")
-	for _, entry := range fuzzBin.fuzzPackagedModule.Data {
-		builder.Command().Text("cp").
-			Input(entry).
-			Output(intermediateDir.Join(ctx, entry.Rel()))
-	}
-	builder.Build("copy_data", "copy data")
-	fuzzBin.fuzzPackagedModule.DataIntermediateDir = intermediateDir
-
-	if fuzzBin.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
-		fuzzBin.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzBin.fuzzPackagedModule.FuzzProperties.Dictionary)
-		if fuzzBin.fuzzPackagedModule.Dictionary.Ext() != ".dict" {
-			ctx.PropertyErrorf("dictionary",
-				"Fuzzer dictionary %q does not have '.dict' extension",
-				fuzzBin.fuzzPackagedModule.Dictionary.String())
-		}
-	}
-
-	if fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
-		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
-		android.WriteFileRule(ctx, configPath, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
-		fuzzBin.fuzzPackagedModule.Config = configPath
-	}
+	fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx)
 
 	// Grab the list of required shared libraries.
 	fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
 
 	for _, lib := range fuzzBin.sharedLibraries {
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
-			sharedLibraryInstallLocation(
+			SharedLibraryInstallLocation(
 				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
-				sharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+				SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 }
 
+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")
+	for _, entry := range fuzzPackagedModule.Corpus {
+		builder.Command().Text("cp").
+			Input(entry).
+			Output(intermediateDir.Join(ctx, entry.Base()))
+	}
+	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")
+	for _, entry := range fuzzPackagedModule.Data {
+		builder.Command().Text("cp").
+			Input(entry).
+			Output(intermediateDir.Join(ctx, entry.Rel()))
+	}
+	builder.Build("copy_data", "copy data")
+	fuzzPackagedModule.DataIntermediateDir = intermediateDir
+
+	if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+		fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
+		if fuzzPackagedModule.Dictionary.Ext() != ".dict" {
+			ctx.PropertyErrorf("dictionary",
+				"Fuzzer dictionary %q does not have '.dict' extension",
+				fuzzPackagedModule.Dictionary.String())
+		}
+	}
+
+	if fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
+		android.WriteFileRule(ctx, configPath, fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+		fuzzPackagedModule.Config = configPath
+	}
+	return fuzzPackagedModule
+}
+
 func NewFuzzer(hod android.HostOrDeviceSupported) *Module {
 	module, binary := newBinary(hod, false)
 	baseInstallerPath := "fuzz"
@@ -344,7 +349,7 @@
 
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
-type ccFuzzPackager struct {
+type ccRustFuzzPackager struct {
 	fuzz.FuzzPackager
 	fuzzPackagingArchModules         string
 	fuzzTargetSharedDepsInstallPairs string
@@ -353,7 +358,7 @@
 
 func fuzzPackagingFactory() android.Singleton {
 
-	fuzzPackager := &ccFuzzPackager{
+	fuzzPackager := &ccRustFuzzPackager{
 		fuzzPackagingArchModules:         "SOONG_FUZZ_PACKAGING_ARCH_MODULES",
 		fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
 		allFuzzTargetsName:               "ALL_FUZZ_TARGETS",
@@ -361,7 +366,7 @@
 	return fuzzPackager
 }
 
-func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+func (s *ccRustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination, and the files that
 	// need to be packaged (in the tuple of {source file, destination folder in
 	// archive}).
@@ -376,19 +381,18 @@
 	sharedLibraryInstalled := make(map[string]bool)
 
 	ctx.VisitAllModules(func(module android.Module) {
-		ccModule, ok := module.(*Module)
-		if !ok || ccModule.Properties.PreventInstall {
+		ccModule, ok := module.(LinkableInterface)
+		if !ok || ccModule.PreventInstall() {
 			return
 		}
 
 		// Discard non-fuzz targets.
-		if ok := fuzz.IsValid(ccModule.FuzzModule); !ok {
+		if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
 			return
 		}
 
 		sharedLibsInstallDirPrefix := "lib"
-		fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
-		if !ok {
+		if !ccModule.IsFuzzModule() {
 			return
 		}
 
@@ -399,12 +403,12 @@
 
 		fpm := fuzz.FuzzPackagedModule{}
 		if ok {
-			fpm = fuzzModule.fuzzPackagedModule
+			fpm = ccModule.FuzzPackagedModule()
 		}
 
 		intermediatePath := "fuzz"
 
-		archString := ccModule.Arch().ArchType.String()
+		archString := ccModule.Target().Arch.ArchType.String()
 		archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
 		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
@@ -415,7 +419,7 @@
 		files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
 
 		// Package shared libraries
-		files = append(files, GetSharedLibsToZip(fuzzModule.sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
+		files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
 		files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
@@ -429,7 +433,7 @@
 	s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
 }
 
-func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+func (s *ccRustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
 	packages := s.Packages.Strings()
 	sort.Strings(packages)
 	sort.Strings(s.FuzzPackager.SharedLibInstallStrings)
@@ -460,7 +464,7 @@
 		// For each architecture-specific shared library dependency, we need to
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
-		installDestination := sharedLibraryInstallLocation(
+		installDestination := SharedLibraryInstallLocation(
 			library, module.Host(), fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
@@ -479,7 +483,7 @@
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
 		if !module.Host() {
-			symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
+			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
diff --git a/cc/installer.go b/cc/installer.go
index e2c0e7b..716a0df 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -100,6 +100,10 @@
 	installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
 }
 
+func (installer *baseInstaller) installExecutable(ctx ModuleContext, file android.Path) {
+	installer.path = ctx.InstallExecutable(installer.installDir(ctx), file.Base(), file)
+}
+
 func (installer *baseInstaller) everInstallable() bool {
 	// Most cc modules are installable.
 	return true
diff --git a/cc/library.go b/cc/library.go
index 8fd0019..a9ada97 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -243,7 +243,6 @@
 	Local_includes         bazel.StringListAttribute
 	Absolute_includes      bazel.StringListAttribute
 	Linkopts               bazel.StringListAttribute
-	Use_libcrt             bazel.BoolAttribute
 	Rtti                   bazel.BoolAttribute
 
 	Stl     *string
@@ -251,7 +250,6 @@
 	C_std   *string
 
 	// This is shared only.
-	Link_crt                 bazel.BoolAttribute
 	Additional_linker_inputs bazel.LabelListAttribute
 
 	// Common properties shared between both shared and static variants.
@@ -266,11 +264,15 @@
 type aidlLibraryAttributes struct {
 	Srcs        bazel.LabelListAttribute
 	Include_dir *string
+	Tags        bazel.StringListAttribute
 }
 
 type ccAidlLibraryAttributes struct {
 	Deps                        bazel.LabelListAttribute
+	Implementation_deps         bazel.LabelListAttribute
 	Implementation_dynamic_deps bazel.LabelListAttribute
+	Tags                        bazel.StringListAttribute
+	sdkAttributes
 }
 
 type stripAttributes struct {
@@ -360,7 +362,6 @@
 		Export_system_includes:   exportedIncludes.SystemIncludes,
 		Local_includes:           compilerAttrs.localIncludes,
 		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Use_libcrt:               linkerAttrs.useLibcrt,
 		Rtti:                     compilerAttrs.rtti,
 		Stl:                      compilerAttrs.stl,
 		Cpp_std:                  compilerAttrs.cppStd,
@@ -381,8 +382,6 @@
 		Local_includes:           compilerAttrs.localIncludes,
 		Absolute_includes:        compilerAttrs.absoluteIncludes,
 		Linkopts:                 linkerAttrs.linkopts,
-		Link_crt:                 linkerAttrs.linkCrt,
-		Use_libcrt:               linkerAttrs.useLibcrt,
 		Rtti:                     compilerAttrs.rtti,
 		Stl:                      compilerAttrs.stl,
 		Cpp_std:                  compilerAttrs.cppStd,
@@ -432,8 +431,10 @@
 	if compilerAttrs.stubsSymbolFile == nil && len(compilerAttrs.stubsVersions.Value) == 0 {
 		tagsForStaticVariant = android.ApexAvailableTags(m)
 	}
+	tagsForStaticVariant.Append(bazel.StringListAttribute{Value: staticAttrs.Apex_available})
 
 	tagsForSharedVariant := android.ApexAvailableTags(m)
+	tagsForSharedVariant.Append(bazel.StringListAttribute{Value: sharedAttrs.Apex_available})
 
 	ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
 		android.CommonAttributes{
@@ -459,26 +460,41 @@
 		}
 		soname := m.Name() + ".so"
 		stubSuitesAttrs := &bazelCcStubSuiteAttributes{
-			Symbol_file:     compilerAttrs.stubsSymbolFile,
-			Versions:        compilerAttrs.stubsVersions,
-			Export_includes: exportedIncludes.Includes,
-			Soname:          &soname,
-			Source_library:  *bazel.MakeLabelAttribute(":" + m.Name()),
-			Deps:            baseAttributes.deps,
+			Symbol_file:          compilerAttrs.stubsSymbolFile,
+			Versions:             compilerAttrs.stubsVersions,
+			Export_includes:      exportedIncludes.Includes,
+			Soname:               &soname,
+			Source_library_label: proptools.StringPtr(m.GetBazelLabel(ctx, m)),
+			Deps:                 baseAttributes.deps,
 		}
 		ctx.CreateBazelTargetModule(stubSuitesProps,
 			android.CommonAttributes{Name: m.Name() + "_stub_libs"},
 			stubSuitesAttrs)
+
+		// Add alias for the stub shared_library in @api_surfaces repository
+		currentModuleLibApiDir := ctx.Config().ApiSurfacesDir(android.ModuleLibApi, "current")
+		actualLabelInMainWorkspace := bazel.Label{
+			Label: fmt.Sprintf("@//%s:%s%s", ctx.ModuleDir(), m.Name(), stubsSuffix),
+		}
+		ctx.CreateBazelTargetAliasInDir(currentModuleLibApiDir, m.Name(), actualLabelInMainWorkspace)
+
+		// Add alias for headers exported by the stub library
+		headerLabelInMainWorkspace := bazel.Label{
+			// This label is generated from cc_stub_suite macro
+			Label: fmt.Sprintf("@//%s:%s_stub_libs_%s_headers", ctx.ModuleDir(), m.Name(), android.ModuleLibApi.String()),
+		}
+		headerAlias := m.Name() + "_headers"
+		ctx.CreateBazelTargetAliasInDir(currentModuleLibApiDir, headerAlias, headerLabelInMainWorkspace)
 	}
 }
 
 func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
 	apiSurfaces := make([]string, 0)
 	apiHeaders := make([]string, 0)
-	// systemapi (non-null `stubs` property)
+	// module-libapi for apexes (non-null `stubs` property)
 	if module.HasStubsVariants() {
-		apiSurfaces = append(apiSurfaces, android.SystemApi.String())
-		apiIncludes := getSystemApiIncludes(ctx, module)
+		apiSurfaces = append(apiSurfaces, android.ModuleLibApi.String())
+		apiIncludes := getModuleLibApiIncludes(ctx, module)
 		if !apiIncludes.isEmpty() {
 			createApiHeaderTarget(ctx, apiIncludes)
 			apiHeaders = append(apiHeaders, apiIncludes.name)
@@ -494,8 +510,8 @@
 		}
 	}
 	// create a target only if this module contributes to an api surface
-	// TODO: Currently this does not distinguish systemapi-only headers and vendrorapi-only headers
-	// TODO: Update so that systemapi-only headers do not get exported to vendorapi (and vice-versa)
+	// TODO: Currently this does not distinguish modulelibapi-only headers and vendrorapi-only headers
+	// TODO: Update so that modulelibapi-only headers do not get exported to vendorapi (and vice-versa)
 	if len(apiSurfaces) > 0 {
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "cc_api_contribution",
@@ -527,8 +543,8 @@
 	linker := module.linker.(*libraryDecorator)
 	if llndkApi := linker.Properties.Llndk.Symbol_file; llndkApi != nil {
 		apiFile = llndkApi
-	} else if systemApi := linker.Properties.Stubs.Symbol_file; systemApi != nil {
-		apiFile = systemApi
+	} else if moduleLibApi := linker.Properties.Stubs.Symbol_file; moduleLibApi != nil {
+		apiFile = moduleLibApi
 	} else {
 		ctx.ModuleErrorf("API surface library does not have any API file")
 	}
@@ -566,7 +582,8 @@
 	includes.attrs.Deps.Append(lla)
 }
 
-func getSystemApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+// includes provided to the module-lib API surface. This API surface is used by apexes.
+func getModuleLibApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
 	flagProps := c.library.(*libraryDecorator).flagExporter.Properties
 	linkProps := c.library.(*libraryDecorator).baseLinker.Properties
 	includes := android.FirstUniqueStrings(flagProps.Export_include_dirs)
@@ -579,7 +596,7 @@
 	}
 
 	return apiIncludes{
-		name: c.Name() + ".systemapi.headers",
+		name: c.Name() + ".module-libapi.headers",
 		attrs: bazelCcApiLibraryHeadersAttributes{
 			bazelCcLibraryHeadersAttributes: attrs,
 		},
@@ -844,7 +861,11 @@
 		ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
 		return
 	}
-	outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
+	var outputFilePath android.Path = android.PathForBazelOut(ctx, rootStaticArchives[0])
+	if len(ccInfo.TidyFiles) > 0 {
+		handler.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+		outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+	}
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 
 	objPaths := ccInfo.CcObjectFiles
@@ -880,9 +901,13 @@
 		ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
 		return
 	}
-	outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
-	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+	var outputFilePath android.Path = android.PathForBazelOut(ctx, rootDynamicLibraries[0])
+	if len(ccInfo.TidyFiles) > 0 {
+		handler.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+		outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+	}
 
+	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 	handler.module.linker.(*libraryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, ccInfo.UnstrippedOutput)
 
 	var tocFile android.OptionalPath
@@ -906,12 +931,12 @@
 
 func (handler *ccLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (handler *ccLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
 		return
@@ -936,6 +961,8 @@
 		// implementation.
 		i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
 	}
+
+	handler.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
 }
 
 func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
@@ -1239,6 +1266,10 @@
 		// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
 		// systemapi, respectively. The former is for symbols defined in platform libraries
 		// and the latter is for symbols defined in APEXes.
+		// A single library can contain either # apex or # systemapi, but not both.
+		// The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
+		// However, having this distinction helps guard accidental
+		// promotion or demotion of API and also helps the API review process b/191371676
 		var flag string
 		if ctx.Module().(android.ApexModule).NotInPlatform() {
 			flag = "--apex"
@@ -1485,7 +1516,7 @@
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
 	} else if library.shared() {
-		if !Bool(library.baseLinker.Properties.Nocrt) {
+		if library.baseLinker.Properties.crt() {
 			deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...)
 			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...)
 		}
@@ -2869,13 +2900,10 @@
 		commonAttrs.Deps.Add(baseAttributes.protoDependency)
 		attrs = &bazelCcLibraryStaticAttributes{
 			staticOrSharedAttributes: commonAttrs,
-
-			Use_libcrt: linkerAttrs.useLibcrt,
-
-			Rtti:    compilerAttrs.rtti,
-			Stl:     compilerAttrs.stl,
-			Cpp_std: compilerAttrs.cppStd,
-			C_std:   compilerAttrs.cStd,
+			Rtti:                     compilerAttrs.rtti,
+			Stl:                      compilerAttrs.stl,
+			Cpp_std:                  compilerAttrs.cppStd,
+			C_std:                    compilerAttrs.cStd,
 
 			Export_includes:          exportedIncludes.Includes,
 			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
@@ -2900,8 +2928,6 @@
 			Asflags:    asFlags,
 
 			Linkopts:        linkerAttrs.linkopts,
-			Link_crt:        linkerAttrs.linkCrt,
-			Use_libcrt:      linkerAttrs.useLibcrt,
 			Use_version_lib: linkerAttrs.useVersionLib,
 
 			Rtti:    compilerAttrs.rtti,
@@ -2946,12 +2972,6 @@
 
 	tags := android.ApexAvailableTags(module)
 
-	// This lib needs some special handling in bazel, so add this tag to the build
-	// file.
-	if module.Name() == "libprofile-clang-extras" {
-		tags.Append(bazel.MakeStringListAttribute([]string{"NO_EXPORTING"}))
-	}
-
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs)
 }
 
@@ -2959,13 +2979,11 @@
 type bazelCcLibraryStaticAttributes struct {
 	staticOrSharedAttributes
 
-	Use_libcrt      bazel.BoolAttribute
 	Use_version_lib bazel.BoolAttribute
-
-	Rtti    bazel.BoolAttribute
-	Stl     *string
-	Cpp_std *string
-	C_std   *string
+	Rtti            bazel.BoolAttribute
+	Stl             *string
+	Cpp_std         *string
+	C_std           *string
 
 	Export_includes          bazel.StringListAttribute
 	Export_absolute_includes bazel.StringListAttribute
@@ -2985,10 +3003,7 @@
 type bazelCcLibrarySharedAttributes struct {
 	staticOrSharedAttributes
 
-	Linkopts bazel.StringListAttribute
-	Link_crt bazel.BoolAttribute // Only for linking shared library (and cc_binary)
-
-	Use_libcrt      bazel.BoolAttribute
+	Linkopts        bazel.StringListAttribute
 	Use_version_lib bazel.BoolAttribute
 
 	Rtti    bazel.BoolAttribute
@@ -3024,12 +3039,12 @@
 }
 
 type bazelCcStubSuiteAttributes struct {
-	Symbol_file     *string
-	Versions        bazel.StringListAttribute
-	Export_includes bazel.StringListAttribute
-	Source_library  bazel.LabelAttribute
-	Soname          *string
-	Deps            bazel.LabelListAttribute
+	Symbol_file          *string
+	Versions             bazel.StringListAttribute
+	Export_includes      bazel.StringListAttribute
+	Source_library_label *string
+	Soname               *string
+	Deps                 bazel.LabelListAttribute
 }
 
 type bazelCcHeaderAbiCheckerAttributes struct {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 4d38068..1dee726 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -59,12 +59,12 @@
 
 func (handler *libraryHeaderBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (h *libraryHeaderBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
@@ -76,7 +76,11 @@
 		return
 	}
 
-	outputPath := android.PathForBazelOut(ctx, outputPaths[0])
+	var outputPath android.Path = android.PathForBazelOut(ctx, outputPaths[0])
+	if len(ccInfo.TidyFiles) > 0 {
+		h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+		outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+	}
 	h.module.outputFile = android.OptionalPathForPath(outputPath)
 
 	// HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library
@@ -88,6 +92,8 @@
 	// validation will fail. For now, set this to an empty list.
 	// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
 	h.library.collectedSnapshotHeaders = android.Paths{}
+
+	h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
 }
 
 // cc_library_headers contains a set of c/c++ headers which are imported by
@@ -169,7 +175,7 @@
 	// For API export, create a top-level arch-agnostic target and list the arch-specific targets as its deps
 
 	// arch-agnostic includes
-	apiIncludes := getSystemApiIncludes(ctx, module)
+	apiIncludes := getModuleLibApiIncludes(ctx, module)
 	// arch and os specific includes
 	archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
 	for _, arch := range allArches { // sorted iteration
@@ -186,7 +192,7 @@
 	}
 
 	if !apiIncludes.isEmpty() {
-		// override the name from <mod>.systemapi.headers --> <mod>.contribution
+		// override the name from <mod>.module-libapi.headers --> <mod>.contribution
 		apiIncludes.name = android.ApiContributionTargetName(module.Name())
 		createApiHeaderTarget(ctx, apiIncludes)
 	}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 1bcbdc5..e743bb6 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -405,7 +405,7 @@
 	}
 
 	// Add the collated include dir properties to the output.
-	for _, property := range android.SortedStringKeys(includeDirs) {
+	for _, property := range android.SortedKeys(includeDirs) {
 		outputProperties.AddProperty(property, includeDirs[property])
 	}
 
diff --git a/cc/library_stub.go b/cc/library_stub.go
index d21df51..18d3f21 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -23,7 +23,8 @@
 )
 
 var (
-	ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
+	ndkVariantRegex  = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
+	stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
 )
 
 func init() {
@@ -60,6 +61,12 @@
 			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
 			ctx.AddDependency(m, nil, variantName)
 		}
+	} else if m.IsStubs() {
+		targetVariant := "apex." + m.StubsVersion()
+		if inList(targetVariant, apiLibrary.properties.Variants) {
+			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
+			ctx.AddDependency(m, nil, variantName)
+		}
 	}
 }
 
@@ -153,15 +160,15 @@
 		in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
 	}
 
-	// LLNDK variant
-	if m.UseVndk() && d.hasLLNDKStubs() {
-		apiVariantModule := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
+	libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
 
+	load_cc_variant := func(apiVariantModule string) {
 		var mod android.Module
 
 		ctx.VisitDirectDeps(func(depMod android.Module) {
 			if depMod.Name() == apiVariantModule {
 				mod = depMod
+				libName = apiVariantModule
 			}
 		})
 
@@ -184,29 +191,17 @@
 				}
 			}
 		}
+	}
+
+	if m.UseVndk() && d.hasLLNDKStubs() {
+		// LLNDK variant
+		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
 	} else if m.IsSdkVariant() {
 		// NDK Variant
-		apiVariantModule := BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion())
-
-		var mod android.Module
-
-		ctx.VisitDirectDeps(func(depMod android.Module) {
-			if depMod.Name() == apiVariantModule {
-				mod = depMod
-			}
-		})
-
-		if mod != nil {
-			variantMod, ok := mod.(*CcApiVariant)
-			if ok {
-				in = variantMod.Src()
-
-				// Copy NDK properties to cc_api_library module
-				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
-					d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
-					variantMod.exportProperties.Export_include_dirs...)
-			}
-		}
+		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
+	} else if m.IsStubs() {
+		// APEX Variant
+		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
 	}
 
 	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
@@ -229,20 +224,58 @@
 	d.libraryDecorator.flagExporter.setProvider(ctx)
 
 	d.unstrippedOutputFile = in
-	libName := d.libraryDecorator.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
+	libName += flags.Toolchain.ShlibSuffix()
 
 	tocFile := android.PathForModuleOut(ctx, libName+".toc")
 	d.tocFile = android.OptionalPathForPath(tocFile)
 	TransformSharedObjectToToc(ctx, in, tocFile)
 
+	outputFile := android.PathForModuleOut(ctx, libName)
+
+	// TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
+	// We can just use original input if there is any way to avoid name conflict without copy.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cp,
+		Description: "API surface imported library",
+		Input:       in,
+		Output:      outputFile,
+		Args: map[string]string{
+			"cpFlags": "-L",
+		},
+	})
+
 	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
-		SharedLibrary: in,
+		SharedLibrary: outputFile,
 		Target:        ctx.Target(),
 
 		TableOfContents: d.tocFile,
 	})
 
-	return in
+	d.shareStubs(ctx)
+
+	return outputFile
+}
+
+// Share additional information about stub libraries with provider
+func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
+	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
+	if len(stubs) > 0 {
+		var stubsInfo []SharedStubLibrary
+		for _, stub := range stubs {
+			stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
+			flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
+			stubsInfo = append(stubsInfo, SharedStubLibrary{
+				Version:           moduleLibraryInterface(stub).stubsVersion(),
+				SharedLibraryInfo: stubInfo,
+				FlagExporterInfo:  flagInfo,
+			})
+		}
+		ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
+			SharedStubLibraries: stubsInfo,
+
+			IsLLNDK: ctx.IsLlndk(),
+		})
+	}
 }
 
 func (d *apiLibraryDecorator) availableFor(what string) bool {
@@ -250,6 +283,19 @@
 	return true
 }
 
+func (d *apiLibraryDecorator) hasApexStubs() bool {
+	for _, variant := range d.properties.Variants {
+		if strings.HasPrefix(variant, "apex") {
+			return true
+		}
+	}
+	return false
+}
+
+func (d *apiLibraryDecorator) hasStubsVariants() bool {
+	return d.hasApexStubs()
+}
+
 func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
 	m, ok := ctx.Module().(*Module)
 
@@ -257,14 +303,8 @@
 		return nil
 	}
 
-	if d.hasLLNDKStubs() && m.UseVndk() {
-		// LLNDK libraries only need a single stubs variant.
-		return []string{android.FutureApiLevel.String()}
-	}
-
 	// TODO(b/244244438) Create more version information for NDK and APEX variations
 	// NDK variants
-
 	if m.IsSdkVariant() {
 		// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
 		if d.hasNDKStubs() {
@@ -272,6 +312,17 @@
 		}
 	}
 
+	if d.hasLLNDKStubs() && m.UseVndk() {
+		// LLNDK libraries only need a single stubs variant.
+		return []string{android.FutureApiLevel.String()}
+	}
+
+	stubsVersions := d.getStubVersions()
+
+	if len(stubsVersions) != 0 {
+		return stubsVersions
+	}
+
 	if m.MinSdkVersion() == "" {
 		return nil
 	}
@@ -311,6 +362,18 @@
 	return ndkVersions
 }
 
+func (d *apiLibraryDecorator) getStubVersions() []string {
+	stubVersions := []string{}
+
+	for _, variant := range d.properties.Variants {
+		if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
+			stubVersions = append(stubVersions, match[1])
+		}
+	}
+
+	return stubVersions
+}
+
 // 'cc_api_headers' is similar with 'cc_api_library', but which replaces
 // header libraries. The module will replace any dependencies to existing
 // original header libraries.
@@ -425,7 +488,7 @@
 // Implement ImageInterface to generate image variants
 func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
 func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return String(v.properties.Variant) == "ndk"
+	return inList(String(v.properties.Variant), []string{"ndk", "apex"})
 }
 func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool       { return false }
 func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
index 868447a..528577a 100644
--- a/cc/library_stub_test.go
+++ b/cc/library_stub_test.go
@@ -41,6 +41,7 @@
 		cc_library {
 			name: "libfoo",
 			shared_libs: ["libbar"],
+			vendor_available: true,
 		}
 
 		cc_library {
@@ -49,6 +50,7 @@
 
 		cc_api_library {
 			name: "libbar",
+			vendor_available: true,
 			src: "libbar.so",
 		}
 
@@ -57,7 +59,6 @@
 			shared_libs: [
 				"libbar",
 			],
-			header_libs: [],
 		}
 	`
 
@@ -67,8 +68,14 @@
 	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
 	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
 
-	android.AssertBoolEquals(t, "original library should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbar))
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+	android.AssertBoolEquals(t, "original library should be linked with non-stub variant", true, hasDirectDependency(t, ctx, libfoo, libbar))
+	android.AssertBoolEquals(t, "Stub library from API surface should be not linked with non-stub variant", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+
+	libfooVendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
+	libbarApiImportVendor := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "original library should not be linked", false, hasDirectDependency(t, ctx, libfooVendor, libbar))
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfooVendor, libbarApiImportVendor))
 }
 
 func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
@@ -76,11 +83,13 @@
 		cc_library {
 			name: "libfoo",
 			shared_libs: ["libbar"],
+			vendor: true,
 		}
 
 		cc_api_library {
 			name: "libbar",
 			src: "libbar.so",
+			vendor_available: true,
 		}
 
 		api_imports {
@@ -88,14 +97,13 @@
 			shared_libs: [
 				"libbar",
 			],
-			header_libs: [],
 		}
 	`
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+	libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
+	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
 
 	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
 }
@@ -105,143 +113,36 @@
 		cc_library {
 			name: "libfoo",
 			shared_libs: ["libbar"],
+			vendor_available: true,
 		}
 
 		cc_library {
 			name: "libbar",
+			vendor_available: true,
 		}
 
 		cc_api_library {
 			name: "libbar",
 			src: "libbar.so",
+			vendor_available: true,
 		}
 
 		api_imports {
 			name: "api_imports",
 			shared_libs: [],
-			header_libs: [],
 		}
 	`
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+	libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
+	libbar := ctx.ModuleForTests("libbar", "android_vendor.29_arm64_armv8-a_shared").Module()
+	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
 
 	android.AssertBoolEquals(t, "original library should be linked", true, hasDirectDependency(t, ctx, libfoo, libbar))
 	android.AssertBoolEquals(t, "Stub library from API surface should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
 }
 
-func TestApiHeaderReplacesExistingModule(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			header_libs: ["libfoo_headers"],
-		}
-
-		cc_api_library {
-			name: "libfoo",
-			header_libs: ["libfoo_headers"],
-			src: "libfoo.so",
-		}
-
-		cc_library_headers {
-			name: "libfoo_headers",
-		}
-
-		cc_api_headers {
-			name: "libfoo_headers",
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libfoo",
-			],
-			header_libs: [
-				"libfoo_headers",
-			],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libfooApiImport := ctx.ModuleForTests("libfoo.apiimport", "android_arm64_armv8-a_shared").Module()
-	libfooHeader := ctx.ModuleForTests("libfoo_headers", "android_arm64_armv8-a").Module()
-	libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
-
-	android.AssertBoolEquals(t, "original header should not be used for original library", false, hasDirectDependency(t, ctx, libfoo, libfooHeader))
-	android.AssertBoolEquals(t, "Header from API surface should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
-	android.AssertBoolEquals(t, "original header should not be used for library imported from API surface", false, hasDirectDependency(t, ctx, libfooApiImport, libfooHeader))
-	android.AssertBoolEquals(t, "Header from API surface should be used for library imported from API surface", true, hasDirectDependency(t, ctx, libfooApiImport, libfooHeaderApiImport))
-}
-
-func TestApiHeadersDoNotRequireOriginalModule(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libfoo",
-		header_libs: ["libfoo_headers"],
-	}
-
-	cc_api_headers {
-		name: "libfoo_headers",
-	}
-
-	api_imports {
-		name: "api_imports",
-		shared_libs: [
-			"libfoo",
-		],
-		header_libs: [
-			"libfoo_headers",
-		],
-	}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
-
-	android.AssertBoolEquals(t, "Header from API surface should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
-}
-
-func TestApiHeadersShouldNotReplaceWithoutApiImport(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			header_libs: ["libfoo_headers"],
-		}
-
-		cc_library_headers {
-			name: "libfoo_headers",
-		}
-
-		cc_api_headers {
-			name: "libfoo_headers",
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libfoo",
-			],
-			header_libs: [],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libfooHeader := ctx.ModuleForTests("libfoo_headers", "android_arm64_armv8-a").Module()
-	libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
-
-	android.AssertBoolEquals(t, "original header should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeader))
-	android.AssertBoolEquals(t, "Header from API surface should not be used for original library", false, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
-}
-
 func TestExportDirFromStubLibrary(t *testing.T) {
 	bp := `
 		cc_library {
@@ -330,7 +231,7 @@
 	android.AssertBoolEquals(t, "Stub library variant from API surface should be linked", true, hasDirectDependency(t, ctx, libbarApiImport, libbarApiVariant))
 
 	binFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar_llndk.so")
+	android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar.llndk.apiimport.so")
 
 	binFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
 	android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", binFooCFlags, "-Ilibbar_llndk_include")
@@ -354,6 +255,17 @@
 			stl: "c++_shared",
 		}
 
+		cc_binary {
+			name: "binqux",
+			srcs: ["binfoo.cc"],
+			shared_libs: ["libbar"],
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["libbar.cc"],
+		}
+
 		cc_api_library {
 			name: "libbar",
 			// TODO(b/244244438) Remove src property once all variants are implemented.
@@ -417,10 +329,14 @@
 	android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
 
 	binFooLibFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar_ndk_29.so")
+	android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar.ndk.29.apiimport.so")
 
 	binFooCFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("cc").Args["cFlags"]
 	android.AssertStringDoesContain(t, "Binary using sdk should include headers from the NDK variant source", binFooCFlags, "-Ilibbar_ndk_29_include")
+
+	binQux := ctx.ModuleForTests("binqux", "android_arm64_armv8-a").Module()
+	android.AssertBoolEquals(t, "NDK Stub library from API surface should not be linked with nonSdk binary", false,
+		(hasDirectDependency(t, ctx, binQux, libbarApiImportv30) || hasDirectDependency(t, ctx, binQux, libbarApiImportv29)))
 }
 
 func TestApiLibraryWithMultipleVariants(t *testing.T) {
@@ -440,6 +356,11 @@
 			shared_libs: ["libbar"],
 		}
 
+		cc_library {
+			name: "libbar",
+			srcs: ["libbar.cc"],
+		}
+
 		cc_api_library {
 			name: "libbar",
 			// TODO(b/244244438) Remove src property once all variants are implemented.
@@ -450,6 +371,9 @@
 				"ndk.29",
 				"ndk.30",
 				"ndk.current",
+				"apex.29",
+				"apex.30",
+				"apex.current",
 			],
 		}
 
@@ -479,6 +403,30 @@
 
 		cc_api_variant {
 			name: "libbar",
+			variant: "apex",
+			version: "29",
+			src: "libbar_apex_29.so",
+			export_include_dirs: ["libbar_apex_29_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "apex",
+			version: "30",
+			src: "libbar_apex_30.so",
+			export_include_dirs: ["libbar_apex_30_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "apex",
+			version: "current",
+			src: "libbar_apex_current.so",
+			export_include_dirs: ["libbar_apex_current_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
 			variant: "llndk",
 			src: "libbar_llndk.so",
 			export_include_dirs: ["libbar_llndk_include"]
@@ -489,7 +437,9 @@
 			shared_libs: [
 				"libbar",
 			],
-			header_libs: [],
+			apex_shared_libs: [
+				"libbar",
+			],
 		}
 	`
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
diff --git a/cc/library_test.go b/cc/library_test.go
index dab5bb8..dbe2be8 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -308,6 +308,75 @@
 	android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
 }
 
+func TestCcLibraryWithBazelValidations(t *testing.T) {
+	t.Parallel()
+	bp := `
+cc_library {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+	tidy: true,
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcObjectFiles:        []string{"foo.o"},
+				Includes:             []string{"include"},
+				SystemIncludes:       []string{"system_include"},
+				Headers:              []string{"foo.h"},
+				RootDynamicLibraries: []string{"foo.so"},
+				UnstrippedOutput:     "foo_unstripped.so",
+			},
+			"//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+				CcObjectFiles:      []string{"foo.o"},
+				Includes:           []string{"include"},
+				SystemIncludes:     []string{"system_include"},
+				Headers:            []string{"foo.h"},
+				RootStaticArchives: []string{"foo.a"},
+				TidyFiles:          []string{"foo.c.tidy"},
+			},
+		},
+	}
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureMergeEnv(map[string]string{
+			"ALLOW_LOCAL_TIDY_TRUE": "1",
+		}),
+	).RunTestWithConfig(t, config).TestContext
+
+	staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+	outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+
+	expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm_armv7-a-neon_static/validated/foo.a"}
+	android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+	flagExporter := ctx.ModuleProvider(staticFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+	android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+	android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_library outputfiles %s", err)
+	}
+	expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	android.AssertStringEquals(t, "unstripped shared library", "outputbase/execroot/__main__/foo_unstripped.so", sharedFoo.(*Module).linker.unstrippedOutputFilePath().String())
+	flagExporter = ctx.ModuleProvider(sharedFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+	android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+	android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+}
+
 func TestLibraryVersionScript(t *testing.T) {
 	t.Parallel()
 	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
@@ -344,6 +413,60 @@
 
 }
 
+func TestCcLibrarySharedWithBazelValidations(t *testing.T) {
+	t.Parallel()
+	bp := `
+cc_library_shared {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+	tidy: true,
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcObjectFiles:        []string{"foo.o"},
+				Includes:             []string{"include"},
+				SystemIncludes:       []string{"system_include"},
+				RootDynamicLibraries: []string{"foo.so"},
+				TocFile:              "foo.so.toc",
+				TidyFiles:            []string{"foo.c.tidy"},
+			},
+		},
+	}
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureMergeEnv(map[string]string{
+			"ALLOW_LOCAL_TIDY_TRUE": "1",
+		}),
+	).RunTestWithConfig(t, config).TestContext
+
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	producer := sharedFoo.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm_armv7-a-neon_shared/validated/foo.so"}
+	android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+	tocFilePath := sharedFoo.(*Module).Toc()
+	if !tocFilePath.Valid() {
+		t.Errorf("Invalid tocFilePath: %s", tocFilePath)
+	}
+	tocFile := tocFilePath.Path()
+	expectedToc := "outputbase/execroot/__main__/foo.so.toc"
+	android.AssertStringEquals(t, "toc file", expectedToc, tocFile.String())
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+	expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+}
+
 func TestCcLibrarySharedWithBazel(t *testing.T) {
 	t.Parallel()
 	bp := `
@@ -388,6 +511,7 @@
 	expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
 	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
 	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func TestWholeStaticLibPrebuilts(t *testing.T) {
diff --git a/cc/linkable.go b/cc/linkable.go
index 0522fc6..9578807 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -3,6 +3,7 @@
 import (
 	"android/soong/android"
 	"android/soong/bazel/cquery"
+	"android/soong/fuzz"
 	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
@@ -120,6 +121,17 @@
 	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
+	// IsFuzzModule returns true if this a *_fuzz module.
+	IsFuzzModule() bool
+
+	// FuzzPackagedModule returns the fuzz.FuzzPackagedModule for this module.
+	// Expects that IsFuzzModule returns true.
+	FuzzPackagedModule() fuzz.FuzzPackagedModule
+
+	// FuzzSharedLibraries returns the shared library dependencies for this module.
+	// Expects that IsFuzzModule returns true.
+	FuzzSharedLibraries() android.Paths
+
 	Device() bool
 	Host() bool
 
@@ -256,6 +268,9 @@
 
 	// Partition returns the partition string for this module.
 	Partition() string
+
+	// FuzzModule returns the fuzz.FuzzModule associated with the module.
+	FuzzModuleStruct() fuzz.FuzzModule
 }
 
 var (
diff --git a/cc/linker.go b/cc/linker.go
index 625d89c..e49b97d 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -15,10 +15,10 @@
 package cc
 
 import (
-	"fmt"
-
 	"android/soong/android"
 	"android/soong/cc/config"
+	"fmt"
+	"path/filepath"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -237,29 +237,14 @@
 	Exclude_shared_libs []string `android:"arch_variant"`
 }
 
-func invertBoolPtr(value *bool) *bool {
-	if value == nil {
-		return nil
-	}
-	ret := !(*value)
-	return &ret
+func (blp *BaseLinkerProperties) crt() bool {
+	// Since crt is enabled for almost every module compiling against the Bionic runtime,
+	// we interpret `nil` as  enabled.
+	return blp.Nocrt == nil || !*blp.Nocrt
 }
 
-func (blp *BaseLinkerProperties) crt() *bool {
-	val := invertBoolPtr(blp.Nocrt)
-	if val != nil && *val {
-		// == True
-		//
-		// Since crt is enabled for almost every module compiling against the Bionic runtime,
-		// use `nil` when it's enabled, and rely on the Starlark macro to set it to True by default.
-		// This keeps the BUILD files clean.
-		return nil
-	}
-	return val // can be False or nil
-}
-
-func (blp *BaseLinkerProperties) libCrt() *bool {
-	return invertBoolPtr(blp.No_libcrt)
+func (blp *BaseLinkerProperties) libCrt() bool {
+	return blp.No_libcrt == nil || !*blp.No_libcrt
 }
 
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -270,8 +255,7 @@
 type baseLinker struct {
 	Properties        BaseLinkerProperties
 	dynamicProperties struct {
-		RunPaths   []string `blueprint:"mutated"`
-		BuildStubs bool     `blueprint:"mutated"`
+		BuildStubs bool `blueprint:"mutated"`
 	}
 
 	sanitize *sanitize
@@ -281,13 +265,8 @@
 	linker.Properties.Ldflags = append(linker.Properties.Ldflags, flags...)
 }
 
-// linkerInit initializes dynamic properties of the linker (such as runpath).
+// linkerInit initializes dynamic properties of the linker.
 func (linker *baseLinker) linkerInit(ctx BaseModuleContext) {
-	if ctx.toolchain().Is64Bit() {
-		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib64", "lib64")
-	} else {
-		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib", "lib")
-	}
 }
 
 func (linker *baseLinker) linkerProps() []interface{} {
@@ -398,7 +377,7 @@
 
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins has to be last on the command line
-		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
+		if linker.Properties.libCrt() && !ctx.header() {
 			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 
@@ -421,7 +400,7 @@
 			ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
 		}
 	} else if ctx.toolchain().Musl() {
-		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
+		if linker.Properties.libCrt() && !ctx.header() {
 			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 	}
@@ -529,17 +508,8 @@
 
 	flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
 
-	if ctx.Host() && !ctx.Windows() {
-		rpathPrefix := `\$$ORIGIN/`
-		if ctx.Darwin() {
-			rpathPrefix = "@loader_path/"
-		}
-
-		if !ctx.static() {
-			for _, rpath := range linker.dynamicProperties.RunPaths {
-				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
-			}
-		}
+	if ctx.Host() && !ctx.Windows() && !ctx.static() {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
 	}
 
 	if ctx.useSdk() {
@@ -610,6 +580,45 @@
 	return flags
 }
 
+// RpathFlags returns the rpath linker flags for current target to search the following directories relative
+// to the binary:
+//
+//   - "." to find libraries alongside tests
+//   - "lib[64]" to find libraries in a subdirectory of the binaries' directory
+//   - "../lib[64]" to find libraries when the binaries are in a bin directory
+//   - "../../lib[64]" to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+//     out/host/linux-x86/nativetest/<test dir>/<test>
+//   - "../../../lib[[64] to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+//     out/host/linux-x86/testcases/<test dir>/<CPU>/<test>
+func RpathFlags(ctx android.ModuleContext) []string {
+	key := struct {
+		os   android.OsType
+		arch android.ArchType
+	}{ctx.Target().Os, ctx.Target().Arch.ArchType}
+
+	return ctx.Config().OnceStringSlice(android.NewCustomOnceKey(key), func() []string {
+		rpathPrefix := `\$$ORIGIN/`
+		if key.os == android.Darwin {
+			rpathPrefix = "@loader_path/"
+		}
+
+		var libDir string
+		if key.arch.Multilib == "lib64" {
+			libDir = "lib64"
+		} else {
+			libDir = "lib"
+		}
+
+		return []string{
+			"-Wl,-rpath," + rpathPrefix,
+			"-Wl,-rpath," + rpathPrefix + libDir,
+			"-Wl,-rpath," + rpathPrefix + filepath.Join("..", libDir),
+			"-Wl,-rpath," + rpathPrefix + filepath.Join("../..", libDir),
+			"-Wl,-rpath," + rpathPrefix + filepath.Join("../../..", libDir),
+		}
+	})
+}
+
 func (linker *baseLinker) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 	panic(fmt.Errorf("baseLinker doesn't know how to link"))
diff --git a/cc/lto_test.go b/cc/lto_test.go
index fbd91be..4220f32 100644
--- a/cc/lto_test.go
+++ b/cc/lto_test.go
@@ -15,10 +15,11 @@
 package cc
 
 import (
-	"android/soong/android"
 	"strings"
 	"testing"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -177,3 +178,64 @@
 		t.Errorf("'baz' expected to have flags %q, but got %q", w, libFooCFlags)
 	}
 }
+
+func TestLtoDisabledButEnabledForArch(t *testing.T) {
+	t.Parallel()
+	bp := `
+	cc_library {
+		name: "libfoo",
+		srcs: ["foo.c"],
+		lto: {
+			never: true,
+		},
+		target: {
+			android_arm: {
+				lto: {
+					never: false,
+					thin: true,
+				},
+			},
+		},
+	}`
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	libFooWithLto := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+	libFooWithoutLto := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+
+	android.AssertStringDoesContain(t, "missing flag for LTO in variant that expects it",
+		libFooWithLto.Args["ldFlags"], "-flto=thin")
+	android.AssertStringDoesNotContain(t, "got flag for LTO in variant that doesn't expect it",
+		libFooWithoutLto.Args["ldFlags"], "-flto=thin")
+}
+
+func TestLtoDoesNotPropagateToRuntimeLibs(t *testing.T) {
+	t.Parallel()
+	bp := `
+	cc_library {
+		name: "runtime_libbar",
+		srcs: ["bar.c"],
+	}
+
+	cc_library {
+		name: "libfoo",
+		srcs: ["foo.c"],
+		runtime_libs: ["runtime_libbar"],
+		lto: {
+			thin: true,
+		},
+	}`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	libFoo := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+	libBar := result.ModuleForTests("runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld")
+
+	android.AssertStringDoesContain(t, "missing flag for LTO in LTO enabled library",
+		libFoo.Args["ldFlags"], "-flto=thin")
+	android.AssertStringDoesNotContain(t, "got flag for LTO in runtime_lib",
+		libBar.Args["ldFlags"], "-flto=thin")
+}
diff --git a/cc/makevars.go b/cc/makevars.go
index c70d4a6..6c3f551 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -93,6 +93,7 @@
 
 	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
+	ctx.Strict("GLOBAL_CLANG_CFLAGS_64_NO_OVERRIDE", "${config.NoOverride64GlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
 	ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
 
diff --git a/cc/ndk_api_coverage_parser/OWNERS b/cc/ndk_api_coverage_parser/OWNERS
deleted file mode 100644
index a90c48c..0000000
--- a/cc/ndk_api_coverage_parser/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-sophiez@google.com
diff --git a/cc/object.go b/cc/object.go
index 6cb1a30..ef44467 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -54,12 +54,12 @@
 
 func (handler *objectBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (handler *objectBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
@@ -142,6 +142,7 @@
 	Absolute_includes   bazel.StringListAttribute
 	Stl                 *string
 	Linker_script       bazel.LabelAttribute
+	Crt                 *bool
 	sdkAttributes
 }
 
@@ -208,6 +209,7 @@
 		Absolute_includes:   compilerAttrs.absoluteIncludes,
 		Stl:                 compilerAttrs.stl,
 		Linker_script:       linkerScript,
+		Crt:                 m.linker.(*objectLinker).Properties.Crt,
 		sdkAttributes:       bp2BuildParseSdkAttributes(m),
 	}
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index af83278..5b7ba43 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -352,6 +352,7 @@
 	Static_library         bazel.LabelAttribute
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
+	Alwayslink             bazel.BoolAttribute
 }
 
 // TODO(b/228623543): The below is not entirely true until the bug is fixed. For now, both targets are always generated
@@ -389,6 +390,11 @@
 
 	tags := android.ApexAvailableTags(module)
 	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
+
+	_true := true
+	alwayslinkAttrs := *attrs
+	alwayslinkAttrs.Alwayslink.SetValue(&_true)
+	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name + "_alwayslink", Tags: tags}, &alwayslinkAttrs, prebuiltAttrs.Enabled)
 }
 
 type bazelPrebuiltLibrarySharedAttributes struct {
@@ -462,6 +468,8 @@
 	}
 
 	h.module.maybeUnhideFromMake()
+
+	h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
 }
 
 func (h *prebuiltLibraryBazelHandler) processStaticBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
@@ -486,13 +494,17 @@
 		return true
 	}
 
-	out := android.PathForBazelOut(ctx, staticLibs[0])
-	h.module.outputFile = android.OptionalPathForPath(out)
+	var outputPath android.Path = android.PathForBazelOut(ctx, staticLibs[0])
+	if len(ccInfo.TidyFiles) > 0 {
+		h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+		outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+	}
 
-	depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(out).Build()
+	h.module.outputFile = android.OptionalPathForPath(outputPath)
+
+	depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputPath).Build()
 	ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
-		StaticLibrary: out,
-
+		StaticLibrary:                        outputPath,
 		TransitiveStaticLibrariesForOrdering: depSet,
 	})
 
@@ -516,21 +528,26 @@
 		return true
 	}
 
-	out := android.PathForBazelOut(ctx, sharedLibs[0])
-	h.module.outputFile = android.OptionalPathForPath(out)
+	var outputPath android.Path = android.PathForBazelOut(ctx, sharedLibs[0])
+	if len(ccInfo.TidyFiles) > 0 {
+		h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+		outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+	}
+
+	h.module.outputFile = android.OptionalPathForPath(outputPath)
 
 	// FIXME(b/214600441): We don't yet strip prebuilt shared libraries
-	h.library.unstrippedOutputFile = out
+	h.library.unstrippedOutputFile = outputPath
 
 	var toc android.Path
 	if len(ccInfo.TocFile) > 0 {
 		toc = android.PathForBazelOut(ctx, ccInfo.TocFile)
 	} else {
-		toc = out // Just reuse `out` so ninja still gets an input but won't matter
+		toc = outputPath // Just reuse `out` so ninja still gets an input but won't matter
 	}
 
 	info := SharedLibraryInfo{
-		SharedLibrary:   out,
+		SharedLibrary:   outputPath,
 		TableOfContents: android.OptionalPathForPath(toc),
 		Target:          ctx.Target(),
 	}
@@ -750,12 +767,12 @@
 
 func (h *prebuiltBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (h *prebuiltBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 95fa99e..e3ec9d5 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -169,6 +169,11 @@
 	if !hasDep(crtx, prebuiltCrtx) {
 		t.Errorf("crtx missing dependency on prebuilt_crtx")
 	}
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, prebuiltLiba)[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+	entries = android.AndroidMkEntriesForTest(t, ctx, prebuiltLibb)[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_static", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func TestPrebuiltLibraryShared(t *testing.T) {
@@ -443,6 +448,75 @@
 		expectedStaticOutputFiles, staticOutputFiles.Strings())
 }
 
+func TestPrebuiltLibraryWithBazelValidations(t *testing.T) {
+	const bp = `
+cc_prebuilt_library {
+	name: "foo",
+	shared: {
+		srcs: ["foo.so"],
+	},
+	static: {
+		srcs: ["foo.a"],
+	},
+	bazel_module: { label: "//foo/bar:bar" },
+	tidy: true,
+}`
+	outBaseDir := "outputbase"
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltTest,
+		android.FixtureMergeEnv(map[string]string{
+			"ALLOW_LOCAL_TIDY_TRUE": "1",
+		}),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outBaseDir,
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					"//foo/bar:bar": cquery.CcInfo{
+						CcSharedLibraryFiles: []string{"foo.so"},
+						TidyFiles:            []string{"foo.c.tidy"},
+					},
+					"//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+						CcStaticLibraryFiles: []string{"foo.a"},
+						TidyFiles:            []string{"foo.c.tidy"},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+
+	expectedOutputFile := "out/soong/.intermediates/foo/android_arm_armv7-a-neon_shared/validated/foo.so"
+	sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t,
+		"prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+		expectedOutputFile, sharedInfo.SharedLibrary)
+
+	outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{expectedOutputFile}
+	android.AssertPathsRelativeToTopEquals(t,
+		"prebuilt library shared target output files did not match expected.",
+		expectedOutputFiles, outputFiles)
+
+	staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+	staticInfo := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+	expectedStaticOutputFile := "out/soong/.intermediates/foo/android_arm_armv7-a-neon_static/validated/foo.a"
+	android.AssertPathRelativeToTopEquals(t,
+		"prebuilt library static target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+		expectedStaticOutputFile, staticInfo.StaticLibrary)
+
+	staticOutputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object staticOutputFiles %s", err)
+	}
+	expectedStaticOutputFiles := []string{expectedStaticOutputFile}
+	android.AssertPathsRelativeToTopEquals(t,
+		"prebuilt library static target output files did not match expected.",
+		expectedStaticOutputFiles, staticOutputFiles)
+}
+
 func TestPrebuiltLibraryWithBazelStaticDisabled(t *testing.T) {
 	const bp = `
 cc_prebuilt_library {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 8e2d161..c899cc4 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -76,8 +76,11 @@
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
 	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
-		"export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
-	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
+		"export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
+	memtagStackCommonFlags = []string{"-march=armv8-a+memtag", "-mllvm", "-dom-tree-reachability-max-bbs-to-explore=128"}
+
+	hostOnlySanitizeFlags   = []string{"-fno-sanitize-recover=all"}
+	deviceOnlySanitizeFlags = []string{"-fsanitize-trap=all", "-ftrap-function=abort"}
 )
 
 type SanitizerType int
@@ -251,7 +254,7 @@
 	// This should not be used in Android 11+ : https://source.android.com/devices/tech/debug/scudo
 	// deprecated
 	Scudo *bool `android:"arch_variant"`
-	// shadow-call-stack sanitizer, only available on arm64
+	// shadow-call-stack sanitizer, only available on arm64/riscv64.
 	Scs *bool `android:"arch_variant"`
 	// Memory-tagging, only available on arm64
 	// if diag.memtag unset or false, enables async memory tagging
@@ -379,7 +382,12 @@
 
 var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)
 
+var exportedVars = android.NewExportedVariables(pctx)
+
 func init() {
+	exportedVars.ExportStringListStaticVariable("HostOnlySanitizeFlags", hostOnlySanitizeFlags)
+	exportedVars.ExportStringList("DeviceOnlySanitizeFlags", deviceOnlySanitizeFlags)
+
 	android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
 	android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
 }
@@ -585,8 +593,8 @@
 		s.Hwaddress = nil
 	}
 
-	// SCS is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
+	// SCS is only implemented on AArch64/riscv64.
+	if (ctx.Arch().ArchType != android.Arm64 && ctx.Arch().ArchType != android.Riscv64) || !ctx.toolchain().Bionic() {
 		s.Scs = nil
 	}
 
@@ -869,9 +877,9 @@
 			// When fuzzing, we wish to crash with diagnostics on any bug.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
 		} else if ctx.Host() {
-			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover=all")
+			flags.Local.CFlags = append(flags.Local.CFlags, hostOnlySanitizeFlags...)
 		} else {
-			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+			flags.Local.CFlags = append(flags.Local.CFlags, deviceOnlySanitizeFlags...)
 		}
 
 		if enableMinimalRuntime(s) {
@@ -1708,9 +1716,9 @@
 // These are to be used by use_soong_sanitized_static_libraries.
 // See build/make/core/binary.mk for more details.
 func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
-	for _, image := range android.SortedStringKeys(s.libsMap) {
+	for _, image := range android.SortedKeys(s.libsMap) {
 		archMap := s.libsMap[ImageVariantType(image)]
-		for _, arch := range android.SortedStringKeys(archMap) {
+		for _, arch := range android.SortedKeys(archMap) {
 			libs := archMap[arch]
 			sort.Strings(libs)
 
@@ -1787,3 +1795,7 @@
 func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
 	hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
 }
+
+func BazelCcSanitizerToolchainVars(config android.Config) string {
+	return android.BazelToolchainVars(config, exportedVars)
+}
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index fe592dc..71c5a22 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -28,12 +28,22 @@
 var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
 	cc_library_shared {
 		name: "libclang_rt.asan",
+		host_supported: true,
+	}
+	cc_library_static {
+		name: "libclang_rt.asan.static",
+		host_supported: true,
+	}
+	cc_library_static {
+		name: "libclang_rt.asan_cxx.static",
+		host_supported: true,
 	}
 `))
 
 var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(`
 	cc_library_shared {
 		name: "libclang_rt.tsan",
+		host_supported: true,
 	}
 `))
 
@@ -54,6 +64,19 @@
 	}
 }
 
+// expectNoSharedLinkDep verifies that the from module links against the to module as a
+// shared library.
+func expectNoSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+	t.Helper()
+	fromLink := from.Description("link")
+	toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+
+	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); android.InList(w, g) {
+		t.Errorf("%s should not link against %s, expected %q, got %q",
+			from.Module(), to.Module(), w, g)
+	}
+}
+
 // expectStaticLinkDep verifies that the from module links against the to module as a
 // static library.
 func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
@@ -68,6 +91,20 @@
 
 }
 
+// expectNoStaticLinkDep verifies that the from module links against the to module as a
+// static library.
+func expectNoStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+	t.Helper()
+	fromLink := from.Description("link")
+	toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+
+	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); android.InList(w, g) {
+		t.Errorf("%s should not link against %s, expected %q, got %q",
+			from.Module(), to.Module(), w, g)
+	}
+
+}
+
 // expectInstallDep verifies that the install rule of the from module depends on the
 // install rule of the to module.
 func expectInstallDep(t *testing.T, from, to android.TestingModule) {
@@ -85,6 +122,13 @@
 	}
 }
 
+type expectedRuntimeLinkage int
+
+const (
+	RUNTIME_LINKAGE_NONE   = expectedRuntimeLinkage(0)
+	RUNTIME_LINKAGE_SHARED = iota
+)
+
 func TestAsan(t *testing.T) {
 	t.Parallel()
 	bp := `
@@ -162,12 +206,14 @@
 
 	`
 
-	result := android.GroupFixturePreparers(
+	preparer := android.GroupFixturePreparers(
 		prepareForCcTest,
 		prepareForAsanTest,
-	).RunTestWithBp(t, bp)
+	)
+	buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
 
-	check := func(t *testing.T, result *android.TestResult, variant string) {
+	check := func(t *testing.T, variant string, runtimeLinkage expectedRuntimeLinkage, preparer android.FixturePreparer) {
+		result := preparer.RunTestWithBp(t, bp)
 		ctx := result.TestContext
 		asanVariant := variant + "_asan"
 		sharedVariant := variant + "_shared"
@@ -198,6 +244,8 @@
 		libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant)
 		libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant)
 
+		libAsanSharedRuntime := result.ModuleForTests("libclang_rt.asan", sharedVariant)
+
 		expectSharedLinkDep(t, ctx, binWithAsan, libShared)
 		expectSharedLinkDep(t, ctx, binWithAsan, libAsan)
 		expectSharedLinkDep(t, ctx, libShared, libTransitive)
@@ -227,10 +275,28 @@
 		expectInstallDep(t, binNoAsan, libTransitive)
 		expectInstallDep(t, libShared, libTransitive)
 		expectInstallDep(t, libAsan, libTransitive)
+
+		if runtimeLinkage == RUNTIME_LINKAGE_SHARED {
+			expectSharedLinkDep(t, ctx, binWithAsan, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, binNoAsan, libAsanSharedRuntime)
+			expectSharedLinkDep(t, ctx, libAsan, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, libShared, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, libTransitive, libAsanSharedRuntime)
+		} else {
+			expectNoSharedLinkDep(t, ctx, binWithAsan, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, binNoAsan, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, libAsan, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, libShared, libAsanSharedRuntime)
+			expectNoSharedLinkDep(t, ctx, libTransitive, libAsanSharedRuntime)
+		}
 	}
 
-	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
-	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+	t.Run("host", func(t *testing.T) { check(t, buildOS, RUNTIME_LINKAGE_NONE, preparer) })
+	t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", RUNTIME_LINKAGE_SHARED, preparer) })
+	t.Run("host musl", func(t *testing.T) {
+		check(t, "linux_musl_x86_64", RUNTIME_LINKAGE_SHARED,
+			android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+	})
 }
 
 func TestTsan(t *testing.T) {
@@ -278,12 +344,14 @@
 	}
 `
 
-	result := android.GroupFixturePreparers(
+	preparer := android.GroupFixturePreparers(
 		prepareForCcTest,
 		prepareForTsanTest,
-	).RunTestWithBp(t, bp)
+	)
+	buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
 
-	check := func(t *testing.T, result *android.TestResult, variant string) {
+	check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
+		result := preparer.RunTestWithBp(t, bp)
 		ctx := result.TestContext
 		tsanVariant := variant + "_tsan"
 		sharedVariant := variant + "_shared"
@@ -311,8 +379,11 @@
 		expectSharedLinkDep(t, ctx, libTsan, libTransitive)
 	}
 
-	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
-	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+	t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
+	t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
+	t.Run("host musl", func(t *testing.T) {
+		check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+	})
 }
 
 func TestMiscUndefined(t *testing.T) {
@@ -369,11 +440,13 @@
 	}
 `
 
-	result := android.GroupFixturePreparers(
+	preparer := android.GroupFixturePreparers(
 		prepareForCcTest,
-	).RunTestWithBp(t, bp)
+	)
+	buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
 
-	check := func(t *testing.T, result *android.TestResult, variant string) {
+	check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
+		result := preparer.RunTestWithBp(t, bp)
 		ctx := result.TestContext
 		staticVariant := variant + "_static"
 
@@ -415,8 +488,11 @@
 		expectStaticLinkDep(t, ctx, binNoUbsan, libUbsan)
 	}
 
-	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
-	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+	t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
+	t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
+	t.Run("host musl", func(t *testing.T) {
+		check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+	})
 }
 
 func TestFuzz(t *testing.T) {
@@ -647,11 +723,13 @@
 		}
 	`
 
-	result := android.GroupFixturePreparers(
+	preparer := android.GroupFixturePreparers(
 		prepareForCcTest,
-	).RunTestWithBp(t, bp)
+	)
+	buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()
 
-	check := func(t *testing.T, result *android.TestResult, variant string) {
+	check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
+		result := preparer.RunTestWithBp(t, bp)
 		staticVariant := variant + "_static"
 		sharedVariant := variant + "_shared"
 
@@ -705,8 +783,11 @@
 			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
 	}
 
-	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
-	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+	t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
+	t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
+	t.Run("host musl", func(t *testing.T) {
+		check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
+	})
 }
 
 type MemtagNoteType int
diff --git a/cc/sdk.go b/cc/sdk.go
index 3e50c9f..4f361eb 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -47,33 +47,41 @@
 
 			// Mark the SDK variant.
 			modules[1].(*Module).Properties.IsSdkVariant = true
+			// SDK variant is not supposed to be installed
+			modules[1].(*Module).Properties.PreventInstall = true
 
 			if ctx.Config().UnbundledBuildApps() {
 				// For an unbundled apps build, hide the platform variant from Make.
 				modules[0].(*Module).Properties.HideFromMake = true
-				modules[0].(*Module).Properties.PreventInstall = true
 			} else {
 				// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 				// exposed to Make.
 				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
-				modules[1].(*Module).Properties.PreventInstall = true
 			}
 			ctx.AliasVariation("")
 		} else if isCcModule && ccModule.isImportedApiLibrary() {
 			apiLibrary, _ := ccModule.linker.(*apiLibraryDecorator)
 			if apiLibrary.hasNDKStubs() && ccModule.canUseSdk() {
+				variations := []string{"sdk"}
+				if apiLibrary.hasApexStubs() {
+					variations = append(variations, "")
+				}
 				// Handle cc_api_library module with NDK stubs and variants only which can use SDK
-				modules := ctx.CreateVariations("", "sdk")
-				modules[1].(*Module).Properties.IsSdkVariant = true
+				modules := ctx.CreateVariations(variations...)
+				// Mark the SDK variant.
+				modules[0].(*Module).Properties.IsSdkVariant = true
 				if ctx.Config().UnbundledBuildApps() {
-					// For an unbundled apps build, hide the platform variant from Make.
-					modules[0].(*Module).Properties.HideFromMake = true
-					modules[0].(*Module).Properties.PreventInstall = true
+					if apiLibrary.hasApexStubs() {
+						// For an unbundled apps build, hide the platform variant from Make.
+						modules[1].(*Module).Properties.HideFromMake = true
+						modules[1].(*Module).Properties.PreventInstall = true
+					}
 				} else {
 					// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 					// exposed to Make.
-					modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
-					modules[1].(*Module).Properties.PreventInstall = true
+					modules[0].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+					// SDK variant is not supposed to be installed
+					modules[0].(*Module).Properties.PreventInstall = true
 				}
 			} else {
 				ccModule.Properties.Sdk_version = nil
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 61925e3..790440c 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -101,3 +101,95 @@
 	assertDep(t, libsdkNDK, libcxxNDK)
 	assertDep(t, libsdkPlatform, libcxxPlatform)
 }
+
+func TestMakeModuleNameForSdkVariant(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			srcs: ["main_test.cpp"],
+			sdk_version: "current",
+			stl: "none",
+		}
+	`
+	platformVariant := "android_arm64_armv8-a_shared"
+	sdkVariant := "android_arm64_armv8-a_sdk_shared"
+	testCases := []struct {
+		name              string
+		unbundledApps     []string
+		variant           string
+		skipInstall       bool // soong skips install
+		hideFromMake      bool // no make entry
+		makeUninstallable bool // make skips install
+		makeModuleName    string
+	}{
+		{
+			name:          "platform variant in normal builds",
+			unbundledApps: nil,
+			variant:       platformVariant,
+			// installable in soong
+			skipInstall: false,
+			// visiable in Make as "libfoo"
+			hideFromMake:   false,
+			makeModuleName: "libfoo",
+			// installable in Make
+			makeUninstallable: false,
+		},
+		{
+			name:          "sdk variant in normal builds",
+			unbundledApps: nil,
+			variant:       sdkVariant,
+			// soong doesn't install
+			skipInstall: true,
+			// visible in Make as "libfoo.sdk"
+			hideFromMake:   false,
+			makeModuleName: "libfoo.sdk",
+			// but not installed
+			makeUninstallable: true,
+		},
+		{
+			name:          "platform variant in unbunded builds",
+			unbundledApps: []string{"bar"},
+			variant:       platformVariant,
+			// installable in soong
+			skipInstall: false,
+			// hidden from make
+			hideFromMake: true,
+		},
+		{
+			name:          "sdk variant in unbunded builds",
+			unbundledApps: []string{"bar"},
+			variant:       sdkVariant,
+			// soong doesn't install
+			skipInstall: true,
+			// visible in Make as "libfoo"
+			hideFromMake:   false,
+			makeModuleName: "libfoo",
+			// but not installed
+			makeUninstallable: true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixture := android.GroupFixturePreparers(prepareForCcTest,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.Unbundled_build_apps = tc.unbundledApps
+				}),
+			)
+			ctx := fixture.RunTestWithBp(t, bp).TestContext
+			module := ctx.ModuleForTests("libfoo", tc.variant).Module().(*Module)
+			android.AssertBoolEquals(t, "IsSkipInstall", tc.skipInstall, module.IsSkipInstall())
+			android.AssertBoolEquals(t, "HideFromMake", tc.hideFromMake, module.HiddenFromMake())
+			if !tc.hideFromMake {
+				entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+				android.AssertStringEquals(t, "LOCAL_MODULE",
+					tc.makeModuleName, entries.EntryMap["LOCAL_MODULE"][0])
+				actualUninstallable := false
+				if actual, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
+					actualUninstallable = "true" == actual[0]
+				}
+				android.AssertBoolEquals(t, "LOCAL_UNINSTALLABLE_MODULE",
+					tc.makeUninstallable, actualUninstallable)
+			}
+		})
+	}
+}
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 32878ca..bb6e257 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -522,6 +522,8 @@
 	return false
 }
 
+var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+
 func (p *snapshotLibraryDecorator) isSanitizerAvailable(t SanitizerType) bool {
 	switch t {
 	case cfi:
@@ -644,8 +646,6 @@
 	return module.Init()
 }
 
-var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
-
 // Module definitions for snapshots of executable binaries.
 //
 // Modules (vendor|recovery)_snapshot_binary are defined here. They have their prebuilt executable
diff --git a/cc/stl.go b/cc/stl.go
index 6353a4a..f1433ef 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -38,9 +38,9 @@
 func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
 	stl := m.SelectedStl()
 	switch stl {
-	case "ndk_libc++_shared":
+	case "ndk_libc++_shared", "libc++":
 		return "libc++", "shared"
-	case "ndk_libc++_static":
+	case "ndk_libc++_static", "libc++_static":
 		return "libc++", "static"
 	case "ndk_system":
 		return "system", "shared"
@@ -80,7 +80,8 @@
 			return ""
 		}
 		s = deduplicateStlInput(s)
-		if ctx.useSdk() && ctx.Device() {
+		archHasNDKStl := ctx.Arch().ArchType != android.Riscv64
+		if ctx.useSdk() && ctx.Device() && archHasNDKStl {
 			switch s {
 			case "", "system":
 				return "ndk_system"
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 76da782..f324dcc 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -73,7 +73,7 @@
 
 func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
 	// Convert stub library file names into Makefile variable.
-	ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedStringKeys(s.stubLibraryMap), " "))
+	ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedKeys(s.stubLibraryMap), " "))
 
 	// Export the list of API XML files to Make.
 	sort.Strings(s.apiListCoverageXmlPaths)
diff --git a/cc/sysprop.go b/cc/sysprop.go
index 2b1e354..0df290a 100644
--- a/cc/sysprop.go
+++ b/cc/sysprop.go
@@ -22,11 +22,13 @@
 // TODO(b/240463568): Additional properties will be added for API validation
 type bazelSyspropLibraryAttributes struct {
 	Srcs bazel.LabelListAttribute
+	Tags bazel.StringListAttribute
 }
 
 type bazelCcSyspropLibraryAttributes struct {
 	Dep             bazel.LabelAttribute
 	Min_sdk_version *string
+	Tags            bazel.StringListAttribute
 }
 
 type SyspropLibraryLabels struct {
@@ -36,6 +38,7 @@
 }
 
 func Bp2buildSysprop(ctx android.Bp2buildMutatorContext, labels SyspropLibraryLabels, srcs bazel.LabelListAttribute, minSdkVersion *string) {
+	apexAvailableTags := android.ApexAvailableTags(ctx.Module())
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "sysprop_library",
@@ -44,11 +47,14 @@
 		android.CommonAttributes{Name: labels.SyspropLibraryLabel},
 		&bazelSyspropLibraryAttributes{
 			Srcs: srcs,
-		})
+			Tags: apexAvailableTags,
+		},
+	)
 
 	attrs := &bazelCcSyspropLibraryAttributes{
 		Dep:             *bazel.MakeLabelAttribute(":" + labels.SyspropLibraryLabel),
 		Min_sdk_version: minSdkVersion,
+		Tags:            apexAvailableTags,
 	}
 
 	if labels.SharedLibraryLabel != "" {
diff --git a/cc/test.go b/cc/test.go
index 16ef0ef..27de06b 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -311,23 +311,6 @@
 	return deps
 }
 
-func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
-	// 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
-	// find out/host/linux-x86/lib[64]/library.so
-	// 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
-	// also find out/host/linux-x86/lib[64]/library.so
-	runpaths := []string{"../../lib", "../../../lib"}
-	for _, runpath := range runpaths {
-		if ctx.toolchain().Is64Bit() {
-			runpath += "64"
-		}
-		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
-	}
-
-	// add "" to rpath so that test binaries can find libraries in their own test directory
-	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
-}
-
 func (test *testDecorator) linkerProps() []interface{} {
 	return []interface{}{&test.LinkerProperties}
 }
@@ -356,11 +339,6 @@
 	return props
 }
 
-func (test *testBinary) linkerInit(ctx BaseModuleContext) {
-	test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
-	test.binaryDecorator.linkerInit(ctx)
-}
-
 func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = test.testDecorator.linkerDeps(ctx, deps)
 	deps = test.binaryDecorator.linkerDeps(ctx, deps)
@@ -535,11 +513,6 @@
 	return append(props, test.libraryDecorator.linkerProps()...)
 }
 
-func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
-	test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
-	test.libraryDecorator.linkerInit(ctx)
-}
-
 func (test *testLibrary) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = test.testDecorator.linkerDeps(ctx, deps)
 	deps = test.libraryDecorator.linkerDeps(ctx, deps)
@@ -610,15 +583,6 @@
 	return true
 }
 
-func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
-	runpath := "../../lib"
-	if ctx.toolchain().Is64Bit() {
-		runpath += "64"
-	}
-	benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
-	benchmark.binaryDecorator.linkerInit(ctx)
-}
-
 func (benchmark *benchmarkDecorator) linkerProps() []interface{} {
 	props := benchmark.binaryDecorator.linkerProps()
 	props = append(props, &benchmark.Properties)
@@ -685,9 +649,15 @@
 		return
 	}
 
-	outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
+	var outputFilePath android.Path = android.PathForBazelOut(ctx, info.OutputFile)
+	if len(info.TidyFiles) > 0 {
+		handler.module.tidyFiles = android.PathsForBazelOut(ctx, info.TidyFiles)
+		outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+	}
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 	handler.module.linker.(*testBinary).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+	handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
 }
 
 // binaryAttributes contains Bazel attributes corresponding to a cc test
diff --git a/cc/testing.go b/cc/testing.go
index 992069b..d38a57c 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -70,6 +70,7 @@
 	return `
 		cc_defaults {
 			name: "toolchain_libs_defaults",
+			host_supported: true,
 			vendor_available: true,
 			product_available: true,
 			recovery_available: true,
@@ -135,6 +136,12 @@
 		}
 
 		cc_prebuilt_library_static {
+			name: "libclang_rt.ubsan_standalone.static",
+			defaults: ["toolchain_libs_defaults"],
+			srcs: [""],
+		}
+
+		cc_prebuilt_library_static {
 			name: "libclang_rt.ubsan_minimal",
 			defaults: ["toolchain_libs_defaults"],
 			host_supported: true,
@@ -151,6 +158,12 @@
 				linux_glibc_x86: {
 					srcs: ["libclang_rt.ubsan_minimal.x86.a"],
 				},
+				linux_musl_x86_64: {
+					srcs: ["libclang_rt.ubsan_minimal.x86_64.a"],
+				},
+				linux_musl_x86: {
+					srcs: ["libclang_rt.ubsan_minimal.x86.a"],
+				},
 			},
 		}
 
@@ -619,6 +632,45 @@
 	}),
 )
 
+// PrepareForTestWithHostMusl sets the host configuration to musl libc instead of glibc.  It also disables the test
+// on mac, which doesn't support musl libc, and adds musl modules.
+var PrepareForTestWithHostMusl = android.GroupFixturePreparers(
+	android.FixtureModifyConfig(android.ModifyTestConfigForMusl),
+	android.PrepareForSkipTestOnMac,
+	android.FixtureAddTextFile("external/musl/Android.bp", `
+		cc_defaults {
+			name: "libc_musl_crt_defaults",
+			host_supported: true,
+			device_supported: false,
+		}
+
+		cc_object {
+			name: "libc_musl_crtbegin_so",
+			defaults: ["libc_musl_crt_defaults"],
+		}
+
+		cc_object {
+			name: "libc_musl_crtend_so",
+			defaults: ["libc_musl_crt_defaults"],
+		}
+
+		cc_object {
+			name: "libc_musl_crtbegin_dynamic",
+			defaults: ["libc_musl_crt_defaults"],
+		}
+
+		cc_object {
+			name: "libc_musl_crtbegin_static",
+			defaults: ["libc_musl_crt_defaults"],
+		}
+
+		cc_object {
+			name: "libc_musl_crtend",
+			defaults: ["libc_musl_crt_defaults"],
+		}
+	`),
+)
+
 // TestConfig is the legacy way of creating a test Config for testing cc modules.
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
diff --git a/cc/util.go b/cc/util.go
index 4e10037..aa0f6b5 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -118,7 +118,7 @@
 // ...
 func installMapListFileRule(ctx android.SingletonContext, m map[string]string, path string) android.OutputPath {
 	var txtBuilder strings.Builder
-	for idx, k := range android.SortedStringKeys(m) {
+	for idx, k := range android.SortedKeys(m) {
 		if idx > 0 {
 			txtBuilder.WriteString("\n")
 		}
diff --git a/cc/vndk.go b/cc/vndk.go
index 6ab4734..3b7c87d 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -876,7 +876,7 @@
 	})
 
 	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
-		strings.Join(android.SortedStringKeys(movedToApexLlndkLibraries), " "))
+		strings.Join(android.SortedKeys(movedToApexLlndkLibraries), " "))
 
 	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
 	ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index 82db634..2e71fe1 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -41,6 +41,7 @@
 	abis             map[android_bundle_proto.Abi_AbiAlias]int
 	allowPrereleased bool
 	stem             string
+	skipSdkCheck     bool
 }
 
 // An APK set is a zip archive. An entry 'toc.pb' describes its contents.
@@ -322,6 +323,12 @@
 
 func (m sdkVersionTargetingMatcher) matches(config TargetConfig) bool {
 	const preReleaseVersion = 10000
+	// TODO (b274518686) This check should only be used while SHA based targeting is active
+	// Once we have switched to an SDK version, this can be changed to throw an error if
+	// it was accidentally set
+	if config.skipSdkCheck == true {
+		return true
+	}
 	if m.SdkVersionTargeting == nil {
 		return true
 	}
@@ -572,7 +579,7 @@
 func processArgs() {
 	flag.Usage = func() {
 		fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> [-zip <output-zip-file>] `+
-			`-sdk-version value -abis value `+
+			`-sdk-version value -abis value [-skip-sdk-check]`+
 			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
 			`[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
 		flag.PrintDefaults()
@@ -585,6 +592,7 @@
 		"'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)")
 	flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false,
 		"allow prereleased")
+	flag.BoolVar(&targetConfig.skipSdkCheck, "skip-sdk-check", false, "Skip the SDK version check")
 	flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
 	flag.Parse()
 	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 712c7fc..e3d1179 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -359,7 +359,8 @@
 		}
 		for _, file := range inputZip.Entries() {
 			pyPkg := getPackage(file.Name)
-			if filepath.Base(file.Name) == "__init__.py" {
+			baseName := filepath.Base(file.Name)
+			if baseName == "__init__.py" || baseName == "__init__.pyc" {
 				if _, found := initedPackages[pyPkg]; found {
 					panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
 				}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index d8d5e5d..0212075 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -321,7 +321,7 @@
 		jobs = runtime.NumCPU() / 4
 
 		ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
-		if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
+		if ramJobs := ramGb / 40; ramGb > 0 && jobs > ramJobs {
 			jobs = ramJobs
 		}
 
@@ -515,7 +515,8 @@
 		"TARGET_BUILD_VARIANT="+*buildVariant,
 		"TARGET_BUILD_TYPE=release",
 		"TARGET_BUILD_APPS=",
-		"TARGET_BUILD_UNBUNDLED=")
+		"TARGET_BUILD_UNBUNDLED=",
+		"USE_RBE=false") // Disabling RBE saves ~10 secs per product
 
 	if *alternateResultDir {
 		cmd.Env = append(cmd.Env,
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 803ab0d..3a4d71a 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -78,9 +78,11 @@
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
 	flag.StringVar(&cmdlineArgs.BazelForceEnabledModules, "bazel-force-enabled-modules", "", "additional modules to build with Bazel. Comma-delimited")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
+	flag.BoolVar(&cmdlineArgs.MultitreeBuild, "multitree-build", false, "this is a multitree build")
 	flag.BoolVar(&cmdlineArgs.BazelMode, "bazel-mode", false, "use bazel for analysis of certain modules")
 	flag.BoolVar(&cmdlineArgs.BazelModeStaging, "bazel-mode-staging", false, "use bazel for analysis of certain near-ready modules")
 	flag.BoolVar(&cmdlineArgs.BazelModeDev, "bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
+	flag.BoolVar(&cmdlineArgs.UseBazelProxy, "use-bazel-proxy", false, "communicate with bazel using unix socket proxy instead of spawning subprocesses")
 
 	// Flags that probably shouldn't be flags of soong_build, but we haven't found
 	// the time to remove them yet
@@ -135,7 +137,7 @@
 	ctx.EventHandler.Begin("queryview")
 	defer ctx.EventHandler.End("queryview")
 	codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.QueryView, topDir)
-	err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir))
+	err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir), false)
 	maybeQuit(err, "")
 	touch(shared.JoinPath(topDir, queryviewMarker))
 }
@@ -173,12 +175,34 @@
 	// Run codegen to generate BUILD files
 	codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.ApiBp2build, topDir)
 	absoluteApiBp2buildDir := shared.JoinPath(topDir, cmdlineArgs.BazelApiBp2buildDir)
-	err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir)
+	// Always generate bp2build_all_srcs filegroups in api_bp2build.
+	// This is necessary to force each Android.bp file to create an equivalent BUILD file
+	// and prevent package boundray issues.
+	// e.g.
+	// Source
+	// f/b/Android.bp
+	// java_library{
+	//   name: "foo",
+	//   api: "api/current.txt",
+	// }
+	//
+	// f/b/api/Android.bp <- will cause package boundary issues
+	//
+	// Gen
+	// f/b/BUILD
+	// java_contribution{
+	//   name: "foo.contribution",
+	//   api: "//f/b/api:current.txt",
+	// }
+	//
+	// If we don't generate f/b/api/BUILD, foo.contribution will be unbuildable.
+	err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir, true)
 	maybeQuit(err, "")
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 
 	// Create soong_injection repository
-	soongInjectionFiles := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
+	soongInjectionFiles, err := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
+	maybeQuit(err, "")
 	absoluteSoongInjectionDir := shared.JoinPath(topDir, ctx.Config().SoongOutDir(), bazel.SoongInjectionDirName)
 	for _, file := range soongInjectionFiles {
 		// The API targets in api_bp2build workspace do not have any dependency on api_bp2build.
@@ -191,7 +215,7 @@
 
 	workspace := shared.JoinPath(ctx.Config().SoongOutDir(), "api_bp2build")
 	// Create the symlink forest
-	symlinkDeps := bp2build.PlantSymlinkForest(
+	symlinkDeps, _, _ := bp2build.PlantSymlinkForest(
 		ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE"),
 		topDir,
 		workspace,
@@ -447,6 +471,7 @@
 		"bazel-genfiles",
 		"bazel-out",
 		"bazel-testlogs",
+		"bazel-workspace",
 		"bazel-" + filepath.Base(topDir),
 	}
 }
@@ -461,8 +486,10 @@
 // symlink tree creation binary. Then the latter would not need to depend on
 // the very heavy-weight machinery of soong_build .
 func runSymlinkForestCreation(ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
+	var ninjaDeps []string
+	var mkdirCount, symlinkCount uint64
+
 	ctx.EventHandler.Do("symlink_forest", func() {
-		var ninjaDeps []string
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 		verbose := ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE")
 
@@ -471,15 +498,16 @@
 		// or file created/deleted under it would trigger an update of the symlink forest.
 		generatedRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "bp2build")
 		workspaceRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "workspace")
+		var symlinkForestDeps []string
 		ctx.EventHandler.Do("plant", func() {
-			symlinkForestDeps := bp2build.PlantSymlinkForest(
+			symlinkForestDeps, mkdirCount, symlinkCount = bp2build.PlantSymlinkForest(
 				verbose, topDir, workspaceRoot, generatedRoot, excludedFromSymlinkForest(ctx, verbose))
-			ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 		})
-
-		writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
-		touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
+		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 	})
+
+	writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
+	touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
 	codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
 	if codegenMetrics == nil {
 		m := bp2build.CreateCodegenMetrics()
@@ -488,6 +516,8 @@
 		//TODO (usta) we cannot determine if we loaded a stale file, i.e. from an unrelated prior
 		//invocation of codegen. We should simply use a separate .pb file
 	}
+	codegenMetrics.SetSymlinkCount(symlinkCount)
+	codegenMetrics.SetMkDirCount(mkdirCount)
 	writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
 	return cmdlineArgs.SymlinkForestMarker
 }
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 45b451c..ce32184 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -25,11 +25,11 @@
 )
 
 // A helper function to generate a Read-only Bazel workspace in outDir
-func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string) error {
+func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string, generateFilegroups bool) error {
 	os.RemoveAll(outDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
-	res, err := bp2build.GenerateBazelTargets(ctx, true)
+	res, err := bp2build.GenerateBazelTargets(ctx, generateFilegroups)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8c99988..301246a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -169,7 +169,7 @@
 
 	// Create a new Status instance, which manages action counts and event output channels.
 	stat := &status.Status{}
-	defer stat.Finish()
+
 	// Hook up the terminal output and tracer to Status.
 	stat.AddOutput(output)
 	stat.AddOutput(trace.StatusTracer())
@@ -180,14 +180,15 @@
 		log.Cleanup()
 		stat.Finish()
 	})
-
+	criticalPath := status.NewCriticalPath()
 	buildCtx := build.Context{ContextImpl: &build.ContextImpl{
-		Context: ctx,
-		Logger:  log,
-		Metrics: met,
-		Tracer:  trace,
-		Writer:  output,
-		Status:  stat,
+		Context:      ctx,
+		Logger:       log,
+		Metrics:      met,
+		Tracer:       trace,
+		Writer:       output,
+		Status:       stat,
+		CriticalPath: criticalPath,
 	}}
 
 	config := c.config(buildCtx, args...)
@@ -199,11 +200,18 @@
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
+	bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
+	soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
+
+	//the profile file generated by Bazel"
+	bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
 	metricsFiles := []string{
 		buildErrorFile,           // build error strings
 		rbeMetricsFile,           // high level metrics related to remote build execution.
 		bp2buildMetricsFile,      // high level metrics related to bp2build.
 		soongMetricsFile,         // high level metrics related to this build system.
+		bazelMetricsFile,         // high level metrics related to bazel execution
+		soongBuildMetricsFile,    // high level metrics related to soong build(except bp2build)
 		config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
 	}
 
@@ -213,13 +221,16 @@
 
 	trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
 
+	defer func() {
+		stat.Finish()
+		criticalPath.WriteToMetrics(met)
+		met.Dump(soongMetricsFile)
+		if !config.SkipMetricsUpload() {
+			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
+		}
+	}()
 	c.run(buildCtx, config, args)
 
-	defer met.Dump(soongMetricsFile)
-	if !config.SkipMetricsUpload() {
-		defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
-	}
-
 }
 
 func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
@@ -247,7 +258,7 @@
 	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
 	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
 	stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
-	stat.AddOutput(status.NewCriticalPath(log))
+	stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
 	stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
 
 	buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
diff --git a/compliance/OWNERS b/compliance/OWNERS
new file mode 100644
index 0000000..f52e201
--- /dev/null
+++ b/compliance/OWNERS
@@ -0,0 +1,8 @@
+# OSEP Build
+bbadour@google.com
+kanouche@google.com
+napier@google.com
+
+# Open Source Compliance Tools
+rtp@google.com
+austinyuan@google.com
diff --git a/cuj/cuj.go b/cuj/cuj.go
index 869e0f7..de6f10d 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -94,7 +94,7 @@
 	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
 	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
 	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, "build_error")))
-	stat.AddOutput(status.NewCriticalPath(log))
+	stat.AddOutput(status.NewCriticalPathLogger(log, nil))
 
 	defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
 
diff --git a/dexpreopt/OWNERS b/dexpreopt/OWNERS
deleted file mode 100644
index 5a2a198..0000000
--- a/dexpreopt/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file * = ngeoffray@google.com,calin@google.com,skvadrik@google.com
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e3404a5..a590c72 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -101,6 +101,10 @@
 }
 
 func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
+	if ctx.Config().UnbundledBuild() {
+		return true
+	}
+
 	if contains(global.DisablePreoptModules, module.Name) {
 		return true
 	}
diff --git a/docs/OWNERS b/docs/OWNERS
deleted file mode 100644
index 2d71271..0000000
--- a/docs/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-per-file map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com
-per-file native_code_coverage.md = pirama@google.com, srhines@google.com, allenhair@google.com
-per-file tidy.md = chh@google.com, srhines@google.com
diff --git a/docs/map_files.md b/docs/map_files.md
index 1388059..35e8cbb 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -148,9 +148,24 @@
 
 ### systemapi
 
-This is a synonym of the `apex` tag. It should be used to clarify that the API
-is an API exposed by the system for an APEX, whereas `apex` should be used for
-APIs exposed by an APEX to the platform or another APEX.
+Indicates that the symbol is exposed by the platform for an apex. Whereas `apex`
+should be used for APIs exposed by an APEX to the platform or another APEX.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
+
+Since a single library can be installed ether in platform or an apex, but not
+both, a single map.txt file should not contain _both_ # apex and # systemapi symbols.
+
+The granularity between # apex and # systemapi exists to help the API review
+process (b/191371676). These two symbols have very similar lifetime "in
+practice". A #systemapi symbol can be dropped from the next release if we are
+confident that no one is using it. Similarily, #apex can be dropped if we are
+sure that the old platform which used the symbol has reached EOL and thus is no
+longer accepting new APEX updates. Unlike the APIs for apps where we have zero
+control over how APIs are used, we are in a much more controllable environment
+when talking about #systemapi and #apex symbols. So, we have some flexibility
+here when determining the lifetime of a symbol.
 
 ### var
 
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index b0660df..dcd7fdc 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -717,7 +717,7 @@
 			}
 		}
 
-		for propName, productConfigProps := range android.ProductVariableProperties(ctx) {
+		for propName, productConfigProps := range android.ProductVariableProperties(ctx, ctx.Module()) {
 			for configProp, propVal := range productConfigProps {
 				if propName == "Src" {
 					props, ok := propVal.(*string)
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index a6477dd..0d44c31 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -140,6 +140,7 @@
 		"LOCAL_REQUIRED_MODULES":        {"modA", "moduleB"},
 		"LOCAL_HOST_REQUIRED_MODULES":   {"hostModA", "hostModB"},
 		"LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"},
+		"LOCAL_SOONG_MODULE_TYPE":       {"prebuilt_etc"},
 	}
 
 	mod := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 25b8fe8..023c69a 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -22,6 +22,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/cc"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -498,3 +499,11 @@
 	}
 	return fmt.Sprintf("%x", h.Sum(nil))
 }
+
+// Base cc.UseCoverage
+
+var _ cc.UseCoverage = (*filesystem)(nil)
+
+func (*filesystem) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 444ffd0..aef4756 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -20,6 +20,9 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/etc"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func TestMain(m *testing.M) {
@@ -28,6 +31,7 @@
 
 var fixture = android.GroupFixturePreparers(
 	android.PrepareForIntegrationTestWithAndroid,
+	etc.PrepareForTestWithPrebuiltEtc,
 	cc.PrepareForIntegrationTestWithCc,
 	PrepareForTestWithFilesystemBuildComponents,
 )
@@ -188,3 +192,93 @@
 	android.AssertStringDoesContain(t, "Can't find --include_descriptors_from_image",
 		cmd, "--include_descriptors_from_image ")
 }
+
+func TestFileSystemShouldInstallCoreVariantIfTargetBuildAppsIsSet(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		fixture,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Unbundled_build_apps = []string{"bar"}
+		}),
+	)
+	result := context.RunTestWithBp(t, `
+		android_system_image {
+			name: "myfilesystem",
+			deps: [
+				"libfoo",
+			],
+			linker_config_src: "linker.config.json",
+		}
+
+		cc_library {
+			name: "libfoo",
+			shared_libs: [
+				"libbar",
+			],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			sdk_version: "9",
+			stl: "none",
+		}
+	`)
+
+	inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
+	android.AssertStringListContains(t, "filesystem should have libbar even for unbundled build",
+		inputs.Strings(),
+		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
+}
+
+func TestFileSystemWithCoverageVariants(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		fixture,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.GcovCoverage = proptools.BoolPtr(true)
+			variables.Native_coverage = proptools.BoolPtr(true)
+		}),
+	)
+
+	result := context.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "prebuilt",
+			src: ":myfilesystem",
+		}
+
+		android_system_image {
+			name: "myfilesystem",
+			deps: [
+				"libfoo",
+			],
+			linker_config_src: "linker.config.json",
+		}
+
+		cc_library {
+			name: "libfoo",
+			shared_libs: [
+				"libbar",
+			],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+		}
+	`)
+
+	filesystem := result.ModuleForTests("myfilesystem", "android_common_cov")
+	inputs := filesystem.Output("deps.zip").Implicits
+	android.AssertStringListContains(t, "filesystem should have libfoo(cov)",
+		inputs.Strings(),
+		"out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so")
+	android.AssertStringListContains(t, "filesystem should have libbar(cov)",
+		inputs.Strings(),
+		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared_cov/libbar.so")
+
+	filesystemOutput := filesystem.Output("myfilesystem.img").Output
+	prebuiltInput := result.ModuleForTests("prebuilt", "android_arm64_armv8-a").Rule("Cp").Input
+	if filesystemOutput != prebuiltInput {
+		t.Error("prebuilt should use cov variant of filesystem")
+	}
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 5e5769b..8175a37 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -69,42 +69,222 @@
 	Dir          string
 }
 
-type PrivilegedLevel string
+type Vector string
 
 const (
-	// Environment with the most minimal permissions.
-	Constrained PrivilegedLevel = "Constrained"
-	// Typical execution environment running unprivileged code.
-	Unprivileged = "Unprivileged"
-	// May have access to elevated permissions.
-	Privileged = "Privileged"
-	// Trusted computing base.
-	Tcb = "TCB"
-	// Bootloader chain.
-	Bootloader = "Bootloader"
-	// Tusted execution environment.
-	Tee = "Tee"
-	// Secure enclave.
-	Se = "Se"
-	// Other.
-	Other = "Other"
+	unknown_access_vector Vector = "unknown_access_vector"
+	// The code being fuzzed is reachable from a remote source, or using data
+	// provided by a remote source.  For example: media codecs process media files
+	// from the internet, SMS processing handles remote message data.
+	// See
+	// https://source.android.com/docs/security/overview/updates-resources#local-vs-remote
+	// for an explanation of what's considered "remote."
+	remote = "remote"
+	// The code being fuzzed can only be reached locally, such as from an
+	// installed app.  As an example, if it's fuzzing a Binder interface, it's
+	// assumed that you'd need a local app to make arbitrary Binder calls.
+	// And the app that's calling the fuzzed code does not require any privileges;
+	// any 3rd party app could make these calls.
+	local_no_privileges_required = "local_no_privileges_required"
+	// The code being fuzzed can only be called locally, and the calling process
+	// requires additional permissions that prevent arbitrary 3rd party apps from
+	// calling the code.  For instance: this requires a privileged or signature
+	// permission to reach, or SELinux restrictions prevent the untrusted_app
+	// domain from calling it.
+	local_privileges_required = "local_privileges_required"
+	// The code is only callable on a PC host, not on a production Android device.
+	// For instance, this is fuzzing code used during the build process, or
+	// tooling that does not exist on a user's actual Android device.
+	host_access = "host_access"
+	// The code being fuzzed is only reachable if the user has enabled Developer
+	// Options, or has enabled a persistent Developer Options setting.
+	local_with_developer_options = "local_with_developer_options"
 )
 
+func (vector Vector) isValidVector() bool {
+	switch vector {
+	case "",
+		unknown_access_vector,
+		remote,
+		local_no_privileges_required,
+		local_privileges_required,
+		host_access,
+		local_with_developer_options:
+		return true
+	}
+	return false
+}
+
+type ServicePrivilege string
+
+const (
+	unknown_service_privilege ServicePrivilege = "unknown_service_privilege"
+	// The code being fuzzed runs on a Secure Element.  This has access to some
+	// of the most privileged data on the device, such as authentication keys.
+	// Not all devices have a Secure Element.
+	secure_element = "secure_element"
+	// The code being fuzzed runs in the TEE.  The TEE is designed to be resistant
+	// to a compromised kernel, and stores sensitive data.
+	trusted_execution = "trusted_execution"
+	// The code being fuzzed has privileges beyond what arbitrary 3rd party apps
+	// have.  For instance, it's running as the System UID, or it's in an SELinux
+	// domain that's able to perform calls that can't be made by 3rd party apps.
+	privileged = "privileged"
+	// The code being fuzzed is equivalent to a 3rd party app.  It runs in the
+	// untrusted_app SELinux domain, or it only has privileges that are equivalent
+	// to what a 3rd party app could have.
+	unprivileged = "unprivileged"
+	// The code being fuzzed is significantly constrained, and even if it's
+	// compromised, it has significant restrictions that prevent it from
+	// performing most actions.  This is significantly more restricted than
+	// UNPRIVILEGED.  An example is the isolatedProcess=true setting in a 3rd
+	// party app.  Or a process that's very restricted by SELinux, such as
+	// anything in the mediacodec SELinux domain.
+	constrained = "constrained"
+	// The code being fuzzed always has Negligible Security Impact.  Even
+	// arbitrary out of bounds writes and full code execution would not be
+	// considered a security vulnerability.  This typically only makes sense if
+	// FuzzedCodeUsage is set to FUTURE_VERSION or EXPERIMENTAL, and if
+	// AutomaticallyRouteTo is set to ALWAYS_NSI.
+	nsi = "nsi"
+	// The code being fuzzed only runs on a PC host, not on a production Android
+	// device.  For instance, the fuzzer is fuzzing code used during the build
+	// process, or tooling that does not exist on a user's actual Android device.
+	host_only = "host_only"
+)
+
+func (service_privilege ServicePrivilege) isValidServicePrivilege() bool {
+	switch service_privilege {
+	case "",
+		unknown_service_privilege,
+		secure_element,
+		trusted_execution,
+		privileged,
+		unprivileged,
+		constrained,
+		nsi,
+		host_only:
+		return true
+	}
+	return false
+}
+
+type UserData string
+
+const (
+	unknown_user_data UserData = "unknown_user_data"
+	// The process being fuzzed only handles data from a single user, or from a
+	// single process or app.  It's possible the process shuts down before
+	// handling data from another user/process/app, or it's possible the process
+	// only ever handles one user's/process's/app's data.  As an example, some
+	// print spooler processes are started for a single document and terminate
+	// when done, so each instance only handles data from a single user/app.
+	single_user = "single_user"
+	// The process handles data from multiple users, or from multiple other apps
+	// or processes.  Media processes, for instance, can handle media requests
+	// from multiple different apps without restarting.  Wi-Fi and network
+	// processes handle data from multiple users, and processes, and apps.
+	multi_user = "multi_user"
+)
+
+func (user_data UserData) isValidUserData() bool {
+	switch user_data {
+	case "",
+		unknown_user_data,
+		single_user,
+		multi_user:
+		return true
+	}
+	return false
+}
+
+type FuzzedCodeUsage string
+
+const (
+	undefined FuzzedCodeUsage = "undefined"
+	unknown                   = "unknown"
+	// The code being fuzzed exists in a shipped version of Android and runs on
+	// devices in production.
+	shipped = "shipped"
+	// The code being fuzzed is not yet in a shipping version of Android, but it
+	// will be at some point in the future.
+	future_version = "future_version"
+	// The code being fuzzed is not in a shipping version of Android, and there
+	// are no plans to ship it in the future.
+	experimental = "experimental"
+)
+
+func (fuzzed_code_usage FuzzedCodeUsage) isValidFuzzedCodeUsage() bool {
+	switch fuzzed_code_usage {
+	case "",
+		undefined,
+		unknown,
+		shipped,
+		future_version,
+		experimental:
+		return true
+	}
+	return false
+}
+
+type AutomaticallyRouteTo string
+
+const (
+	undefined_routing AutomaticallyRouteTo = "undefined_routing"
+	// Automatically route this to the Android Automotive security team for
+	// assessment.
+	android_automotive = "android_automotive"
+	// This should not be used in fuzzer configurations.  It is used internally
+	// by Severity Assigner to flag memory leak reports.
+	memory_leak = "memory_leak"
+	// Route this vulnerability to our Ittiam vendor team for assessment.
+	ittiam = "ittiam"
+	// Reports from this fuzzer are always NSI (see the NSI ServicePrivilegeEnum
+	// value for additional context).  It is not possible for this code to ever
+	// have a security vulnerability.
+	always_nsi = "always_nsi"
+	// Route this vulnerability to AIDL team for assessment.
+	aidl = "aidl"
+)
+
+func (automatically_route_to AutomaticallyRouteTo) isValidAutomaticallyRouteTo() bool {
+	switch automatically_route_to {
+	case "",
+		undefined_routing,
+		android_automotive,
+		memory_leak,
+		ittiam,
+		always_nsi,
+		aidl:
+		return true
+	}
+	return false
+}
+
 func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
 	var config = fuzzModule.FuzzProperties.Fuzz_config
 	if config != nil {
-		var level = PrivilegedLevel(config.Privilege_level)
-		if level != "" {
-			switch level {
-			case Constrained, Unprivileged, Privileged, Tcb, Bootloader, Tee, Se, Other:
-				return true
-			}
-			panic(fmt.Errorf("Invalid privileged level in fuzz config in %s", moduleName))
+		if !config.Vector.isValidVector() {
+			panic(fmt.Errorf("Invalid vector in fuzz config in %s", moduleName))
 		}
-		return true
-	} else {
-		return false
+
+		if !config.Service_privilege.isValidServicePrivilege() {
+			panic(fmt.Errorf("Invalid service_privilege in fuzz config in %s", moduleName))
+		}
+
+		if !config.Users.isValidUserData() {
+			panic(fmt.Errorf("Invalid users (user_data) in fuzz config in %s", moduleName))
+		}
+
+		if !config.Fuzzed_code_usage.isValidFuzzedCodeUsage() {
+			panic(fmt.Errorf("Invalid fuzzed_code_usage in fuzz config in %s", moduleName))
+		}
+
+		if !config.Automatically_route_to.isValidAutomaticallyRouteTo() {
+			panic(fmt.Errorf("Invalid automatically_route_to in fuzz config in %s", moduleName))
+		}
 	}
+	return true
 }
 
 type FuzzConfig struct {
@@ -112,19 +292,24 @@
 	Cc []string `json:"cc,omitempty"`
 	// A brief description of what the fuzzed code does.
 	Description string `json:"description,omitempty"`
-	// Can this code be triggered remotely or only locally.
-	Remotely_accessible *bool `json:"remotely_accessible,omitempty"`
-	// Is the fuzzed code host only, i.e. test frameworks or support utilities.
-	Host_only *bool `json:"host_only,omitempty"`
+	// Whether the code being fuzzed is remotely accessible or requires privileges
+	// to access locally.
+	Vector Vector `json:"vector,omitempty"`
+	// How privileged the service being fuzzed is.
+	Service_privilege ServicePrivilege `json:"service_privilege,omitempty"`
+	// Whether the service being fuzzed handles data from multiple users or only
+	// a single one.
+	Users UserData `json:"users,omitempty"`
+	// Specifies the use state of the code being fuzzed. This state factors into
+	// how an issue is handled.
+	Fuzzed_code_usage FuzzedCodeUsage `json:"fuzzed_code_usage,omitempty"`
+	// Comment describing how we came to these settings for this fuzzer.
+	Config_comment string
+	// Which team to route this to, if it should be routed automatically.
+	Automatically_route_to AutomaticallyRouteTo `json:"automatically_route_to,omitempty"`
 	// Can third party/untrusted apps supply data to fuzzed code.
 	Untrusted_data *bool `json:"untrusted_data,omitempty"`
-	// Is the code being fuzzed in a privileged, constrained or any other
-	// context from:
-	// https://source.android.com/security/overview/updates-resources#context_types.
-	Privilege_level PrivilegedLevel `json:"privilege_level,omitempty"`
-	// Can the fuzzed code isolated or can be called by multiple users/processes.
-	Isolated *bool `json:"users_isolation,omitempty"`
-	// When code was relaeased or will be released.
+	// When code was released or will be released.
 	Production_date string `json:"production_date,omitempty"`
 	// Prevents critical service functionality like phone calls, bluetooth, etc.
 	Critical *bool `json:"critical,omitempty"`
@@ -134,7 +319,7 @@
 	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
 	// Component in Google's bug tracking system that bugs should be filed to.
 	Componentid *int64 `json:"componentid,omitempty"`
-	// Hotlists in Google's bug tracking system that bugs should be marked with.
+	// Hotlist(s) in Google's bug tracking system that bugs should be marked with.
 	Hotlists []string `json:"hotlists,omitempty"`
 	// Specify whether this fuzz target was submitted by a researcher. Defaults
 	// to false.
diff --git a/java/OWNERS b/java/OWNERS
deleted file mode 100644
index 5b71b1e..0000000
--- a/java/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com
-
-# For metalava team to disable lint checks in platform
-per-file droidstubs.go = aurimas@google.com,emberrose@google.com,sjgilbert@google.com
\ No newline at end of file
diff --git a/java/aar.go b/java/aar.go
index f4a2ff2..f162a17 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -219,13 +219,32 @@
 	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
 	linkDeps = append(linkDeps, assetDeps...)
 
-	// SDK version flags
-	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
-	if err != nil {
-		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	// Returns the effective version for {min|target}_sdk_version
+	effectiveVersionString := func(sdkVersion android.SdkSpec, minSdkVersion android.ApiLevel) string {
+		// If {min|target}_sdk_version is current, use sdk_version to determine the effective level
+		// This is necessary for vendor modules.
+		// The effective version does not _only_ depend on {min|target}_sdk_version(level),
+		// but also on the sdk_version (kind+level)
+		if minSdkVersion.IsCurrent() {
+			ret, err := sdkVersion.EffectiveVersionString(ctx)
+			if err != nil {
+				ctx.ModuleErrorf("invalid sdk_version: %s", err)
+			}
+			return ret
+		}
+		ret, err := minSdkVersion.EffectiveVersionString(ctx)
+		if err != nil {
+			ctx.ModuleErrorf("invalid min_sdk_version: %s", err)
+		}
+		return ret
 	}
+	// SDK version flags
+	sdkVersion := sdkContext.SdkVersion(ctx)
+	minSdkVersion := effectiveVersionString(sdkVersion, sdkContext.MinSdkVersion(ctx))
 
 	linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
+	// Use minSdkVersion for target-sdk-version, even if `target_sdk_version` is set
+	// This behavior has been copied from Make.
 	linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
 
 	// Version code
@@ -651,6 +670,8 @@
 	// Functionality common to Module and Import.
 	embeddableInModuleAndImport
 
+	providesTransitiveHeaderJars
+
 	properties AARImportProperties
 
 	classpathFile         android.WritablePath
@@ -668,7 +689,7 @@
 	jniPackages android.Paths
 
 	sdkVersion    android.SdkSpec
-	minSdkVersion android.SdkSpec
+	minSdkVersion android.ApiLevel
 }
 
 var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -693,19 +714,19 @@
 	return ""
 }
 
-func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
 	if a.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(ctx, *a.properties.Min_sdk_version)
+		return android.ApiLevelFrom(ctx, *a.properties.Min_sdk_version)
 	}
-	return a.SdkVersion(ctx)
+	return a.SdkVersion(ctx).ApiLevel
 }
 
 func (a *AARImport) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, "")
 }
 
-func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return a.SdkVersion(ctx)
+func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return a.SdkVersion(ctx).ApiLevel
 }
 
 func (a *AARImport) javaVersion() string {
@@ -897,8 +918,11 @@
 		a.assetsPackage = mergedAssets
 	}
 
+	a.collectTransitiveHeaderJars(ctx)
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
+		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
+		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
 		ImplementationJars:             android.PathsIfNonNil(a.classpathFile),
 	})
@@ -1031,7 +1055,7 @@
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "aar_import",
-			Bzl_load_location: "@rules_android//rules:rules.bzl",
+			Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 		},
 		android.CommonAttributes{Name: name},
 		&bazelAndroidLibraryImport{
@@ -1041,6 +1065,24 @@
 		},
 	)
 
+	neverlink := true
+	ctx.CreateBazelTargetModule(
+		AndroidLibraryBazelTargetModuleProperties(),
+		android.CommonAttributes{Name: name + "-neverlink"},
+		&bazelAndroidLibrary{
+			javaLibraryAttributes: &javaLibraryAttributes{
+				Neverlink: bazel.BoolAttribute{Value: &neverlink},
+				Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+			},
+		},
+	)
+
+}
+func AndroidLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
+	return bazel.BazelTargetModuleProperties{
+		Rule_class:        "android_library",
+		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+	}
 }
 
 func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@@ -1053,12 +1095,8 @@
 	} else if !depLabels.Deps.IsEmpty() {
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
 	}
-
 	name := a.Name()
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "android_library",
-		Bzl_load_location: "@rules_android//rules:rules.bzl",
-	}
+	props := AndroidLibraryBazelTargetModuleProperties()
 
 	ctx.CreateBazelTargetModule(
 		props,
diff --git a/java/android_manifest.go b/java/android_manifest.go
index c785310..dbcf098 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -44,14 +44,14 @@
 // When TARGET_BUILD_APPS is not empty, this method returns 10000 for modules targeting an unreleased SDK
 // This enables release builds (that run with TARGET_BUILD_APPS=[val...]) to target APIs that have not yet been finalized as part of an SDK
 func targetSdkVersionForManifestFixer(ctx android.ModuleContext, params ManifestFixerParams) string {
-	targetSdkVersionSpec := params.SdkContext.TargetSdkVersion(ctx)
+	targetSdkVersionLevel := params.SdkContext.TargetSdkVersion(ctx)
 
 	// Check if we want to return 10000
 	// TODO(b/240294501): Determine the rules for handling test apexes
-	if shouldReturnFinalOrFutureInt(ctx, targetSdkVersionSpec, params.EnforceDefaultTargetSdkVersion) {
+	if shouldReturnFinalOrFutureInt(ctx, targetSdkVersionLevel, params.EnforceDefaultTargetSdkVersion) {
 		return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt())
 	}
-	targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx)
+	targetSdkVersion, err := targetSdkVersionLevel.EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
 	}
@@ -62,11 +62,11 @@
 // 1. The module is built in unbundled mode (TARGET_BUILD_APPS not empty)
 // 2. The module is run as part of MTS, and should be testable on stable branches
 // Do not return 10000 if we are enforcing default targetSdkVersion and sdk has been finalised
-func shouldReturnFinalOrFutureInt(ctx android.ModuleContext, targetSdkVersionSpec android.SdkSpec, enforceDefaultTargetSdkVersion bool) bool {
+func shouldReturnFinalOrFutureInt(ctx android.ModuleContext, targetSdkVersionLevel android.ApiLevel, enforceDefaultTargetSdkVersion bool) bool {
 	if enforceDefaultTargetSdkVersion && ctx.Config().PlatformSdkFinal() {
 		return false
 	}
-	return targetSdkVersionSpec.ApiLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
+	return targetSdkVersionLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
 }
 
 // Helper function that casts android.Module to java.androidTestApp
@@ -149,13 +149,14 @@
 
 	if params.SdkContext != nil {
 		targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
-		args = append(args, "--targetSdkVersion ", targetSdkVersion)
 
 		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
 			targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
 			deps = append(deps, ApiFingerprintPath(ctx))
 		}
 
+		args = append(args, "--targetSdkVersion ", targetSdkVersion)
+
 		minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
diff --git a/java/app.go b/java/app.go
index 845f850..52caf6d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -752,7 +752,7 @@
 
 type appDepsInterface interface {
 	SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
-	MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+	MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
 	RequiresStableAPIs(ctx android.BaseModuleContext) bool
 }
 
@@ -865,10 +865,10 @@
 		} else {
 			toMinSdkVersion := "(no version)"
 			if m, ok := to.(interface {
-				MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+				MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
 			}); ok {
-				if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
-					toMinSdkVersion = v.ApiLevel.String()
+				if v := m.MinSdkVersion(ctx); !v.IsNone() {
+					toMinSdkVersion = v.String()
 				}
 			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
 				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
@@ -984,8 +984,11 @@
 	// The name of the android_app module that the tests will run against.
 	Instrumentation_for *string
 
-	// if specified, the instrumentation target package name in the manifest is overwritten by it.
+	// If specified, the instrumentation target package name in the manifest is overwritten by it.
 	Instrumentation_target_package *string
+
+	// If specified, the mainline module package name in the test config is overwritten by it.
+	Mainline_package_name *string
 }
 
 type AndroidTest struct {
@@ -1063,6 +1066,11 @@
 			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
 	}
 
+	if a.appTestProperties.Mainline_package_name != nil {
+		fixNeeded = true
+		command.FlagWithArg("--mainline-package-name ", *a.appTestProperties.Mainline_package_name)
+	}
+
 	if fixNeeded {
 		rule.Build("fix_test_config", "fix test config")
 		return fixedConfig
@@ -1312,6 +1320,9 @@
 			ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
 			ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
+	} else {
+		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
+		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
 	}
 }
 
@@ -1478,12 +1489,16 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_app_certificate",
-		Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
 }
 
+type manifestValueAttribute struct {
+	MinSdkVersion *string
+}
+
 type bazelAndroidAppAttributes struct {
 	*javaCommonAttributes
 	*bazelAapt
@@ -1491,6 +1506,7 @@
 	Custom_package   *string
 	Certificate      bazel.LabelAttribute
 	Certificate_name bazel.StringAttribute
+	Manifest_values  *manifestValueAttribute
 }
 
 // ConvertWithBp2build is used to convert android_app to Bazel.
@@ -1505,20 +1521,56 @@
 
 	certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate)
 
-	attrs := &bazelAndroidAppAttributes{
-		commonAttrs,
-		aapt,
-		deps,
+	manifestValues := &manifestValueAttribute{}
+	// TODO(b/274474008 ): Directly convert deviceProperties.Min_sdk_version in bp2build
+	// 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 {
+			manifestValues.MinSdkVersion = &minSdkStr
+		}
+	}
+
+	appAttrs := &bazelAndroidAppAttributes{
 		// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
-		a.overridableAppProperties.Package_name,
-		certificate,
-		certificateName,
+		Custom_package:   a.overridableAppProperties.Package_name,
+		Certificate:      certificate,
+		Certificate_name: certificateName,
+		Manifest_values:  manifestValues,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_binary",
-		Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 	}
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+
+	if !bp2BuildInfo.hasKotlin {
+		appAttrs.javaCommonAttributes = commonAttrs
+		appAttrs.bazelAapt = aapt
+		appAttrs.Deps = deps
+	} else {
+		ktName := a.Name() + "_kt"
+		ctx.CreateBazelTargetModule(
+			AndroidLibraryBazelTargetModuleProperties(),
+			android.CommonAttributes{Name: ktName},
+			&bazelAndroidLibrary{
+				javaLibraryAttributes: &javaLibraryAttributes{
+					javaCommonAttributes: commonAttrs,
+					Deps:                 deps,
+				},
+				bazelAapt: aapt,
+			},
+		)
+
+		appAttrs.bazelAapt = &bazelAapt{Manifest: aapt.Manifest}
+		appAttrs.Deps = bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + ktName})
+	}
+
+	ctx.CreateBazelTargetModule(
+		props,
+		android.CommonAttributes{Name: a.Name()},
+		appAttrs,
+	)
 
 }
diff --git a/java/app_import.go b/java/app_import.go
index 8c1e19c..c1de667 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 (
+	"github.com/google/blueprint"
 	"reflect"
 
 	"github.com/google/blueprint/proptools"
@@ -31,6 +32,24 @@
 	initAndroidAppImportVariantGroupTypes()
 }
 
+var (
+	uncompressEmbeddedJniLibsRule = pctx.AndroidStaticRule("uncompress-embedded-jni-libs", blueprint.RuleParams{
+		Command: `if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+			`${config.Zip2ZipCmd} -i $in -o $out -0 'lib/**/*.so'` +
+			`; else cp -f $in $out; fi`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Uncompress embedded JNI libs",
+	})
+
+	uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
+		Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+			`${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
+			`; else cp -f $in $out; fi`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Uncompress dex files",
+	})
+)
+
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
 	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
@@ -193,15 +212,12 @@
 		})
 		return
 	}
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
-		BuiltTool("zip2zip").
-		FlagWithInput("-i ", inputPath).
-		FlagWithOutput("-o ", outputPath).
-		FlagWithArg("-0 ", "'lib/**/*.so'").
-		Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
-	rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   uncompressEmbeddedJniLibsRule,
+		Input:  inputPath,
+		Output: outputPath,
+	})
 }
 
 // Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -218,19 +234,6 @@
 	return shouldUncompressDex(ctx, &a.dexpreopter)
 }
 
-func (a *AndroidAppImport) uncompressDex(
-	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
-		BuiltTool("zip2zip").
-		FlagWithInput("-i ", inputPath).
-		FlagWithOutput("-o ", outputPath).
-		FlagWithArg("-0 ", "'classes*.dex'").
-		Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
-	rule.Build("uncompress-dex", "Uncompress dex files")
-}
-
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
 }
@@ -306,7 +309,11 @@
 	a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
 	if a.dexpreopter.uncompressedDex {
 		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
-		a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   uncompressDexRule,
+			Input:  jnisUncompressed,
+			Output: dexUncompressed,
+		})
 		jnisUncompressed = dexUncompressed
 	}
 
@@ -419,8 +426,8 @@
 	return android.SdkSpecPrivate
 }
 
-func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return android.SdkSpecPrivate
+func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return android.SdkSpecPrivate.ApiLevel
 }
 
 func (a *AndroidAppImport) LintDepSets() LintDepSets {
diff --git a/java/app_import_test.go b/java/app_import_test.go
index ad27e3a..528fffe 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"reflect"
-	"regexp"
 	"strings"
 	"testing"
 
@@ -294,7 +293,6 @@
 		},
 	}
 
-	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
 	for _, test := range testCases {
 		result := android.GroupFixturePreparers(
 			PrepareForTestWithJavaDefaultModules,
@@ -305,13 +303,9 @@
 		).RunTestWithBp(t, bp)
 
 		variant := result.ModuleForTests("foo", "android_common")
-		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
-		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
-		if len(matches) != 2 {
-			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
-		}
-		if strings.HasSuffix(matches[1], test.expected) {
-			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+		if strings.HasSuffix(input, test.expected) {
+			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
 		}
 
 		provenanceMetaDataRule := variant.Rule("genProvenanceMetaData")
@@ -369,11 +363,14 @@
 
 		a := variant.Module().(*AndroidAppImport)
 		expectedValues := []string{test.expected}
-		actualValues := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+		entries := android.AndroidMkEntriesForTest(t, ctx, a)[0]
+		actualValues := entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
 		if !reflect.DeepEqual(actualValues, expectedValues) {
 			t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
 				actualValues, expectedValues)
 		}
+		android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "android_app_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+
 		rule := variant.Rule("genProvenanceMetaData")
 		android.AssertStringEquals(t, "Invalid input", test.expectedArtifactPath, rule.Inputs[0].String())
 		android.AssertStringEquals(t, "Invalid output", test.expectedMetaDataPath, rule.Output.String())
@@ -456,7 +453,6 @@
 		},
 	}
 
-	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
 	for _, test := range testCases {
 		ctx, _ := testJava(t, test.bp)
 
@@ -469,13 +465,9 @@
 			android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
 			continue
 		}
-		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
-		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
-		if len(matches) != 2 {
-			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
-		}
-		if strings.HasSuffix(matches[1], test.expected) {
-			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+		if strings.HasSuffix(input, test.expected) {
+			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
 		}
 		rule := variant.Rule("genProvenanceMetaData")
 		android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
@@ -571,6 +563,7 @@
 	} else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
 		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
 	}
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "android_app_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
@@ -686,8 +679,8 @@
 		`)
 
 	variant := ctx.ModuleForTests("foo", "android_common")
-	jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
-	if !strings.HasPrefix(jniRule, "if (zipinfo") {
+	jniRule := variant.Output("jnis-uncompressed/foo.apk").BuildParams.Rule.String()
+	if jniRule == android.Cp.String() {
 		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
 	}
 
diff --git a/java/app_set.go b/java/app_set.go
index 0f55b77..d2d3b06 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -142,6 +142,7 @@
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
 				"screen-densities":  screenDensities,
 				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
+				"skip-sdk-check":    strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
 				"stem":              as.BaseModuleName(),
 				"apkcerts":          as.apkcertsFile.String(),
 				"partition":         as.PartitionTag(ctx.DeviceConfig()),
diff --git a/java/app_set_test.go b/java/app_set_test.go
index 03eb667..10bc5de 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -89,6 +89,7 @@
 				"allow-prereleased": "false",
 				"screen-densities":  "LDPI,XXHDPI",
 				"sdk-version":       "29",
+				"skip-sdk-check":    "false",
 				"stem":              "foo",
 			},
 		},
@@ -105,6 +106,7 @@
 				"allow-prereleased": "false",
 				"screen-densities":  "all",
 				"sdk-version":       "30",
+				"skip-sdk-check":    "false",
 				"stem":              "foo",
 			},
 		},
diff --git a/java/app_test.go b/java/app_test.go
index cd88864..561be68 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1005,6 +1005,7 @@
 		platformSdkInt        int
 		platformSdkCodename   string
 		platformSdkFinal      bool
+		minSdkVersionBp       string
 		expectedMinSdkVersion string
 		platformApis          bool
 		activeCodenames       []string
@@ -1052,6 +1053,14 @@
 			platformSdkCodename:   "S",
 			activeCodenames:       []string{"S"},
 		},
+		{
+			name:                  "two active SDKs",
+			sdkVersion:            "module_current",
+			minSdkVersionBp:       "UpsideDownCake",
+			expectedMinSdkVersion: "UpsideDownCake", // And not VanillaIceCream
+			platformSdkCodename:   "VanillaIceCream",
+			activeCodenames:       []string{"UpsideDownCake", "VanillaIceCream"},
+		},
 	}
 
 	for _, moduleType := range []string{"android_app", "android_library"} {
@@ -1061,12 +1070,17 @@
 				if test.platformApis {
 					platformApiProp = "platform_apis: true,"
 				}
+				minSdkVersionProp := ""
+				if test.minSdkVersionBp != "" {
+					minSdkVersionProp = fmt.Sprintf(` min_sdk_version: "%s",`, test.minSdkVersionBp)
+				}
 				bp := fmt.Sprintf(`%s {
 					name: "foo",
 					srcs: ["a.java"],
 					sdk_version: "%s",
 					%s
-				}`, moduleType, test.sdkVersion, platformApiProp)
+					%s
+				}`, moduleType, test.sdkVersion, platformApiProp, minSdkVersionProp)
 
 				result := android.GroupFixturePreparers(
 					prepareForJavaTest,
@@ -2330,12 +2344,14 @@
 			srcs: ["b.java"],
 			package_name: "com.android.bar.test",
 			instrumentation_for: "foo",
+			mainline_package_name: "com.android.bar",
 		}
 
 		override_android_test {
 			name: "baz_test",
 			base: "foo_test",
 			package_name: "com.android.baz.test",
+			mainline_package_name: "com.android.baz",
 		}
 		`)
 
@@ -2354,6 +2370,7 @@
 			expectedFlags: []string{
 				"--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
 				"--package-name com.android.bar.test",
+				"--mainline-package-name com.android.bar",
 			},
 		},
 		{
@@ -2363,6 +2380,8 @@
 				"--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
 				"--package-name com.android.baz.test",
 				"--test-file-name baz_test.apk",
+				"out/soong/.intermediates/foo_test/android_common_baz_test/test_config_fixer/AndroidTest.xml",
+				"--mainline-package-name com.android.baz",
 			},
 		},
 	}
@@ -3058,13 +3077,17 @@
 		},
 	}
 	for _, testCase := range testCases {
+		targetSdkVersionTemplate := ""
+		if testCase.targetSdkVersionInBp != "" {
+			targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
+		}
 		bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				sdk_version: "current",
-				target_sdk_version: "%v",
+				%s
 			}
-			`, testCase.targetSdkVersionInBp)
+			`, targetSdkVersionTemplate)
 		fixture := android.GroupFixturePreparers(
 			prepareForJavaTest,
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -3142,16 +3165,20 @@
 		},
 	}
 	for _, testCase := range testCases {
+		targetSdkVersionTemplate := ""
+		if testCase.targetSdkVersionInBp != nil {
+			targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
+		}
 		bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				sdk_version: "current",
 				min_sdk_version: "29",
-				target_sdk_version: "%v",
+				%s
 				updatable: %t,
 				enforce_default_target_sdk_version: %t
 			}
-			`, proptools.String(testCase.targetSdkVersionInBp), testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable
+			`, targetSdkVersionTemplate, testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable
 
 		fixture := android.GroupFixturePreparers(
 			PrepareForTestWithJavaDefaultModules,
@@ -3163,6 +3190,7 @@
 				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_sdk_codename = &platform_sdk_codename
 				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+				variables.Unbundled_build = proptools.BoolPtr(true)
 				variables.Unbundled_build_apps = []string{"sampleModule"}
 			}),
 		)
@@ -3241,6 +3269,7 @@
 				variables.Platform_sdk_final = &testCase.platform_sdk_final
 				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_sdk_codename = &platform_sdk_codename
+				variables.Unbundled_build = proptools.BoolPtr(true)
 				variables.Unbundled_build_apps = []string{"sampleModule"}
 			}),
 		)
@@ -3311,6 +3340,7 @@
 				variables.Platform_sdk_final = &testCase.platform_sdk_final
 				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_sdk_codename = &platform_sdk_codename
+				variables.Unbundled_build = proptools.BoolPtr(true)
 				variables.Unbundled_build_apps = []string{"sampleModule"}
 			}),
 		)
diff --git a/java/base.go b/java/base.go
index 84fda37..2d213a1 100644
--- a/java/base.go
+++ b/java/base.go
@@ -489,7 +489,7 @@
 	hideApexVariantFromMake bool
 
 	sdkVersion    android.SdkSpec
-	minSdkVersion android.SdkSpec
+	minSdkVersion android.ApiLevel
 	maxSdkVersion android.SdkSpec
 
 	sourceExtensions []string
@@ -665,11 +665,11 @@
 	return proptools.String(j.deviceProperties.System_modules)
 }
 
-func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
 	if j.deviceProperties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(ctx, *j.deviceProperties.Min_sdk_version)
+		return android.ApiLevelFrom(ctx, *j.deviceProperties.Min_sdk_version)
 	}
-	return j.SdkVersion(ctx)
+	return j.SdkVersion(ctx).ApiLevel
 }
 
 func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
@@ -685,14 +685,14 @@
 }
 
 func (j *Module) MinSdkVersionString() string {
-	return j.minSdkVersion.Raw
+	return j.minSdkVersion.String()
 }
 
-func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
 	if j.deviceProperties.Target_sdk_version != nil {
-		return android.SdkSpecFrom(ctx, *j.deviceProperties.Target_sdk_version)
+		return android.ApiLevelFrom(ctx, *j.deviceProperties.Target_sdk_version)
 	}
-	return j.SdkVersion(ctx)
+	return j.SdkVersion(ctx).ApiLevel
 }
 
 func (j *Module) AvailableFor(what string) bool {
@@ -881,7 +881,7 @@
 		j.ignoredAidlPermissionList = android.PathsForModuleSrcExcludes(ctx, exceptions, nil)
 	}
 
-	aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String()
+	aidlMinSdkVersion := j.MinSdkVersion(ctx).String()
 	flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion)
 
 	return strings.Join(flags, " "), deps
@@ -1003,7 +1003,7 @@
 					topLevelDirs[srcFileParts[0]] = true
 				}
 			}
-			patchPaths = append(patchPaths, android.SortedStringKeys(topLevelDirs)...)
+			patchPaths = append(patchPaths, android.SortedKeys(topLevelDirs)...)
 
 			classPath := flags.classpath.FormJavaClassPath("")
 			if classPath != "" {
@@ -1487,7 +1487,14 @@
 			}
 			// Dex compilation
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), implementationAndResourcesJar, jarName)
+			params := &compileDexParams{
+				flags:         flags,
+				sdkVersion:    j.SdkVersion(ctx),
+				minSdkVersion: j.MinSdkVersion(ctx),
+				classesJar:    implementationAndResourcesJar,
+				jarName:       jarName,
+			}
+			dexOutputFile = j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
 				return
 			}
@@ -1535,9 +1542,9 @@
 	}
 
 	if ctx.Device() {
-		lintSDKVersion := func(sdkSpec android.SdkSpec) int {
-			if v := sdkSpec.ApiLevel; !v.IsPreview() {
-				return v.FinalInt()
+		lintSDKVersion := func(apiLevel android.ApiLevel) int {
+			if !apiLevel.IsPreview() {
+				return apiLevel.FinalInt()
 			} else {
 				// When running metalava, we pass --version-codename. When that value
 				// is not REL, metalava will add 1 to the --current-version argument.
@@ -1569,7 +1576,7 @@
 		j.linter.classes = j.implementationJarFile
 		j.linter.minSdkVersion = lintSDKVersion(j.MinSdkVersion(ctx))
 		j.linter.targetSdkVersion = lintSDKVersion(j.TargetSdkVersion(ctx))
-		j.linter.compileSdkVersion = lintSDKVersion(j.SdkVersion(ctx))
+		j.linter.compileSdkVersion = lintSDKVersion(j.SdkVersion(ctx).ApiLevel)
 		j.linter.compileSdkKind = j.SdkVersion(ctx).Kind
 		j.linter.javaLanguageLevel = flags.javaVersion.String()
 		j.linter.kotlinLanguageLevel = "1.3"
@@ -1583,6 +1590,8 @@
 
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
+		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
+		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
 		ImplementationJars:             android.PathsIfNonNil(j.implementationJarFile),
 		ResourceJars:                   android.PathsIfNonNil(j.resourceJar),
@@ -1719,6 +1728,52 @@
 	return instrumentedJar
 }
 
+type providesTransitiveHeaderJars struct {
+	// set of header jars for all transitive libs deps
+	transitiveLibsHeaderJars *android.DepSet
+	// set of header jars for all transitive static libs deps
+	transitiveStaticLibsHeaderJars *android.DepSet
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet {
+	return j.transitiveLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet {
+	return j.transitiveStaticLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) {
+	directLibs := android.Paths{}
+	directStaticLibs := android.Paths{}
+	transitiveLibs := []*android.DepSet{}
+	transitiveStaticLibs := []*android.DepSet{}
+	ctx.VisitDirectDeps(func(module android.Module) {
+		// don't add deps of the prebuilt version of the same library
+		if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
+			return
+		}
+
+		dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		if dep.TransitiveLibsHeaderJars != nil {
+			transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
+		}
+		if dep.TransitiveStaticLibsHeaderJars != nil {
+			transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+		}
+
+		tag := ctx.OtherModuleDependencyTag(module)
+		_, isUsesLibDep := tag.(usesLibraryDependencyTag)
+		if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
+			directLibs = append(directLibs, dep.HeaderJars...)
+		} else if tag == staticLibTag {
+			directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
+		}
+	})
+	j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
+	j.transitiveStaticLibsHeaderJars = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
+}
+
 func (j *Module) HeaderJars() android.Paths {
 	if j.headerJarFile == nil {
 		return nil
@@ -1790,15 +1845,17 @@
 
 // Implements android.ApexModule
 func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
-	sdkSpec := j.MinSdkVersion(ctx)
-	if !sdkSpec.Specified() {
+	sdkVersionSpec := j.SdkVersion(ctx)
+	minSdkVersion := j.MinSdkVersion(ctx)
+	if !minSdkVersion.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
 	}
-	if sdkSpec.Kind == android.SdkCore {
+	// If the module is compiling against core (via sdk_version), skip comparison check.
+	if sdkVersionSpec.Kind == android.SdkCore {
 		return nil
 	}
-	if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
+	if minSdkVersion.GreaterThan(sdkVersion) {
+		return fmt.Errorf("newer SDK(%v)", minSdkVersion)
 	}
 	return nil
 }
@@ -1866,15 +1923,15 @@
 		"stub-annotations", "private-stub-annotations-jar",
 		"core-lambda-stubs", "core-generated-annotation-stubs":
 		return javaCore, true
-	case "android_stubs_current":
+	case android.SdkPublic.JavaLibraryName(ctx.Config()):
 		return javaSdk, true
-	case "android_system_stubs_current":
+	case android.SdkSystem.JavaLibraryName(ctx.Config()):
 		return javaSystem, true
-	case "android_module_lib_stubs_current":
+	case android.SdkModule.JavaLibraryName(ctx.Config()):
 		return javaModule, true
-	case "android_system_server_stubs_current":
+	case android.SdkSystemServer.JavaLibraryName(ctx.Config()):
 		return javaSystemServer, true
-	case "android_test_stubs_current":
+	case android.SdkTest.JavaLibraryName(ctx.Config()):
 		return javaSystem, true
 	}
 
@@ -1947,6 +2004,7 @@
 
 	sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
 
+	j.collectTransitiveHeaderJars(ctx)
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 101d3ca..c07a94a 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -440,7 +440,7 @@
 		return dexJar, nil
 	} else {
 		return nil, fmt.Errorf("unknown bootclasspath_fragment content module %s, expected one of %s",
-			name, strings.Join(android.SortedStringKeys(i.contentModuleDexJarPaths), ", "))
+			name, strings.Join(android.SortedKeys(i.contentModuleDexJarPaths), ", "))
 	}
 }
 
@@ -709,7 +709,7 @@
 	imageName := *imageNamePtr
 	imageConfig := imageConfigs[imageName]
 	if imageConfig == nil {
-		ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
+		ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedKeys(imageConfigs), ", "))
 		return nil
 	}
 	return imageConfig
@@ -1291,7 +1291,7 @@
 		}
 		return bootImageFiles
 	} else {
-		if profile == nil {
+		if profile == nil && imageConfig.isProfileGuided() {
 			ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
 			return bootImageOutputs{}
 		}
diff --git a/java/builder.go b/java/builder.go
index 6f8eec9..4626267 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -131,13 +131,13 @@
 		blueprint.RuleParams{
 			Command: `rm -rf "$out" && ` +
 				`${config.ExtractApksCmd} -o "${out}" -zip "${zip}" -allow-prereleased=${allow-prereleased} ` +
-				`-sdk-version=${sdk-version} -abis=${abis} ` +
+				`-sdk-version=${sdk-version} -skip-sdk-check=${skip-sdk-check} -abis=${abis} ` +
 				`--screen-densities=${screen-densities} --stem=${stem} ` +
 				`-apkcerts=${apkcerts} -partition=${partition} ` +
 				`${in}`,
 			CommandDeps: []string{"${config.ExtractApksCmd}"},
 		},
-		"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition", "zip")
+		"abis", "allow-prereleased", "screen-densities", "sdk-version", "skip-sdk-check", "stem", "apkcerts", "partition", "zip")
 
 	turbine, turbineRE = pctx.RemoteStaticRules("turbine",
 		blueprint.RuleParams{
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 259e977..45e6175 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -111,7 +111,7 @@
 			ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
 		}
 	}
-	return android.SortedStringKeys(set)
+	return android.SortedKeys(set)
 }
 
 // Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
@@ -130,13 +130,13 @@
 				if s, ok := m.(*SdkLibrary); ok {
 					// TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
 					if s.minSdkVersion.Specified() {
-						if s.minSdkVersion.ApiLevel.IsCurrent() {
+						if s.minSdkVersion.IsCurrent() {
 							jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 						} else {
-							jar.minSdkVersion = s.minSdkVersion.ApiLevel.String()
+							jar.minSdkVersion = s.minSdkVersion.String()
 						}
 					}
-					if s.maxSdkVersion.Specified() {
+					if s.maxSdkVersion.ApiLevel.Specified() {
 						if s.maxSdkVersion.ApiLevel.IsCurrent() {
 							jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 						} else {
diff --git a/java/config/config.go b/java/config/config.go
index 49d88c4..13670ee 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -93,11 +93,13 @@
 		"-JXmx4096M",
 		"-JXX:+TieredCompilation",
 		"-JXX:TieredStopAtLevel=1",
+		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
+		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
 		"-JXmx2048M",
-		// Disable this optimization as it can impact weak reference semantics. See b/233432839.
-		"-JDcom.android.tools.r8.disableEnqueuerDeferredTracing=true",
+		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
+		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 	}, dexerJavaVmFlagsList...))
 
 	exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
@@ -197,6 +199,7 @@
 	pctx.HostBinToolVariable("ManifestMergerCmd", "manifest-merger")
 
 	pctx.HostBinToolVariable("Class2NonSdkList", "class2nonsdklist")
+	pctx.HostBinToolVariable("MergeCsvCommand", "merge_csv")
 	pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
 
 	hostBinToolVariableWithSdkToolsPrebuilt("Aapt2Cmd", "aapt2")
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 4fb1d76..b9332dd 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -63,11 +63,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
     sdk_version: "none",
     system_modules: "none",
@@ -148,11 +143,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
     sdk_version: "none",
     system_modules: "none",
@@ -278,11 +268,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
 }
 
@@ -294,11 +279,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
 }
 
@@ -322,11 +302,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
 
         // Ensure that core libraries that depend on the public API can access
         // the UnsupportedAppUsage, CorePlatformApi and IntraCoreApi
diff --git a/java/dex.go b/java/dex.go
index 40ee99d..4d6aa34 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -89,7 +89,10 @@
 	// list of extra proguard flag files
 	extraProguardFlagFiles android.Paths
 	proguardDictionary     android.OptionalPath
+	proguardConfiguration  android.OptionalPath
 	proguardUsageZip       android.OptionalPath
+
+	providesTransitiveHeaderJars
 }
 
 func (d *dexer) effectiveOptimizeEnabled() bool {
@@ -130,17 +133,18 @@
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
+			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
 			`mkdir -p $$(dirname ${outUsage}) && ` +
 			`mkdir -p $$(dirname $tmpJar) && ` +
 			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
 			`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
+			`--pg-conf-output ${outConfig} ` +
 			`-printusage ${outUsage} ` +
 			`--deps-file ${out}.d ` +
 			`$r8Flags && ` +
-			`touch "${outDict}" "${outUsage}" && ` +
+			`touch "${outDict}" "${outConfig}" "${outUsage}" && ` +
 			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
@@ -176,11 +180,11 @@
 			ExecStrategy: "${config.RER8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
+	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
 		"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
-	minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
+	dexParams *compileDexParams) (flags []string, deps android.Paths) {
 
 	flags = d.dexProperties.Dxflags
 	// Translate all the DX flags to D8 ones until all the build files have been migrated
@@ -209,11 +213,11 @@
 	// Note: Targets with a min SDK kind of core_platform (e.g., framework.jar) or unspecified (e.g.,
 	// services.jar), are not classified as stable, which is WAI.
 	// TODO(b/232073181): Expand to additional min SDK cases after validation.
-	if !minSdkVersion.Stable() {
+	if !dexParams.sdkVersion.Stable() {
 		flags = append(flags, "--android-platform-build")
 	}
 
-	effectiveVersion, err := minSdkVersion.EffectiveVersion(ctx)
+	effectiveVersion, err := dexParams.minSdkVersion.EffectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
@@ -249,13 +253,32 @@
 	})
 
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
-	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
-	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
-
 	r8Deps = append(r8Deps, proguardRaiseDeps...)
+	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.bootClasspath...)
+	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.dexClasspath...)
 
+	transitiveStaticLibsLookupMap := map[android.Path]bool{}
+	if d.transitiveStaticLibsHeaderJars != nil {
+		for _, jar := range d.transitiveStaticLibsHeaderJars.ToList() {
+			transitiveStaticLibsLookupMap[jar] = true
+		}
+	}
+	transitiveHeaderJars := android.Paths{}
+	if d.transitiveLibsHeaderJars != nil {
+		for _, jar := range d.transitiveLibsHeaderJars.ToList() {
+			if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
+				// don't include a lib if it is already packaged in the current JAR as a static lib
+				continue
+			}
+			transitiveHeaderJars = append(transitiveHeaderJars, jar)
+		}
+	}
+	transitiveClasspath := classpath(transitiveHeaderJars)
+	r8Flags = append(r8Flags, transitiveClasspath.FormJavaClassPath("-libraryjars"))
+	r8Deps = append(r8Deps, transitiveClasspath...)
+
 	flagFiles := android.Paths{
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
@@ -317,20 +340,27 @@
 	return r8Flags, r8Deps
 }
 
-func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion android.SdkSpec,
-	classesJar android.Path, jarName string) android.OutputPath {
+type compileDexParams struct {
+	flags         javaBuilderFlags
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.ApiLevel
+	classesJar    android.Path
+	jarName       string
+}
+
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
-	javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
+	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
-	tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", jarName)
+	tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", dexParams.jarName)
 
 	zipFlags := "--ignore_missing_files"
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		zipFlags += " -L 0"
 	}
 
-	commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
+	commonFlags, commonDeps := d.dexCommonFlags(ctx, dexParams)
 
 	// Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
 	mergeZipsFlags := ""
@@ -342,18 +372,21 @@
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+		proguardConfiguration := android.PathForModuleOut(ctx, "proguard_configuration")
+		d.proguardConfiguration = android.OptionalPathForPath(proguardConfiguration)
 		proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
 		proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
 			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
 		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
-		r8Flags, r8Deps := d.r8Flags(ctx, flags)
+		r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags)
 		r8Deps = append(r8Deps, commonDeps...)
 		rule := r8
 		args := map[string]string{
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
 			"zipFlags":       zipFlags,
 			"outDict":        proguardDictionary.String(),
+			"outConfig":      proguardConfiguration.String(),
 			"outUsageDir":    proguardUsageDir.String(),
 			"outUsage":       proguardUsage.String(),
 			"outUsageZip":    proguardUsageZip.String(),
@@ -370,12 +403,12 @@
 			Description:     "r8",
 			Output:          javalibJar,
 			ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
-			Input:           classesJar,
+			Input:           dexParams.classesJar,
 			Implicits:       r8Deps,
 			Args:            args,
 		})
 	} else {
-		d8Flags, d8Deps := d8Flags(flags)
+		d8Flags, d8Deps := d8Flags(dexParams.flags)
 		d8Deps = append(d8Deps, commonDeps...)
 		rule := d8
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
@@ -385,7 +418,7 @@
 			Rule:        rule,
 			Description: "d8",
 			Output:      javalibJar,
-			Input:       classesJar,
+			Input:       dexParams.classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
 				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
@@ -397,7 +430,7 @@
 		})
 	}
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
-		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
+		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
 		javalibJar = alignedJavalibJar
 	}
diff --git a/java/dex_test.go b/java/dex_test.go
index fc6cd0f..97fc3d0 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -18,6 +18,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func TestR8(t *testing.T) {
@@ -41,6 +43,7 @@
 			name: "core_platform_app",
 			srcs: ["foo.java"],
 			sdk_version: "core_platform",
+			min_sdk_version: "31",
 		}
 
 		java_library {
@@ -74,7 +77,7 @@
 
 	android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], libHeader.String())
-	android.AssertStringDoesNotContain(t, "expected no  static_lib header jar in app javac classpath",
+	android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], staticLibHeader.String())
 	android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
 		appR8.Args["r8Flags"], "-ignorewarnings")
@@ -86,6 +89,174 @@
 		corePlatformAppR8.Args["r8Flags"], "--android-platform-build")
 }
 
+func TestR8TransitiveDeps(t *testing.T) {
+	bp := `
+		override_android_app {
+			name: "override_app",
+			base: "app",
+		}
+
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			libs: [
+				"lib",
+				"uses_libs_dep_import",
+			],
+			static_libs: [
+				"static_lib",
+				"repeated_dep",
+			],
+			platform_apis: true,
+		}
+
+		java_library {
+			name: "static_lib",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "lib",
+			libs: [
+				"transitive_lib",
+				"repeated_dep",
+				"prebuilt_lib",
+			],
+			static_libs: ["transitive_static_lib"],
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "repeated_dep",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "transitive_static_lib",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "transitive_lib",
+			srcs: ["foo.java"],
+			libs: ["transitive_lib_2"],
+		}
+
+		java_library {
+			name: "transitive_lib_2",
+			srcs: ["foo.java"],
+		}
+
+		java_import {
+			name: "lib",
+			jars: ["lib.jar"],
+		}
+
+		java_library {
+			name: "uses_lib",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "optional_uses_lib",
+			srcs: ["foo.java"],
+		}
+
+		android_library {
+			name: "uses_libs_dep",
+			uses_libs: ["uses_lib"],
+			optional_uses_libs: ["optional_uses_lib"],
+		}
+
+		android_library_import {
+			name: "uses_libs_dep_import",
+			aars: ["aar.aar"],
+			static_libs: ["uses_libs_dep"],
+		}
+	`
+
+	testcases := []struct {
+		name      string
+		unbundled bool
+	}{
+		{
+			name:      "non-unbundled build",
+			unbundled: false,
+		},
+		{
+			name:      "unbundled build",
+			unbundled: true,
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixturePreparer := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd
+			if tc.unbundled {
+				fixturePreparer = android.GroupFixturePreparers(
+					fixturePreparer,
+					android.FixtureModifyProductVariables(
+						func(variables android.FixtureProductVariables) {
+							variables.Unbundled_build = proptools.BoolPtr(true)
+						},
+					),
+				)
+			}
+			result := fixturePreparer.RunTestWithBp(t, bp)
+
+			getHeaderJar := func(name string) android.Path {
+				mod := result.ModuleForTests(name, "android_common")
+				return mod.Output("turbine-combined/" + name + ".jar").Output
+			}
+
+			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+			overrideAppR8 := result.ModuleForTests("app", "android_common_override_app").Rule("r8")
+			appHeader := getHeaderJar("app")
+			overrideAppHeader := result.ModuleForTests("app", "android_common_override_app").Output("turbine-combined/app.jar").Output
+			libHeader := getHeaderJar("lib")
+			transitiveLibHeader := getHeaderJar("transitive_lib")
+			transitiveLib2Header := getHeaderJar("transitive_lib_2")
+			staticLibHeader := getHeaderJar("static_lib")
+			transitiveStaticLibHeader := getHeaderJar("transitive_static_lib")
+			repeatedDepHeader := getHeaderJar("repeated_dep")
+			usesLibHeader := getHeaderJar("uses_lib")
+			optionalUsesLibHeader := getHeaderJar("optional_uses_lib")
+			prebuiltLibHeader := result.ModuleForTests("prebuilt_lib", "android_common").Output("combined/lib.jar").Output
+
+			for _, rule := range []android.TestingBuildParams{appR8, overrideAppR8} {
+				android.AssertStringDoesNotContain(t, "expected no app header jar in app r8 classpath",
+					rule.Args["r8Flags"], appHeader.String())
+				android.AssertStringDoesNotContain(t, "expected no override_app header jar in app r8 classpath",
+					rule.Args["r8Flags"], overrideAppHeader.String())
+				android.AssertStringDoesContain(t, "expected transitive lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], transitiveLibHeader.String())
+				android.AssertStringDoesContain(t, "expected transitive lib ^2 header jar in app r8 classpath",
+					rule.Args["r8Flags"], transitiveLib2Header.String())
+				android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], libHeader.String())
+				android.AssertStringDoesContain(t, "expected uses_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], usesLibHeader.String())
+				android.AssertStringDoesContain(t, "expected optional_uses_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], optionalUsesLibHeader.String())
+				android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], staticLibHeader.String())
+				android.AssertStringDoesNotContain(t, "expected no transitive static_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], transitiveStaticLibHeader.String())
+				// we shouldn't list this dep because it is already included as static_libs in the app
+				android.AssertStringDoesNotContain(t, "expected no repeated_dep header jar in app r8 classpath",
+					rule.Args["r8Flags"], repeatedDepHeader.String())
+				// skip a prebuilt transitive dep if the source is also a transitive dep
+				android.AssertStringDoesNotContain(t, "expected no prebuilt header jar in app r8 classpath",
+					rule.Args["r8Flags"], prebuiltLibHeader.String())
+				android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
+					rule.Args["r8Flags"], "-ignorewarnings")
+				android.AssertStringDoesContain(t, "expected --android-platform-build in app r8 flags",
+					rule.Args["r8Flags"], "--android-platform-build")
+			}
+		})
+	}
+}
+
 func TestR8Flags(t *testing.T) {
 	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
 		android_app {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 77cbe9c..0ffedf6 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -23,10 +23,24 @@
 )
 
 type DexpreopterInterface interface {
-	IsInstallable() bool // Structs that embed dexpreopter must implement this.
+	// True if the java module is to be dexed and installed on devices.
+	// Structs that embed dexpreopter must implement this.
+	IsInstallable() bool
+
+	// True if dexpreopt is disabled for the java module.
 	dexpreoptDisabled(ctx android.BaseModuleContext) bool
+
+	// If the java module is to be installed into an APEX, this list contains information about the
+	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
+	// outside of the APEX.
 	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+
+	// The Make entries to install the dexpreopt outputs. Derived from
+	// `DexpreoptBuiltInstalledForApex`.
 	AndroidMkEntriesForApex() []android.AndroidMkEntries
+
+	// See `dexpreopter.outputProfilePathOnHost`.
+	OutputProfilePathOnHost() android.Path
 }
 
 type dexpreopterInstall struct {
@@ -77,7 +91,8 @@
 }
 
 type dexpreopter struct {
-	dexpreoptProperties DexpreoptProperties
+	dexpreoptProperties       DexpreoptProperties
+	importDexpreoptProperties ImportDexpreoptProperties
 
 	installPath         android.InstallPath
 	uncompressedDex     bool
@@ -103,6 +118,14 @@
 	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
 	//   dexpreopt another partition).
 	configPath android.WritablePath
+
+	// The path to the profile on host that dexpreopter generates. This is used as the input for
+	// dex2oat.
+	outputProfilePathOnHost android.Path
+
+	// The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
+	// set, it overrides the profile settings in `dexpreoptProperties`.
+	inputProfilePathOnHost android.Path
 }
 
 type DexpreoptProperties struct {
@@ -123,6 +146,18 @@
 		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
 		Profile *string `android:"path"`
 	}
+
+	Dex_preopt_result struct {
+		// True if profile-guided optimization is actually enabled.
+		Profile_guided bool
+	} `blueprint:"mutated"`
+}
+
+type ImportDexpreoptProperties struct {
+	Dex_preopt struct {
+		// If true, use the profile in the prebuilt APEX to guide optimization. Defaults to false.
+		Profile_guided *bool
+	}
 }
 
 func init() {
@@ -180,9 +215,8 @@
 
 	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
 	if isApexVariant(ctx) {
-		// Don't preopt APEX variant module unless the module is an APEX system server jar and we are
-		// building the entire system image.
-		if !isApexSystemServerJar || ctx.Config().UnbundledBuild() {
+		// Don't preopt APEX variant module unless the module is an APEX system server jar.
+		if !isApexSystemServerJar {
 			return true
 		}
 	} else {
@@ -259,6 +293,12 @@
 	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
 
 	bootImage := defaultBootImageConfig(ctx)
+	// When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline
+	// boot jars into bootclasspath, so we should include the mainline boot image as well because it's
+	// generated from those jars.
+	if global.PreoptWithUpdatableBcp {
+		bootImage = mainlineBootImageConfig(ctx)
+	}
 	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 
 	targets := ctx.MultiTargets()
@@ -292,7 +332,9 @@
 	var profileClassListing android.OptionalPath
 	var profileBootListing android.OptionalPath
 	profileIsTextListing := false
-	if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
+	if d.inputProfilePathOnHost != nil {
+		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
+	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
 		// If dex_preopt.profile_guided is not set, default it based on the existence of the
 		// dexprepot.profile option or the profile class listing.
 		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
@@ -307,6 +349,8 @@
 		}
 	}
 
+	d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid()
+
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
 		Name:            moduleName(ctx),
@@ -368,21 +412,29 @@
 		installBase := filepath.Base(install.To)
 		arch := filepath.Base(installDir)
 		installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+		isProfile := strings.HasSuffix(installBase, ".prof")
+
+		if isProfile {
+			d.outputProfilePathOnHost = install.From
+		}
 
 		if isApexSystemServerJar {
-			// APEX variants of java libraries are hidden from Make, so their dexpreopt
-			// outputs need special handling. Currently, for APEX variants of java
-			// libraries, only those in the system server classpath are handled here.
-			// Preopting of boot classpath jars in the ART APEX are handled in
-			// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
-			// The installs will be handled by Make as sub-modules of the java library.
-			d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
-				name:                arch + "-" + installBase,
-				moduleName:          moduleName(ctx),
-				outputPathOnHost:    install.From,
-				installDirOnDevice:  installPath,
-				installFileOnDevice: installBase,
-			})
+			// Profiles are handled separately because they are installed into the APEX.
+			if !isProfile {
+				// APEX variants of java libraries are hidden from Make, so their dexpreopt
+				// outputs need special handling. Currently, for APEX variants of java
+				// libraries, only those in the system server classpath are handled here.
+				// Preopting of boot classpath jars in the ART APEX are handled in
+				// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
+				// The installs will be handled by Make as sub-modules of the java library.
+				d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+					name:                arch + "-" + installBase,
+					moduleName:          moduleName(ctx),
+					outputPathOnHost:    install.From,
+					installDirOnDevice:  installPath,
+					installFileOnDevice: installBase,
+				})
+			}
 		} else if !d.preventInstall {
 			ctx.InstallFile(installPath, installBase, install.From)
 		}
@@ -404,3 +456,7 @@
 	}
 	return entries
 }
+
+func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
+	return d.outputProfilePathOnHost
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 3effff6..f4827ea 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"android/soong/android"
@@ -287,6 +288,12 @@
 
 	// Path of the preloaded classes file.
 	preloadedClassesFile string
+
+	// The "--compiler-filter" argument.
+	compilerFilter string
+
+	// The "--single-image" argument.
+	singleImage bool
 }
 
 // Target-dependent description of a boot image.
@@ -309,17 +316,17 @@
 	// All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
 	imagesDeps android.OutputPaths
 
-	// The path to the primary image variant's imagePathOnHost field, where primary image variant
+	// The path to the base image variant's imagePathOnHost field, where base image variant
 	// means the image variant that this extends.
 	//
 	// This is only set for a variant of an image that extends another image.
-	primaryImages android.OutputPath
+	baseImages android.OutputPaths
 
-	// The paths to the primary image variant's imagesDeps field, where primary image variant
+	// The paths to the base image variant's imagesDeps field, where base image variant
 	// means the image variant that this extends.
 	//
 	// This is only set for a variant of an image that extends another image.
-	primaryImagesDeps android.Paths
+	baseImagesDeps android.Paths
 
 	// Rules which should be used in make to install the outputs on host.
 	//
@@ -394,6 +401,9 @@
 		for _, ext := range exts {
 			ret = append(ret, dir.Join(ctx, name+ext))
 		}
+		if image.singleImage {
+			break
+		}
 	}
 	return ret
 }
@@ -443,6 +453,10 @@
 		append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType))
 }
 
+func (image *bootImageConfig) isProfileGuided() bool {
+	return image.compilerFilter == "speed-profile"
+}
+
 func dexpreoptBootJarsFactory() android.SingletonModule {
 	m := &dexpreoptBootJars{}
 	android.InitAndroidModule(m)
@@ -504,8 +518,13 @@
 
 	defaultImageConfig := defaultBootImageConfig(ctx)
 	d.defaultBootImage = defaultImageConfig
-	artBootImageConfig := artBootImageConfig(ctx)
-	d.otherImages = []*bootImageConfig{artBootImageConfig}
+	imageConfigs := genBootImageConfigs(ctx)
+	d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
+	for _, config := range imageConfigs {
+		if config != defaultImageConfig {
+			d.otherImages = append(d.otherImages, config)
+		}
+	}
 }
 
 // shouldBuildBootImages determines whether boot images should be built.
@@ -525,8 +544,8 @@
 func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
 	// Create the super set of module names.
 	names := []string{}
-	names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
-	names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
+	names = append(names, android.SortedKeys(srcBootDexJarsByModule)...)
+	names = append(names, android.SortedKeys(dstBootJarsByModule)...)
 	names = android.SortedUniqueStrings(names)
 	for _, name := range names {
 		src := srcBootDexJarsByModule[name]
@@ -701,9 +720,11 @@
 	}
 
 	if image.extends != nil {
-		// It is a boot image extension, so it needs the boot image it depends on (in this case the
-		// primary ART APEX image).
-		artImage := image.primaryImages
+		// It is a boot image extension, so it needs the boot images that it depends on.
+		baseImageLocations := make([]string, 0, len(image.baseImages))
+		for _, image := range image.baseImages {
+			baseImageLocations = append(baseImageLocations, dexpreopt.PathToLocation(image, arch))
+		}
 		cmd.
 			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
@@ -711,21 +732,23 @@
 			// dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
 			// to the file cannot be passed to the command make sure to add the actual path as an Implicit
 			// dependency to ensure that it is built before the command runs.
-			FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage).
+			FlagWithList("--boot-image=", baseImageLocations, ":").Implicits(image.baseImages.Paths()).
 			// Similarly, the dex2oat tool will automatically find the paths to other files in the base
 			// boot image so make sure to add them as implicit dependencies to ensure that they are built
 			// before this command is run.
-			Implicits(image.primaryImagesDeps)
+			Implicits(image.baseImagesDeps)
 	} else {
 		// It is a primary image, so it needs a base address.
 		cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
 	}
 
-	// We always expect a preloaded classes file to be available. However, if we cannot find it, it's
-	// OK to not pass the flag to dex2oat.
-	preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
-	if preloadedClassesPath.Valid() {
-		cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
+	if len(image.preloadedClassesFile) > 0 {
+		// We always expect a preloaded classes file to be available. However, if we cannot find it, it's
+		// OK to not pass the flag to dex2oat.
+		preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
+		if preloadedClassesPath.Valid() {
+			cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
+		}
 	}
 
 	cmd.
@@ -745,6 +768,16 @@
 		Flag("--force-determinism").
 		Flag("--abort-on-hard-verifier-error")
 
+	// If the image is profile-guided but the profile is disabled, we omit "--compiler-filter" to
+	// leave the decision to dex2oat to pick the compiler filter.
+	if !(image.isProfileGuided() && global.DisableGenerateProfile) {
+		cmd.FlagWithArg("--compiler-filter=", image.compilerFilter)
+	}
+
+	if image.singleImage {
+		cmd.Flag("--single-image")
+	}
+
 	// Use the default variant/features for host builds.
 	// The map below contains only device CPU info (which might be x86 on some devices).
 	if image.target.Os == android.Android {
@@ -828,6 +861,10 @@
 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
+	}
+
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
@@ -1007,6 +1044,8 @@
 			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, " "))
 	}
 }
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 4d0bd09..8c62c33 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -44,6 +44,8 @@
 	bootImageConfigRawKey  = android.NewOnceKey("bootImageConfigRaw")
 	artBootImageName       = "art"
 	frameworkBootImageName = "boot"
+	mainlineBootImageName  = "mainline"
+	bootImageStem          = "boot"
 )
 
 func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
@@ -52,35 +54,52 @@
 
 		artModules := global.ArtApexJars
 		frameworkModules := global.BootJars.RemoveList(artModules)
+		mainlineBcpModules := global.ApexBootJars
+		frameworkSubdir := "system/framework"
 
 		// ART config for the primary boot image in the ART apex.
 		// It includes the Core Libraries.
 		artCfg := bootImageConfig{
 			name:                     artBootImageName,
-			stem:                     "boot",
+			stem:                     bootImageStem,
 			installDirOnHost:         "apex/art_boot_images/javalib",
-			installDirOnDevice:       "system/framework",
+			installDirOnDevice:       frameworkSubdir,
 			profileInstallPathInApex: "etc/boot-image.prof",
 			modules:                  artModules,
 			preloadedClassesFile:     "art/build/boot/preloaded-classes",
+			compilerFilter:           "speed-profile",
+			singleImage:              false,
 		}
 
 		// Framework config for the boot image extension.
 		// It includes framework libraries and depends on the ART config.
-		frameworkSubdir := "system/framework"
 		frameworkCfg := bootImageConfig{
 			extends:              &artCfg,
 			name:                 frameworkBootImageName,
-			stem:                 "boot",
+			stem:                 bootImageStem,
 			installDirOnHost:     frameworkSubdir,
 			installDirOnDevice:   frameworkSubdir,
 			modules:              frameworkModules,
 			preloadedClassesFile: "frameworks/base/config/preloaded-classes",
+			compilerFilter:       "speed-profile",
+			singleImage:          false,
+		}
+
+		mainlineCfg := bootImageConfig{
+			extends:            &frameworkCfg,
+			name:               mainlineBootImageName,
+			stem:               bootImageStem,
+			installDirOnHost:   frameworkSubdir,
+			installDirOnDevice: frameworkSubdir,
+			modules:            mainlineBcpModules,
+			compilerFilter:     "verify",
+			singleImage:        true,
 		}
 
 		return map[string]*bootImageConfig{
 			artBootImageName:       &artCfg,
 			frameworkBootImageName: &frameworkCfg,
+			mainlineBootImageName:  &mainlineCfg,
 		}
 	}).(map[string]*bootImageConfig)
 }
@@ -92,10 +111,7 @@
 		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
 		configs := genBootImageConfigRaw(ctx)
-		artCfg := configs[artBootImageName]
-		frameworkCfg := configs[frameworkBootImageName]
 
-		// common to all configs
 		for _, c := range configs {
 			c.dir = deviceDir.Join(ctx, "dex_"+c.name+"jars")
 			c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped")
@@ -131,18 +147,42 @@
 			c.zip = c.dir.Join(ctx, c.name+".zip")
 		}
 
-		// specific to the framework config
-		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
-		for i := range targets {
-			frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
-			frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths()
-			frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
+		visited := make(map[string]bool)
+		for _, c := range configs {
+			calculateDepsRecursive(c, targets, visited)
 		}
 
 		return configs
 	}).(map[string]*bootImageConfig)
 }
 
+// calculateDepsRecursive calculates the dependencies of the given boot image config and all its
+// ancestors, if they are not visited.
+// The boot images are supposed to form a tree, where the root is the primary boot image. We do not
+// expect loops (e.g., A extends B, B extends C, C extends A), and we let them crash soong with a
+// stack overflow.
+// Note that a boot image config only has a pointer to the parent, not to children. Therefore, we
+// first go up through the parent chain, and then go back down to visit every code along the path.
+// `visited` is a map where a key is a boot image name and the value indicates whether the boot
+// image config is visited. The boot image names are guaranteed to be unique because they come from
+// `genBootImageConfigRaw` above, which also returns a map and would fail in the first place if the
+// names were not unique.
+func calculateDepsRecursive(c *bootImageConfig, targets []android.Target, visited map[string]bool) {
+	if c.extends == nil || visited[c.name] {
+		return
+	}
+	if c.extends.extends != nil {
+		calculateDepsRecursive(c.extends, targets, visited)
+	}
+	visited[c.name] = true
+	c.dexPathsDeps = android.Concat(c.extends.dexPathsDeps, c.dexPathsDeps)
+	for i := range targets {
+		c.variants[i].baseImages = android.Concat(c.extends.variants[i].baseImages, android.OutputPaths{c.extends.variants[i].imagePathOnHost})
+		c.variants[i].baseImagesDeps = android.Concat(c.extends.variants[i].baseImagesDeps, c.extends.variants[i].imagesDeps.Paths())
+		c.variants[i].dexLocationsDeps = android.Concat(c.extends.variants[i].dexLocationsDeps, c.variants[i].dexLocationsDeps)
+	}
+}
+
 func artBootImageConfig(ctx android.PathContext) *bootImageConfig {
 	return genBootImageConfigs(ctx)[artBootImageName]
 }
@@ -151,6 +191,10 @@
 	return genBootImageConfigs(ctx)[frameworkBootImageName]
 }
 
+func mainlineBootImageConfig(ctx android.PathContext) *bootImageConfig {
+	return genBootImageConfigs(ctx)[mainlineBootImageName]
+}
+
 // 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.
diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go
index b704d09..cd7f295 100644
--- a/java/dexpreopt_config_test.go
+++ b/java/dexpreopt_config_test.go
@@ -28,8 +28,10 @@
 
 	result := android.GroupFixturePreparers(
 		PrepareForBootImageConfigTest,
+		PrepareApexBootJarConfigs,
 	).RunTest(t)
 
 	CheckArtBootImageConfig(t, result)
 	CheckFrameworkBootImageConfig(t, result)
+	CheckMainlineBootImageConfig(t, result)
 }
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 1c236d8..86dd329 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -39,6 +39,78 @@
 	FixtureConfigureBootJars("com.android.art:core1", "com.android.art:core2", "platform:framework"),
 )
 
+var PrepareApexBootJarConfigs = FixtureConfigureApexBootJars(
+	"com.android.foo:framework-foo", "com.android.bar:framework-bar")
+
+var PrepareApexBootJarConfigsAndModules = android.GroupFixturePreparers(
+	PrepareApexBootJarConfigs,
+	prepareApexBootJarModule("com.android.foo", "framework-foo"),
+	prepareApexBootJarModule("com.android.bar", "framework-bar"),
+)
+
+var ApexBootJarFragmentsForPlatformBootclasspath = fmt.Sprintf(`
+	{
+		apex: "%[1]s",
+		module: "%[1]s-bootclasspathfragment",
+	},
+	{
+		apex: "%[2]s",
+		module: "%[2]s-bootclasspathfragment",
+	},
+`, "com.android.foo", "com.android.bar")
+
+var ApexBootJarDexJarPaths = []string{
+	"out/soong/.intermediates/packages/modules/com.android.bar/framework-bar/android_common_apex10000/aligned/framework-bar.jar",
+	"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 {
+	moduleSourceDir := fmt.Sprintf("packages/modules/%s", apexName)
+	return android.GroupFixturePreparers(
+		android.FixtureAddTextFile(moduleSourceDir+"/Android.bp", fmt.Sprintf(`
+			apex {
+				name: "%[1]s",
+				key: "%[1]s.key",
+				bootclasspath_fragments: [
+					"%[1]s-bootclasspathfragment",
+				],
+				updatable: false,
+			}
+
+			apex_key {
+				name: "%[1]s.key",
+				public_key: "%[1]s.avbpubkey",
+				private_key: "%[1]s.pem",
+			}
+
+			bootclasspath_fragment {
+				name: "%[1]s-bootclasspathfragment",
+				contents: ["%[2]s"],
+				apex_available: ["%[1]s"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
+			}
+
+			java_library {
+				name: "%[2]s",
+				srcs: ["%[2]s.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+				apex_available: ["%[1]s"],
+			}
+		`, apexName, moduleName)),
+		android.FixtureMergeMockFs(android.MockFS{
+			fmt.Sprintf("%s/apex_manifest.json", moduleSourceDir):          nil,
+			fmt.Sprintf("%s/%s.avbpubkey", moduleSourceDir, apexName):      nil,
+			fmt.Sprintf("%s/%s.pem", moduleSourceDir, apexName):            nil,
+			fmt.Sprintf("system/sepolicy/apex/%s-file_contexts", apexName): nil,
+			fmt.Sprintf("%s/%s.java", moduleSourceDir, moduleName):         nil,
+		}),
+	)
+}
+
 // normalizedInstall represents a android.RuleBuilderInstall that has been normalized to remove
 // test specific parts of the From path.
 type normalizedInstall struct {
@@ -100,8 +172,8 @@
 	imagePathOnHost   string
 	imagePathOnDevice string
 	imagesDeps        []string
-	primaryImages     string
-	primaryImagesDeps []string
+	baseImages        []string
+	baseImagesDeps    []string
 
 	// Mutated fields
 	installs            []normalizedInstall
@@ -413,8 +485,8 @@
 					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
 					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
 				},
-				primaryImages: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-				primaryImagesDeps: []string{
+				baseImages: []string{"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art"},
+				baseImagesDeps: []string{
 					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
 					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
 					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
@@ -461,8 +533,8 @@
 					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
 					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
 				},
-				primaryImages: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
-				primaryImagesDeps: []string{
+				baseImages: []string{"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art"},
+				baseImagesDeps: []string{
 					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
 					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
 					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
@@ -509,8 +581,8 @@
 					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
 					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
 				},
-				primaryImages: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
-				primaryImagesDeps: []string{
+				baseImages: []string{"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art"},
+				baseImagesDeps: []string{
 					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
 					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
 					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
@@ -557,8 +629,8 @@
 					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
 					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
 				},
-				primaryImages: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
-				primaryImagesDeps: []string{
+				baseImages: []string{"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art"},
+				baseImagesDeps: []string{
 					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
 					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
 					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
@@ -601,6 +673,290 @@
 	checkBootImageConfig(t, imageConfig, mutated, expected)
 }
 
+// getMainlineImageConfig gets the framework bootImageConfig that was created during the test.
+func getMainlineImageConfig(result *android.TestResult) *bootImageConfig {
+	pathCtx := &android.TestPathContext{TestResult: result}
+	imageConfig := mainlineBootImageConfig(pathCtx)
+	return imageConfig
+}
+
+// CheckMainlineBootImageConfig checks the status of the fields of the bootImageConfig and
+// bootImageVariant structures that are returned from mainlineBootImageConfig.
+//
+// This is before any fields are mutated.
+func CheckMainlineBootImageConfig(t *testing.T, result *android.TestResult) {
+	expectedLicenseMetadataFile := ""
+	imageConfig := getMainlineImageConfig(result)
+
+	expected := &expectedConfig{
+		name:                     "mainline",
+		stem:                     "boot",
+		dir:                      "out/soong/test_device/dex_mainlinejars",
+		symbolsDir:               "out/soong/test_device/dex_mainlinejars_unstripped",
+		installDirOnDevice:       "system/framework",
+		installDirOnHost:         "system/framework",
+		profileInstallPathInApex: "",
+		modules: android.CreateTestConfiguredJarList([]string{
+			"com.android.foo:framework-foo",
+			"com.android.bar:framework-bar",
+		}),
+		dexPaths: []string{
+			"out/soong/test_device/dex_mainlinejars_input/framework-foo.jar",
+			"out/soong/test_device/dex_mainlinejars_input/framework-bar.jar",
+		},
+		dexPathsDeps: []string{
+			"out/soong/test_device/dex_artjars_input/core1.jar",
+			"out/soong/test_device/dex_artjars_input/core2.jar",
+			"out/soong/test_device/dex_bootjars_input/framework.jar",
+			"out/soong/test_device/dex_mainlinejars_input/framework-foo.jar",
+			"out/soong/test_device/dex_mainlinejars_input/framework-bar.jar",
+		},
+		zip: "out/soong/test_device/dex_mainlinejars/mainline.zip",
+		variants: []*expectedVariant{
+			{
+				archType: android.Arm64,
+				dexLocations: []string{
+					"/apex/com.android.foo/javalib/framework-foo.jar",
+					"/apex/com.android.bar/javalib/framework-bar.jar",
+				},
+				dexLocationsDeps: []string{
+					"/apex/com.android.art/javalib/core1.jar",
+					"/apex/com.android.art/javalib/core2.jar",
+					"/system/framework/framework.jar",
+					"/apex/com.android.foo/javalib/framework-foo.jar",
+					"/apex/com.android.bar/javalib/framework-bar.jar",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+				imagePathOnDevice: "/system/framework/arm64/boot-framework-foo.art",
+				imagesDeps: []string{
+					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
+					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
+				},
+				baseImages: []string{
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+				},
+				baseImagesDeps: []string{
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+						to:   "/system/framework/arm64/boot-framework-foo.art",
+					},
+					{
+						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
+						to:   "/system/framework/arm64/boot-framework-foo.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
+						to:   "/system/framework/arm64/boot-framework-foo.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat",
+						to:   "/system/framework/arm64/boot-framework-foo.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType: android.Arm,
+				dexLocations: []string{
+					"/apex/com.android.foo/javalib/framework-foo.jar",
+					"/apex/com.android.bar/javalib/framework-bar.jar",
+				},
+				dexLocationsDeps: []string{
+					"/apex/com.android.art/javalib/core1.jar",
+					"/apex/com.android.art/javalib/core2.jar",
+					"/system/framework/framework.jar",
+					"/apex/com.android.foo/javalib/framework-foo.jar",
+					"/apex/com.android.bar/javalib/framework-bar.jar",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+				imagePathOnDevice: "/system/framework/arm/boot-framework-foo.art",
+				imagesDeps: []string{
+					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
+					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
+				},
+				baseImages: []string{
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+				},
+				baseImagesDeps: []string{
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+						to:   "/system/framework/arm/boot-framework-foo.art",
+					},
+					{
+						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
+						to:   "/system/framework/arm/boot-framework-foo.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
+						to:   "/system/framework/arm/boot-framework-foo.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat",
+						to:   "/system/framework/arm/boot-framework-foo.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType: android.X86_64,
+				dexLocations: []string{
+					"host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+					"host/linux-x86/apex/com.android.bar/javalib/framework-bar.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/framework.jar",
+					"host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+					"host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+				imagePathOnDevice: "/system/framework/x86_64/boot-framework-foo.art",
+				imagesDeps: []string{
+					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
+				},
+				baseImages: []string{
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+				},
+				baseImagesDeps: []string{
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+						to:   "/system/framework/x86_64/boot-framework-foo.art",
+					},
+					{
+						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+						to:   "/system/framework/x86_64/boot-framework-foo.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
+						to:   "/system/framework/x86_64/boot-framework-foo.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+						to:   "/system/framework/x86_64/boot-framework-foo.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType: android.X86,
+				dexLocations: []string{
+					"host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+					"host/linux-x86/apex/com.android.bar/javalib/framework-bar.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/framework.jar",
+					"host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
+					"host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+				imagePathOnDevice: "/system/framework/x86/boot-framework-foo.art",
+				imagesDeps: []string{
+					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
+				},
+				baseImages: []string{
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+				},
+				baseImagesDeps: []string{
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+						to:   "/system/framework/x86/boot-framework-foo.art",
+					},
+					{
+						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+						to:   "/system/framework/x86/boot-framework-foo.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
+						to:   "/system/framework/x86/boot-framework-foo.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+						to:   "/system/framework/x86/boot-framework-foo.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+		},
+		profileInstalls:            []normalizedInstall{},
+		profileLicenseMetadataFile: expectedLicenseMetadataFile,
+	}
+
+	checkBootImageConfig(t, imageConfig, false, expected)
+}
+
 // clearMutatedFields clears fields in the expectedConfig that correspond to fields in the
 // bootImageConfig/bootImageVariant structs which are mutated outside the call to
 // genBootImageConfigs.
@@ -664,8 +1020,8 @@
 			android.AssertPathRelativeToTopEquals(t, "imagePathOnHost", expectedVariant.imagePathOnHost, variant.imagePathOnHost)
 			android.AssertStringEquals(t, "imagePathOnDevice", expectedVariant.imagePathOnDevice, variant.imagePathOnDevice)
 			android.AssertPathsRelativeToTopEquals(t, "imagesDeps", expectedVariant.imagesDeps, variant.imagesDeps.Paths())
-			android.AssertPathRelativeToTopEquals(t, "primaryImages", expectedVariant.primaryImages, variant.primaryImages)
-			android.AssertPathsRelativeToTopEquals(t, "primaryImagesDeps", expectedVariant.primaryImagesDeps, variant.primaryImagesDeps)
+			android.AssertPathsRelativeToTopEquals(t, "baseImages", expectedVariant.baseImages, variant.baseImages.Paths())
+			android.AssertPathsRelativeToTopEquals(t, "baseImagesDeps", expectedVariant.baseImagesDeps, variant.baseImagesDeps)
 			assertInstallsEqual(t, "installs", expectedVariant.installs, variant.installs)
 			assertInstallsEqual(t, "vdexInstalls", expectedVariant.vdexInstalls, variant.vdexInstalls)
 			assertInstallsEqual(t, "unstrippedInstalls", expectedVariant.unstrippedInstalls, variant.unstrippedInstalls)
@@ -712,6 +1068,10 @@
 DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
 DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art:/system/framework/x86_64/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art:/system/framework/arm/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art:/system/framework/arm64/boot-framework-foo.art out/soong/test_device/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/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art:/system/framework/x86/boot-framework-foo.art out/soong/test_device/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/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art:/system/framework/x86_64/boot-framework-foo.art out/soong/test_device/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/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex
 DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex
 DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex
@@ -720,6 +1080,10 @@
 DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex
 DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex
 DEXPREOPT_IMAGE_DEPS_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm=%[1]s
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm64=%[1]s
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86=%[1]s
@@ -728,11 +1092,17 @@
 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_LOCATIONS_ON_DEVICEart=/system/framework/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art:/system/framework/boot-framework.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEmainline=/system/framework/boot.art:/system/framework/boot-framework.art:/system/framework/boot-framework-foo.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art:out/soong/test_device/dex_bootjars/android/system/framework/boot-framework.art
-DEXPREOPT_IMAGE_NAMES=art boot
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art:out/soong/test_device/dex_bootjars/android/system/framework/boot-framework.art:out/soong/test_device/dex_mainlinejars/android/system/framework/boot-framework-foo.art
+DEXPREOPT_IMAGE_NAMES=art boot mainline
 DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/test_device/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/test_device/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/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
@@ -743,6 +1113,10 @@
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/test_device/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/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm=out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm64=out/soong/test_device/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/test_device/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/test_device/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/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/test_device/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/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/test_device/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/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex
@@ -751,8 +1125,13 @@
 DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/test_device/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/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex
 DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex:/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex:/system/framework/arm/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex:/system/framework/arm64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex:/system/framework/x86/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex:/system/framework/x86_64/boot-framework-foo.vdex
 DEXPREOPT_IMAGE_ZIP_art=out/soong/test_device/dex_artjars/art.zip
 DEXPREOPT_IMAGE_ZIP_boot=out/soong/test_device/dex_bootjars/boot.zip
+DEXPREOPT_IMAGE_ZIP_mainline=out/soong/test_device/dex_mainlinejars/mainline.zip
 DEXPREOPT_IMAGE_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
 DEXPREOPT_IMAGE_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
 DEXPREOPT_IMAGE_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art
@@ -761,6 +1140,10 @@
 DEXPREOPT_IMAGE_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art
 DEXPREOPT_IMAGE_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art
 DEXPREOPT_IMAGE_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art
+DEXPREOPT_IMAGE_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art
 `
 	expected := strings.TrimSpace(fmt.Sprintf(format, expectedLicenseMetadataFile))
 	actual := strings.TrimSpace(out.String())
diff --git a/java/droiddoc.go b/java/droiddoc.go
index aa55f37..c5a957e 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -248,16 +248,16 @@
 	return proptools.String(j.properties.System_modules)
 }
 
-func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return j.SdkVersion(ctx)
+func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return j.SdkVersion(ctx).ApiLevel
 }
 
 func (j *Javadoc) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
 	return j.SdkVersion(ctx)
 }
 
-func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return j.SdkVersion(ctx)
+func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return j.SdkVersion(ctx).ApiLevel
 }
 
 func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
@@ -304,7 +304,7 @@
 		flags = append(flags, "-I"+src.String())
 	}
 
-	minSdkVersion := j.MinSdkVersion(ctx).ApiLevel.FinalOrFutureInt()
+	minSdkVersion := j.MinSdkVersion(ctx).FinalOrFutureInt()
 	flags = append(flags, fmt.Sprintf("--min_sdk_version=%v", minSdkVersion))
 
 	return strings.Join(flags, " "), deps
@@ -602,8 +602,13 @@
 		Flag("-J-Xmx1600m").
 		Flag("-J-XX:-OmitStackTraceInFastThrow").
 		Flag("-XDignore.symbol.file").
+		Flag("--ignore-source-errors").
 		FlagWithArg("-doclet ", "com.google.doclava.Doclava").
 		FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+		FlagWithArg("-Xmaxerrs ", "10").
+		FlagWithArg("-Xmaxwarns ", "10").
+		Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
 		FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
 		FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
 
@@ -687,7 +692,7 @@
 	outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
 
 	cmd := rule.Command().
-		BuiltTool("soong_javac_wrapper").Tool(android.PathForSource(ctx, "prebuilts/jdk/jdk11/linux-x86/bin/javadoc")).
+		BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
 		Flag(config.JavacVmFlags).
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 4bbe70a..8a521aa 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -148,6 +148,10 @@
 	// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
 	// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
 	Extensions_info_file *string `android:"path"`
+
+	// API surface of this module. If set, the module contributes to an API surface.
+	// For the full list of available API surfaces, refer to soong/android/sdk_version.go
+	Api_surface *string
 }
 
 // Used by xsd_config
@@ -178,6 +182,10 @@
 		&module.Javadoc.properties)
 
 	InitDroiddocModule(module, android.HostAndDeviceSupported)
+
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+		module.createApiContribution(ctx)
+	})
 	return module
 }
 
@@ -862,6 +870,25 @@
 	}, attrs)
 }
 
+func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
+	api_file := d.properties.Check_api.Current.Api_file
+	api_surface := d.properties.Api_surface
+
+	props := struct {
+		Name        *string
+		Api_surface *string
+		Api_file    *string
+		Visibility  []string
+	}{}
+
+	props.Name = proptools.StringPtr(d.Name() + ".api.contribution")
+	props.Api_surface = api_surface
+	props.Api_file = api_file
+	props.Visibility = []string{"//visibility:override", "//visibility:public"}
+
+	ctx.CreateModule(ApiContributionFactory, &props)
+}
+
 // TODO (b/262014796): Export the API contributions of CorePlatformApi
 // A map to populate the api surface of a droidstub from a substring appearing in its name
 // This map assumes that droidstubs (either checked-in or created by java_sdk_library)
@@ -876,6 +903,7 @@
 		"system":        android.SdkSystem,
 		"module_lib":    android.SdkModule,
 		"module-lib":    android.SdkModule,
+		"platform.api":  android.SdkCorePlatform,
 		"test":          android.SdkTest,
 		"toolchain":     android.SdkToolchain,
 	}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index ef2e6dc..7a04d73 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -346,3 +346,60 @@
 		android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
 	}
 }
+
+func TestDroidStubsApiContributionGeneration(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droidstubs {
+			name: "foo",
+			srcs: ["A/a.java"],
+			api_surface: "public",
+			check_api: {
+				current: {
+					api_file: "A/current.txt",
+					removed_api_file: "A/removed.txt",
+				}
+			}
+		}
+		`,
+		map[string][]byte{
+			"A/a.java":      nil,
+			"A/current.txt": nil,
+			"A/removed.txt": nil,
+		},
+	)
+
+	ctx.ModuleForTests("foo.api.contribution", "")
+}
+
+func TestGeneratedApiContributionVisibilityTest(t *testing.T) {
+	library_bp := `
+		java_api_library {
+			name: "bar",
+			api_surface: "public",
+			api_contributions: ["foo.api.contribution"],
+		}
+	`
+	ctx, _ := testJavaWithFS(t, `
+			droidstubs {
+				name: "foo",
+				srcs: ["A/a.java"],
+				api_surface: "public",
+				check_api: {
+					current: {
+						api_file: "A/current.txt",
+						removed_api_file: "A/removed.txt",
+					}
+				},
+				visibility: ["//a"],
+			}
+		`,
+		map[string][]byte{
+			"a/a.java":      nil,
+			"a/current.txt": nil,
+			"a/removed.txt": nil,
+			"b/Android.bp":  []byte(library_bp),
+		},
+	)
+
+	ctx.ModuleForTests("bar", "android_common")
+}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index cf9c7ad..d25096b 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -20,10 +20,17 @@
 	"android/soong/android"
 )
 
-var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
-	Command:     "${config.Class2NonSdkList} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
-	CommandDeps: []string{"${config.Class2NonSdkList}"},
-}, "outFlag", "stubAPIFlags")
+var (
+	hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
+		Command:     "${config.Class2NonSdkList} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
+		CommandDeps: []string{"${config.Class2NonSdkList}"},
+	}, "outFlag", "stubAPIFlags")
+
+	hiddenAPIGenerateIndexRule = pctx.AndroidStaticRule("hiddenAPIGenerateIndex", blueprint.RuleParams{
+		Command:     "${config.MergeCsvCommand} --zip_input --key_field signature --output=$out $in",
+		CommandDeps: []string{"${config.MergeCsvCommand}"},
+	})
+)
 
 type hiddenAPI struct {
 	// True if the module containing this structure contributes to the hiddenapi information or has
@@ -66,7 +73,7 @@
 	android.Module
 	hiddenAPIIntf
 
-	MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+	MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
 }
 
 type hiddenAPIIntf interface {
@@ -150,7 +157,7 @@
 	// Create a copy of the dex jar which has been encoded with hiddenapi flags.
 	flagsCSV := hiddenAPISingletonPaths(ctx).flags
 	outputDir := android.PathForModuleOut(ctx, "hiddenapi").OutputPath
-	encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, android.SdkSpecNone, outputDir)
+	encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, android.NoneApiLevel, outputDir)
 
 	// Use the encoded dex jar from here onwards.
 	return encodedDex
@@ -216,14 +223,12 @@
 // created by the unsupported app usage annotation processor during compilation of the class
 // implementation jar.
 func buildRuleToGenerateIndex(ctx android.ModuleContext, desc string, classesJars android.Paths, indexCSV android.WritablePath) {
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		BuiltTool("merge_csv").
-		Flag("--zip_input").
-		Flag("--key_field signature").
-		FlagWithOutput("--output=", indexCSV).
-		Inputs(classesJars)
-	rule.Build(desc, desc)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        hiddenAPIGenerateIndexRule,
+		Description: desc,
+		Inputs:      classesJars,
+		Output:      indexCSV,
+	})
 }
 
 var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
@@ -248,7 +253,7 @@
 // The encode dex rule requires unzipping, encoding and rezipping the classes.dex files along with
 // all the resources from the input jar. It also ensures that if it was uncompressed in the input
 // it stays uncompressed in the output.
-func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, minSdkVersion android.SdkSpec, outputDir android.OutputPath) android.OutputPath {
+func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, minSdkVersion android.ApiLevel, outputDir android.OutputPath) android.OutputPath {
 
 	// The output file has the same name as the input file and is in the output directory.
 	output := outputDir.Join(ctx, dexInput.Base())
@@ -278,7 +283,7 @@
 
 	// If the library is targeted for Q and/or R then make sure that they do not
 	// have any S+ flags encoded as that will break the runtime.
-	minApiLevel := minSdkVersion.ApiLevel
+	minApiLevel := minSdkVersion
 	if !minApiLevel.IsNone() {
 		if minApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, "R")) {
 			hiddenapiFlags = hiddenapiFlags + " --max-hiddenapi-level=max-target-r"
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 593d772..c6176e6 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -236,9 +236,9 @@
 		testStubModules = append(testStubModules, "sdk_test_current_android")
 	} else {
 		// Use stub modules built from source
-		publicStubModules = append(publicStubModules, "android_stubs_current")
-		systemStubModules = append(systemStubModules, "android_system_stubs_current")
-		testStubModules = append(testStubModules, "android_test_stubs_current")
+		publicStubModules = append(publicStubModules, android.SdkPublic.JavaLibraryName(config))
+		systemStubModules = append(systemStubModules, android.SdkSystem.JavaLibraryName(config))
+		testStubModules = append(testStubModules, android.SdkTest.JavaLibraryName(config))
 	}
 	// We do not have prebuilts of the core platform api yet
 	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
@@ -697,7 +697,7 @@
 // The relative width of APIs is determined by their order in hiddenAPIScopes.
 func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths {
 	stubDexJars := android.Paths{}
-	modules := android.SortedStringKeys(s)
+	modules := android.SortedKeys(s)
 	for _, module := range modules {
 		stubDexJarsByScope := s[module]
 
@@ -714,7 +714,7 @@
 // the returned list.
 func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths {
 	stubDexJars := android.Paths{}
-	modules := android.SortedStringKeys(s)
+	modules := android.SortedKeys(s)
 	for _, module := range modules {
 		stubDexJarsByScope := s[module]
 		// Not every module will have the same set of
@@ -917,7 +917,7 @@
 // bootDexJars returns the boot dex jar paths sorted by their keys.
 func (b bootDexJarByModule) bootDexJars() android.Paths {
 	paths := android.Paths{}
-	for _, k := range android.SortedStringKeys(b) {
+	for _, k := range android.SortedKeys(b) {
 		paths = append(paths, b[k])
 	}
 	return paths
@@ -927,7 +927,7 @@
 // libraries if present.
 func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
 	paths := android.Paths{}
-	for _, k := range android.SortedStringKeys(b) {
+	for _, k := range android.SortedKeys(b) {
 		if k == "jacocoagent" {
 			continue
 		}
@@ -1217,7 +1217,7 @@
 	// Encode the flags into the boot dex files.
 	encodedBootDexJarsByModule := bootDexJarByModule{}
 	outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
-	for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
+	for _, name := range android.SortedKeys(bootDexInfoByModule) {
 		bootDexInfo := bootDexInfoByModule[name]
 		unencodedDex := bootDexInfo.path
 		encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, bootDexInfo.minSdkVersion, outputDir)
@@ -1278,7 +1278,7 @@
 	uncompressDex bool
 
 	// The minimum sdk version that the dex jar will be used on.
-	minSdkVersion android.SdkSpec
+	minSdkVersion android.ApiLevel
 }
 
 // bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
@@ -1288,7 +1288,7 @@
 // bootDexJars returns the boot dex jar paths sorted by their keys.
 func (b bootDexInfoByModule) bootDexJars() android.Paths {
 	paths := android.Paths{}
-	for _, m := range android.SortedStringKeys(b) {
+	for _, m := range android.SortedKeys(b) {
 		paths = append(paths, b[m].path)
 	}
 	return paths
diff --git a/java/java.go b/java/java.go
index 3b0ad8d..3707815 100644
--- a/java/java.go
+++ b/java/java.go
@@ -230,6 +230,12 @@
 	// against this module.  If empty, ImplementationJars should be used instead.
 	HeaderJars android.Paths
 
+	// set of header jars for all transitive libs deps
+	TransitiveLibsHeaderJars *android.DepSet
+
+	// set of header jars for all transitive static libs deps
+	TransitiveStaticLibsHeaderJars *android.DepSet
+
 	// ImplementationAndResourceJars is a list of jars that contain the implementations of classes
 	// in the module as well as any resources included in the module.
 	ImplementationAndResourcesJars android.Paths
@@ -380,6 +386,7 @@
 	instrumentationForTag   = dependencyTag{name: "instrumentation_for"}
 	extraLintCheckTag       = dependencyTag{name: "extra-lint-check", toolchain: true}
 	jniLibTag               = dependencyTag{name: "jnilib", runtimeLinked: true}
+	r8LibraryJarTag         = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	jniInstallTag           = installDependencyTag{name: "jni install"}
 	binaryInstallTag        = installDependencyTag{name: "binary install"}
@@ -510,14 +517,8 @@
 		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() {
 		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
-	} else if ctx.Config().TargetsJava17() {
-		// Temporary experimental flag to be able to try and build with
-		// java version 17 options.  The flag, if used, just sets Java
-		// 17 as the default version, leaving any components that
-		// target an older version intact.
-		return JAVA_VERSION_17
 	} else {
-		return JAVA_VERSION_11
+		return JAVA_VERSION_17
 	}
 }
 
@@ -794,6 +795,8 @@
 
 	// The value of the min_sdk_version property, translated into a number where possible.
 	MinSdkVersion *string `supported_build_releases:"Tiramisu+"`
+
+	DexPreoptProfileGuided *bool `supported_build_releases:"UpsideDownCake+"`
 }
 
 func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -808,9 +811,13 @@
 	// If the min_sdk_version was set then add the canonical representation of the API level to the
 	// snapshot.
 	if j.deviceProperties.Min_sdk_version != nil {
-		canonical := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.ApiLevel.String())
+		canonical := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String())
 		p.MinSdkVersion = proptools.StringPtr(canonical)
 	}
+
+	if j.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+		p.DexPreoptProfileGuided = proptools.BoolPtr(true)
+	}
 }
 
 func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -837,6 +844,11 @@
 		propertySet.AddProperty("permitted_packages", p.PermittedPackages)
 	}
 
+	dexPreoptSet := propertySet.AddPropertySet("dex_preopt")
+	if p.DexPreoptProfileGuided != nil {
+		dexPreoptSet.AddProperty("profile_guided", proptools.Bool(p.DexPreoptProfileGuided))
+	}
+
 	// Do not copy anything else to the snapshot.
 	if memberType.onlyCopyJarToSnapshot {
 		return
@@ -1587,7 +1599,11 @@
 var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
 
 func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file))
+	var apiFile android.Path = nil
+	if apiFileString := ap.properties.Api_file; apiFileString != nil {
+		apiFile = android.PathForModuleSrc(ctx, String(apiFileString))
+	}
+
 	ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
 		ApiFile: apiFile,
 	})
@@ -1618,13 +1634,21 @@
 
 	// List of flags to be passed to the javac compiler to generate jar file
 	Javacflags []string
+
+	// List of shared java libs that this module has dependencies to and
+	// should be passed as classpath in javac invocation
+	Libs []string
+
+	// List of java libs that this module has static dependencies to and will be
+	// passed in metalava invocation
+	Static_libs []string
 }
 
 func ApiLibraryFactory() android.Module {
 	module := &ApiLibrary{}
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	android.InitDefaultableModule(module)
 	module.AddProperties(&module.properties)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -1689,6 +1713,8 @@
 	for _, apiContributionName := range apiContributions {
 		ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
 	}
+	ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...)
 }
 
 func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1706,10 +1732,26 @@
 
 	homeDir := android.PathForModuleOut(ctx, "metalava", "home")
 
-	var srcFiles []android.Path
-	ctx.VisitDirectDepsWithTag(javaApiContributionTag, func(dep android.Module) {
-		provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
-		srcFiles = append(srcFiles, android.PathForSource(ctx, provider.ApiFile.String()))
+	var srcFiles android.Paths
+	var classPaths android.Paths
+	var staticLibs android.Paths
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		switch tag {
+		case javaApiContributionTag:
+			provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
+			providerApiFile := provider.ApiFile
+			if providerApiFile == nil {
+				ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
+			}
+			srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
+		case libTag:
+			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			classPaths = append(classPaths, provider.HeaderJars...)
+		case staticLibTag:
+			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			staticLibs = append(staticLibs, provider.HeaderJars...)
+		}
 	})
 
 	// Add the api_files inputs
@@ -1733,17 +1775,30 @@
 		FlagWithArg("-D ", stubsDir.String())
 
 	rule.Build("metalava", "metalava merged")
-
-	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), "android.jar")
+	compiledStubs := android.PathForModuleOut(ctx, ctx.ModuleName(), "stubs.jar")
+	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
 
 	var flags javaBuilderFlags
 	flags.javaVersion = getStubsJavaVersion()
 	flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
+	flags.classpath = classpath(classPaths)
 
-	TransformJavaToClasses(ctx, al.stubsJar, 0, android.Paths{},
+	TransformJavaToClasses(ctx, compiledStubs, 0, android.Paths{},
 		android.Paths{al.stubsSrcJar}, flags, android.Paths{})
 
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().
+		BuiltTool("merge_zips").
+		Output(al.stubsJar).
+		Inputs(android.Paths{compiledStubs}).
+		Inputs(staticLibs)
+	builder.Build("merge_zips", "merge jar files")
+
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
+
+	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+		HeaderJars: android.PathsIfNonNil(al.stubsJar),
+	})
 }
 
 //
@@ -1819,7 +1874,7 @@
 	hideApexVariantFromMake bool
 
 	sdkVersion    android.SdkSpec
-	minSdkVersion android.SdkSpec
+	minSdkVersion android.ApiLevel
 }
 
 var _ PermittedPackagesForUpdatableBootJars = (*Import)(nil)
@@ -1836,11 +1891,11 @@
 	return "none"
 }
 
-func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
 	if j.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(ctx, *j.properties.Min_sdk_version)
+		return android.ApiLevelFrom(ctx, *j.properties.Min_sdk_version)
 	}
-	return j.SdkVersion(ctx)
+	return j.SdkVersion(ctx).ApiLevel
 }
 
 func (j *Import) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
@@ -1850,8 +1905,8 @@
 	return android.SdkSpecFrom(ctx, "")
 }
 
-func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return j.SdkVersion(ctx)
+func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return j.SdkVersion(ctx).ApiLevel
 }
 
 func (j *Import) Prebuilt() *android.Prebuilt {
@@ -1926,9 +1981,9 @@
 
 	var flags javaBuilderFlags
 
+	j.collectTransitiveHeaderJars(ctx)
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
-
 		if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
 			dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
 			switch tag {
@@ -1964,7 +2019,8 @@
 			if di == nil {
 				return // An error has been reported by FindDeapexerProviderForModule.
 			}
-			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
+			dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(j.BaseModuleName())
+			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
 				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
 				j.dexJarFile = dexJarFile
 				installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
@@ -1973,6 +2029,11 @@
 				j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath)
 				setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 				j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+
+				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
+					j.dexpreopter.inputProfilePathOnHost = profilePath
+				}
+
 				j.dexpreopt(ctx, dexOutputPath)
 
 				// Initialize the hiddenapi structure.
@@ -2000,7 +2061,15 @@
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+			dexParams := &compileDexParams{
+				flags:         flags,
+				sdkVersion:    j.SdkVersion(ctx),
+				minSdkVersion: j.MinSdkVersion(ctx),
+				classesJar:    outputFile,
+				jarName:       jarName,
+			}
+
+			dexOutputFile = j.dexer.compileDex(ctx, dexParams)
 			if ctx.Failed() {
 				return
 			}
@@ -2018,6 +2087,8 @@
 
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.combinedClasspathFile),
+		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
+		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
 		ImplementationJars:             android.PathsIfNonNil(j.combinedClasspathFile),
 		AidlIncludeDirs:                j.exportAidlIncludeDirs,
@@ -2089,15 +2160,17 @@
 // Implements android.ApexModule
 func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
-	sdkSpec := j.MinSdkVersion(ctx)
-	if !sdkSpec.Specified() {
+	sdkVersionSpec := j.SdkVersion(ctx)
+	minSdkVersion := j.MinSdkVersion(ctx)
+	if !minSdkVersion.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
 	}
-	if sdkSpec.Kind == android.SdkCore {
+	// If the module is compiling against core (via sdk_version), skip comparison check.
+	if sdkVersionSpec.Kind == android.SdkCore {
 		return nil
 	}
-	if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
+	if minSdkVersion.GreaterThan(sdkVersion) {
+		return fmt.Errorf("newer SDK(%v)", minSdkVersion)
 	}
 	return nil
 }
@@ -2105,11 +2178,16 @@
 // requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
 // java_sdk_library_import with the specified base module name requires to be exported from a
 // prebuilt_apex/apex_set.
-func requiredFilesFromPrebuiltApexForImport(name string) []string {
+func requiredFilesFromPrebuiltApexForImport(name string, d *dexpreopter) []string {
+	dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(name)
 	// Add the dex implementation jar to the set of exported files.
-	return []string{
-		apexRootRelativePathToJavaLib(name),
+	files := []string{
+		dexJarFileApexRootRelative,
 	}
+	if BoolDefault(d.importDexpreoptProperties.Dex_preopt.Profile_guided, false) {
+		files = append(files, dexJarFileApexRootRelative+".prof")
+	}
+	return files
 }
 
 // apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
@@ -2122,7 +2200,7 @@
 
 func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []string {
 	name := j.BaseModuleName()
-	return requiredFilesFromPrebuiltApexForImport(name)
+	return requiredFilesFromPrebuiltApexForImport(name, &j.dexpreopter)
 }
 
 // Add compile time check for interface implementation
@@ -2163,6 +2241,7 @@
 	module.AddProperties(
 		&module.properties,
 		&module.dexer.dexProperties,
+		&module.importDexpreoptProperties,
 	)
 
 	module.initModuleAndImport(module)
@@ -2402,6 +2481,7 @@
 		&RuntimeResourceOverlayProperties{},
 		&LintProperties{},
 		&appTestHelperAppProperties{},
+		&JavaApiLibraryProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -2526,6 +2606,7 @@
 
 type javaCommonAttributes struct {
 	*javaResourcesAttributes
+	*kotlinAttributes
 	Srcs      bazel.LabelListAttribute
 	Plugins   bazel.LabelListAttribute
 	Javacopts bazel.StringListAttribute
@@ -2544,10 +2625,12 @@
 
 type aidlLibraryAttributes struct {
 	Srcs bazel.LabelListAttribute
+	Tags bazel.StringListAttribute
 }
 
 type javaAidlLibraryAttributes struct {
 	Deps bazel.LabelListAttribute
+	Tags bazel.StringListAttribute
 }
 
 // bp2BuildJavaInfo has information needed for the conversion of  java*_modules
@@ -2555,8 +2638,8 @@
 // depending on the module type.
 type bp2BuildJavaInfo struct {
 	// separates dependencies into dynamic dependencies and static dependencies.
-	DepLabels     *javaDependencyLabels
-	hasKotlinSrcs bool
+	DepLabels *javaDependencyLabels
+	hasKotlin bool
 }
 
 // convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with
@@ -2566,7 +2649,7 @@
 // to be returned to the calling function.
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
 	var srcs bazel.LabelListAttribute
-	var deps bazel.LabelList
+	var deps bazel.LabelListAttribute
 	var staticDeps bazel.LabelList
 
 	archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
@@ -2578,6 +2661,7 @@
 			}
 		}
 	}
+	srcs.ResolveExcludes()
 
 	javaSrcPartition := "java"
 	protoSrcPartition := "proto"
@@ -2618,6 +2702,8 @@
 			return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName)
 		})
 
+		apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+
 		if !aidlSrcs.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
 			ctx.CreateBazelTargetModule(
@@ -2628,6 +2714,7 @@
 				android.CommonAttributes{Name: aidlLibName},
 				&aidlLibraryAttributes{
 					Srcs: aidlSrcs,
+					Tags: apexAvailableTags,
 				},
 			)
 			aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
@@ -2642,6 +2729,7 @@
 			android.CommonAttributes{Name: javaAidlLibName},
 			&javaAidlLibraryAttributes{
 				Deps: aidlLibs,
+				Tags: apexAvailableTags,
 			},
 		)
 
@@ -2672,18 +2760,17 @@
 		Javacopts: bazel.MakeStringListAttribute(javacopts),
 	}
 
-	if m.properties.Libs != nil {
-
-		// TODO 244210934 ALIX Check if this else statement breaks presubmits get rid of it if it doesn't
-		if strings.HasPrefix(ctx.ModuleType(), "java_binary") || strings.HasPrefix(ctx.ModuleType(), "java_library") || ctx.ModuleType() == "android_library" {
-			for _, d := range m.properties.Libs {
-				neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
-				neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
-				deps.Add(&neverlinkLabel)
+	for axis, configToProps := range archVariantProps {
+		for config, _props := range configToProps {
+			if archProps, ok := _props.(*CommonProperties); ok {
+				var libLabels []bazel.Label
+				for _, d := range archProps.Libs {
+					neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
+					neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
+					libLabels = append(libLabels, neverlinkLabel)
+				}
+				deps.SetSelectValue(axis, config, bazel.MakeLabelList(libLabels))
 			}
-
-		} else {
-			deps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Libs))))
 		}
 	}
 
@@ -2701,12 +2788,21 @@
 	staticDeps.Add(protoDepLabel)
 
 	depLabels := &javaDependencyLabels{}
-	depLabels.Deps = bazel.MakeLabelListAttribute(deps)
+	depLabels.Deps = deps
 	depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
 
+	hasKotlin := !kotlinSrcs.IsEmpty()
+	commonAttrs.kotlinAttributes = &kotlinAttributes{
+		Kotlincflags: &m.properties.Kotlincflags,
+	}
+	if len(m.properties.Common_srcs) != 0 {
+		hasKotlin = true
+		commonAttrs.kotlinAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+	}
+
 	bp2BuildInfo := &bp2BuildJavaInfo{
-		DepLabels:     depLabels,
-		hasKotlinSrcs: !kotlinSrcs.IsEmpty(),
+		DepLabels: depLabels,
+		hasKotlin: hasKotlin,
 	}
 
 	return commonAttrs, bp2BuildInfo
@@ -2714,10 +2810,28 @@
 
 type javaLibraryAttributes struct {
 	*javaCommonAttributes
-	Deps        bazel.LabelListAttribute
-	Exports     bazel.LabelListAttribute
-	Neverlink   bazel.BoolAttribute
-	Common_srcs bazel.LabelListAttribute
+	Deps      bazel.LabelListAttribute
+	Exports   bazel.LabelListAttribute
+	Neverlink bazel.BoolAttribute
+}
+
+type kotlinAttributes struct {
+	Common_srcs  bazel.LabelListAttribute
+	Kotlincflags *[]string
+}
+
+func ktJvmLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
+	return bazel.BazelTargetModuleProperties{
+		Rule_class:        "kt_jvm_library",
+		Bzl_load_location: "//build/bazel/rules/kotlin:rules.bzl",
+	}
+}
+
+func javaLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
+	return bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_library",
+		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+	}
 }
 
 func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
@@ -2732,6 +2846,9 @@
 		if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
 			// TODO(b/220869005) remove forced dependency on current public android.jar
 			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import"))
+		} else if sdkVersion.Kind == android.SdkSystem && sdkVersion.ApiLevel == android.FutureApiLevel {
+			// TODO(b/215230098) remove forced dependency on current public android.jar
+			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:system_current_android_sdk_java_import"))
 		}
 	} else if !deps.IsEmpty() {
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
@@ -2745,18 +2862,10 @@
 	}
 	name := m.Name()
 
-	if !bp2BuildInfo.hasKotlinSrcs && len(m.properties.Common_srcs) == 0 {
-		props = bazel.BazelTargetModuleProperties{
-			Rule_class:        "java_library",
-			Bzl_load_location: "//build/bazel/rules/java:library.bzl",
-		}
+	if !bp2BuildInfo.hasKotlin {
+		props = javaLibraryBazelTargetModuleProperties()
 	} else {
-		attrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
-
-		props = bazel.BazelTargetModuleProperties{
-			Rule_class:        "kt_jvm_library",
-			Bzl_load_location: "@rules_kotlin//kotlin:jvm_library.bzl",
-		}
+		props = ktJvmLibraryBazelTargetModuleProperties()
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
@@ -2807,14 +2916,8 @@
 		mainClass = mainClassInManifest
 	}
 
-	attrs := &javaBinaryHostAttributes{
-		javaCommonAttributes: commonAttrs,
-		Deps:                 deps,
-		Runtime_deps:         runtimeDeps,
-		Main_class:           mainClass,
-	}
-
 	// Attribute jvm_flags
+	var jvmFlags bazel.StringListAttribute
 	if m.binaryProperties.Jni_libs != nil {
 		jniLibPackages := map[string]bool{}
 		for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
@@ -2837,15 +2940,42 @@
 			// See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH
 			jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage)
 		}
-		attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
+		jvmFlags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
 	}
 
 	props := bazel.BazelTargetModuleProperties{
-		Rule_class: "java_binary",
+		Rule_class:        "java_binary",
+		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+	}
+	binAttrs := &javaBinaryHostAttributes{
+		Runtime_deps: runtimeDeps,
+		Main_class:   mainClass,
+		Jvm_flags:    jvmFlags,
 	}
 
+	if commonAttrs.Srcs.IsEmpty() {
+		binAttrs.javaCommonAttributes = commonAttrs
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs)
+		return
+	}
+
+	libName := m.Name() + "_lib"
+	var libProps bazel.BazelTargetModuleProperties
+	if bp2BuildInfo.hasKotlin {
+		libProps = ktJvmLibraryBazelTargetModuleProperties()
+	} else {
+		libProps = javaLibraryBazelTargetModuleProperties()
+	}
+	libAttrs := &javaLibraryAttributes{
+		Deps:                 deps,
+		javaCommonAttributes: commonAttrs,
+	}
+
+	ctx.CreateBazelTargetModule(libProps, android.CommonAttributes{Name: libName}, libAttrs)
+	binAttrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
+
 	// Create the BazelTargetModule.
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs)
 }
 
 type bazelJavaImportAttributes struct {
@@ -2869,7 +2999,10 @@
 	attrs := &bazelJavaImportAttributes{
 		Jars: jars,
 	}
-	props := bazel.BazelTargetModuleProperties{Rule_class: "java_import"}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_import",
+		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(i.Name())
 
@@ -2880,7 +3013,10 @@
 		Neverlink: bazel.BoolAttribute{Value: &neverlink},
 		Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
 	}
-	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{Rule_class: "java_library"}, android.CommonAttributes{Name: name + "-neverlink"}, neverlinkAttrs)
+	ctx.CreateBazelTargetModule(
+		javaLibraryBazelTargetModuleProperties(),
+		android.CommonAttributes{Name: name + "-neverlink"},
+		neverlinkAttrs)
 
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index 085f627..68b749b 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -30,7 +30,6 @@
 	"android/soong/cc"
 	"android/soong/dexpreopt"
 	"android/soong/genrule"
-	"android/soong/python"
 )
 
 // Legacy preparer used for running tests within the java package.
@@ -49,7 +48,6 @@
 	// Include all the default java modules.
 	PrepareForTestWithJavaDefaultModules,
 	PrepareForTestWithOverlayBuildComponents,
-	python.PrepareForTestWithPythonBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
 	}),
@@ -617,6 +615,13 @@
 	android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
 
 	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, fooModule.Module())[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+	entries = android.AndroidMkEntriesForTest(t, ctx, barModule.Module())[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
+	entries = android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests("sdklib", "android_common").Module())[0]
+	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_sdk_library_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func assertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) {
@@ -1440,24 +1445,26 @@
 }
 
 func TestDataNativeBinaries(t *testing.T) {
-	ctx, _ := testJava(t, `
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
 		java_test_host {
 			name: "foo",
 			srcs: ["a.java"],
 			data_native_bins: ["bin"]
 		}
 
-		python_binary_host {
+		cc_binary_host {
 			name: "bin",
-			srcs: ["bin.py"],
+			srcs: ["bin.cpp"],
 		}
-	`)
+	`).TestContext
 
 	buildOS := ctx.Config().BuildOS.String()
 
 	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
-	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
 	actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
 }
@@ -1840,6 +1847,20 @@
 		}`)
 }
 
+func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+	testJavaError(t,
+		"Error: foo has an empty api file.",
+		`java_api_contribution {
+			name: "foo",
+		}
+		java_api_library {
+			name: "bar",
+			api_surface: "public",
+			api_contributions: ["foo"],
+		}
+	`)
+}
+
 func TestJavaApiLibraryAndProviderLink(t *testing.T) {
 	provider_bp_a := `
 	java_api_contribution {
@@ -1894,6 +1915,98 @@
 	}
 }
 
+func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `
+	java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	provider_bp_c := `
+	java_api_contribution {
+		name: "foo3",
+		api_file: "foo3.txt",
+	}
+	`
+	provider_bp_d := `
+	java_api_contribution {
+		name: "foo4",
+		api_file: "foo4.txt",
+	}
+	`
+	ctx, _ := testJavaWithFS(t, `
+		java_defaults {
+			name: "baz1",
+			api_surface: "public",
+			api_contributions: ["foo1", "foo2"],
+		}
+
+		java_defaults {
+			name: "baz2",
+			api_surface: "system",
+			api_contributions: ["foo3"],
+		}
+
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_contributions: ["foo1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "public",
+			defaults:["baz1"],
+		}
+
+		java_api_library {
+			name: "bar3",
+			api_surface: "system",
+			defaults:["baz1", "baz2"],
+			api_contributions: ["foo4"],
+			api_files: ["api1/current.txt", "api2/current.txt"]
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+			"c/Android.bp": []byte(provider_bp_c),
+			"d/Android.bp": []byte(provider_bp_d),
+		})
+
+	testcases := []struct {
+		moduleName         string
+		sourceTextFileDirs []string
+	}{
+		{
+			moduleName:         "bar1",
+			sourceTextFileDirs: []string{"a/foo1.txt"},
+		},
+		{
+			moduleName:         "bar2",
+			sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt"},
+		},
+		{
+			moduleName:         "bar3",
+			sourceTextFileDirs: []string{"c/foo3.txt", "a/foo1.txt", "b/foo2.txt", "d/foo4.txt", "api1/current.txt", "api2/current.txt"},
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		manifest := m.Output("metalava.sbox.textproto")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+		manifestCommand := sboxProto.Commands[0].GetCommand()
+		sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
+		android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
+	}
+}
+
 func TestJavaApiLibraryJarGeneration(t *testing.T) {
 	provider_bp_a := `
 	java_api_contribution {
@@ -1901,7 +2014,8 @@
 		api_file: "foo1.txt",
 	}
 	`
-	provider_bp_b := `java_api_contribution {
+	provider_bp_b := `
+	java_api_contribution {
 		name: "foo2",
 		api_file: "foo2.txt",
 	}
@@ -1930,11 +2044,11 @@
 	}{
 		{
 			moduleName:    "bar1",
-			outputJarName: "bar1/android.jar",
+			outputJarName: "bar1/bar1.jar",
 		},
 		{
 			moduleName:    "bar2",
-			outputJarName: "bar2/android.jar",
+			outputJarName: "bar2/bar2.jar",
 		},
 	}
 	for _, c := range testcases {
@@ -1946,6 +2060,155 @@
 	}
 }
 
+func TestJavaApiLibraryLibsLink(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `
+	java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	lib_bp_a := `
+	java_library {
+		name: "lib1",
+		srcs: ["Lib.java"],
+	}
+	`
+	lib_bp_b := `
+	java_library {
+		name: "lib2",
+		srcs: ["Lib.java"],
+	}
+	`
+
+	ctx, _ := testJavaWithFS(t, `
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_contributions: ["foo1"],
+			libs: ["lib1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "system",
+			api_contributions: ["foo1", "foo2"],
+			libs: ["lib1", "lib2", "bar1"],
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+			"c/Android.bp": []byte(lib_bp_a),
+			"c/Lib.java":   {},
+			"d/Android.bp": []byte(lib_bp_b),
+			"d/Lib.java":   {},
+		})
+
+	testcases := []struct {
+		moduleName        string
+		classPathJarNames []string
+	}{
+		{
+			moduleName:        "bar1",
+			classPathJarNames: []string{"lib1.jar"},
+		},
+		{
+			moduleName:        "bar2",
+			classPathJarNames: []string{"lib1.jar", "lib2.jar", "bar1/bar1.jar"},
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		javacRules := m.Rule("javac")
+		classPathArgs := javacRules.Args["classpath"]
+		for _, jarName := range c.classPathJarNames {
+			if !strings.Contains(classPathArgs, jarName) {
+				t.Errorf("Module output does not contain expected jar %s", jarName)
+			}
+		}
+	}
+}
+
+func TestJavaApiLibraryStaticLibsLink(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `
+	java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	lib_bp_a := `
+	java_library {
+		name: "lib1",
+		srcs: ["Lib.java"],
+	}
+	`
+	lib_bp_b := `
+	java_library {
+		name: "lib2",
+		srcs: ["Lib.java"],
+	}
+	`
+
+	ctx, _ := testJavaWithFS(t, `
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_contributions: ["foo1"],
+			static_libs: ["lib1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "system",
+			api_contributions: ["foo1", "foo2"],
+			static_libs: ["lib1", "lib2", "bar1"],
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+			"c/Android.bp": []byte(lib_bp_a),
+			"c/Lib.java":   {},
+			"d/Android.bp": []byte(lib_bp_b),
+			"d/Lib.java":   {},
+		})
+
+	testcases := []struct {
+		moduleName        string
+		staticLibJarNames []string
+	}{
+		{
+			moduleName:        "bar1",
+			staticLibJarNames: []string{"lib1.jar"},
+		},
+		{
+			moduleName:        "bar2",
+			staticLibJarNames: []string{"lib1.jar", "lib2.jar", "bar1/bar1.jar"},
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		mergeZipsCommand := m.Rule("merge_zips").RuleParams.Command
+		for _, jarName := range c.staticLibJarNames {
+			if !strings.Contains(mergeZipsCommand, jarName) {
+				t.Errorf("merge_zips command does not contain expected jar %s", jarName)
+			}
+		}
+	}
+}
+
 func TestTradefedOptions(t *testing.T) {
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 491ce29..933fc51 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -44,6 +44,10 @@
 
 	kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
 		Output("turbine-combined/kotlin-stdlib.jar").Output
+	kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
+		Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
+	kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
+		Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
 	kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
 		Output("turbine-combined/kotlin-annotations.jar").Output
 
@@ -79,6 +83,16 @@
 			fooJar.Inputs.Strings(), kotlinStdlib.String())
 	}
 
+	if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
+		t.Errorf("foo jar inputs %v does not contain %v",
+			fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
+	}
+
+	if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
+		t.Errorf("foo jar inputs %v does not contain %v",
+			fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
+	}
+
 	if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
 		t.Errorf("foo jar inputs %v does not contain %v",
 			fooJar.Inputs.Strings(), kotlinAnnotations.String())
diff --git a/java/lint.go b/java/lint.go
index 07b9629..40ef484 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -96,9 +96,10 @@
 }
 
 type lintOutputs struct {
-	html android.Path
-	text android.Path
-	xml  android.Path
+	html              android.Path
+	text              android.Path
+	xml               android.Path
+	referenceBaseline android.Path
 
 	depSets LintDepSets
 }
@@ -159,6 +160,50 @@
 	}
 }
 
+type lintDatabaseFiles struct {
+	apiVersionsModule       string
+	apiVersionsCopiedName   string
+	apiVersionsPrebuiltPath string
+	annotationsModule       string
+	annotationCopiedName    string
+	annotationPrebuiltpath  string
+}
+
+var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{
+	android.SdkPublic: {
+		apiVersionsModule:       "api_versions_public",
+		apiVersionsCopiedName:   "api_versions_public.xml",
+		apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml",
+		annotationsModule:       "sdk-annotations.zip",
+		annotationCopiedName:    "annotations-public.zip",
+		annotationPrebuiltpath:  "prebuilts/sdk/current/public/data/annotations.zip",
+	},
+	android.SdkSystem: {
+		apiVersionsModule:       "api_versions_system",
+		apiVersionsCopiedName:   "api_versions_system.xml",
+		apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml",
+		annotationsModule:       "sdk-annotations-system.zip",
+		annotationCopiedName:    "annotations-system.zip",
+		annotationPrebuiltpath:  "prebuilts/sdk/current/system/data/annotations.zip",
+	},
+	android.SdkModule: {
+		apiVersionsModule:       "api_versions_module_lib",
+		apiVersionsCopiedName:   "api_versions_module_lib.xml",
+		apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml",
+		annotationsModule:       "sdk-annotations-module-lib.zip",
+		annotationCopiedName:    "annotations-module-lib.zip",
+		annotationPrebuiltpath:  "prebuilts/sdk/current/module-lib/data/annotations.zip",
+	},
+	android.SdkSystemServer: {
+		apiVersionsModule:       "api_versions_system_server",
+		apiVersionsCopiedName:   "api_versions_system_server.xml",
+		apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml",
+		annotationsModule:       "sdk-annotations-system-server.zip",
+		annotationCopiedName:    "annotations-system-server.zip",
+		annotationPrebuiltpath:  "prebuilts/sdk/current/system-server/data/annotations.zip",
+	},
+}
+
 func (l *linter) LintDepSets() LintDepSets {
 	return l.outputs.depSets
 }
@@ -269,19 +314,25 @@
 	cmd.FlagWithInput("@",
 		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
 
-	cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
+	if l.compileSdkKind == android.SdkPublic {
+		cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
+	} else {
+		// TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed.
+		cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors)
+	}
 	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
 	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	if l.GetStrictUpdatabilityLinting() {
-		// Verify the module does not baseline issues that endanger safe updatability.
-		if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
-			cmd.FlagWithInput("--baseline ", baselinePath.Path())
-			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
-		}
-	}
+	// TODO(b/193460475): Re-enable strict updatability linting
+	//if l.GetStrictUpdatabilityLinting() {
+	//	// Verify the module does not baseline issues that endanger safe updatability.
+	//	if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
+	//		cmd.FlagWithInput("--baseline ", baselinePath.Path())
+	//		cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+	//	}
+	//}
 
 	return lintPaths{
 		projectXML: projectXMLPath,
@@ -400,7 +451,7 @@
 	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
 	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
 	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
-	baseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
+	referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
 
 	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
 
@@ -414,26 +465,17 @@
 	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
 	rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
 
-	var apiVersionsName, apiVersionsPrebuilt string
-	if l.compileSdkKind == android.SdkModule || l.compileSdkKind == android.SdkSystemServer {
-		// When compiling an SDK module (or system server) we use the filtered
-		// database because otherwise lint's
-		// NewApi check produces too many false positives; This database excludes information
-		// about classes created in mainline modules hence removing those false positives.
-		apiVersionsName = "api_versions_public_filtered.xml"
-		apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions-filtered.xml"
-	} else {
-		apiVersionsName = "api_versions.xml"
-		apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions.xml"
+	files, ok := allLintDatabasefiles[l.compileSdkKind]
+	if !ok {
+		files = allLintDatabasefiles[android.SdkPublic]
 	}
-
 	var annotationsZipPath, apiVersionsXMLPath android.Path
 	if ctx.Config().AlwaysUsePrebuiltSdks() {
-		annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
-		apiVersionsXMLPath = android.PathForSource(ctx, apiVersionsPrebuilt)
+		annotationsZipPath = android.PathForSource(ctx, files.annotationPrebuiltpath)
+		apiVersionsXMLPath = android.PathForSource(ctx, files.apiVersionsPrebuiltPath)
 	} else {
-		annotationsZipPath = copiedAnnotationsZipPath(ctx)
-		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx, apiVersionsName)
+		annotationsZipPath = copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName)
+		apiVersionsXMLPath = copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName)
 	}
 
 	cmd := rule.Command()
@@ -454,7 +496,6 @@
 		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
-		Flag("--exitcode").
 		Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox
 		Flags(l.properties.Lint.Flags).
 		Implicit(annotationsZipPath).
@@ -463,6 +504,10 @@
 	rule.Temporary(lintPaths.projectXML)
 	rule.Temporary(lintPaths.configXML)
 
+	if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" {
+		cmd.Flag("--exitcode")
+	}
+
 	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
 		cmd.FlagWithArg("--check ", checkOnly)
 	}
@@ -472,7 +517,7 @@
 		cmd.FlagWithInput("--baseline ", lintBaseline.Path())
 	}
 
-	cmd.FlagWithOutput("--write-reference-baseline ", baseline)
+	cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
 
 	cmd.Text("; EXITCODE=$?; ")
 
@@ -494,9 +539,10 @@
 	rule.Build("lint", "lint")
 
 	l.outputs = lintOutputs{
-		html: html,
-		text: text,
-		xml:  xml,
+		html:              html,
+		text:              text,
+		xml:               xml,
+		referenceBaseline: referenceBaseline,
 
 		depSets: depSetsBuilder.Build(),
 	}
@@ -528,9 +574,10 @@
 }
 
 type lintSingleton struct {
-	htmlZip android.WritablePath
-	textZip android.WritablePath
-	xmlZip  android.WritablePath
+	htmlZip              android.WritablePath
+	textZip              android.WritablePath
+	xmlZip               android.WritablePath
+	referenceBaselineZip android.WritablePath
 }
 
 func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -558,54 +605,39 @@
 		return
 	}
 
-	apiVersionsDb := findModuleOrErr(ctx, "api_versions_public")
-	if apiVersionsDb == nil {
-		if !ctx.Config().AllowMissingDependencies() {
-			ctx.Errorf("lint: missing module api_versions_public")
+	for _, sdk := range android.SortedKeys(allLintDatabasefiles) {
+		files := allLintDatabasefiles[sdk]
+		apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule)
+		if apiVersionsDb == nil {
+			if !ctx.Config().AllowMissingDependencies() {
+				ctx.Errorf("lint: missing module api_versions_public")
+			}
+			return
 		}
-		return
-	}
 
-	sdkAnnotations := findModuleOrErr(ctx, "sdk-annotations.zip")
-	if sdkAnnotations == nil {
-		if !ctx.Config().AllowMissingDependencies() {
-			ctx.Errorf("lint: missing module sdk-annotations.zip")
+		sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule)
+		if sdkAnnotations == nil {
+			if !ctx.Config().AllowMissingDependencies() {
+				ctx.Errorf("lint: missing module sdk-annotations.zip")
+			}
+			return
 		}
-		return
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.CpIfChanged,
+			Input:  android.OutputFileForModule(ctx, sdkAnnotations, ""),
+			Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName),
+		})
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.CpIfChanged,
+			Input:  android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
+			Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName),
+		})
 	}
-
-	filteredDb := findModuleOrErr(ctx, "api-versions-xml-public-filtered")
-	if filteredDb == nil {
-		if !ctx.Config().AllowMissingDependencies() {
-			ctx.Errorf("lint: missing api-versions-xml-public-filtered")
-		}
-		return
-	}
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.CpIfChanged,
-		Input:  android.OutputFileForModule(ctx, sdkAnnotations, ""),
-		Output: copiedAnnotationsZipPath(ctx),
-	})
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.CpIfChanged,
-		Input:  android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
-		Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"),
-	})
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.CpIfChanged,
-		Input:  android.OutputFileForModule(ctx, filteredDb, ""),
-		Output: copiedAPIVersionsXmlPath(ctx, "api_versions_public_filtered.xml"),
-	})
 }
 
-func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
-	return android.PathForOutput(ctx, "lint", "annotations.zip")
-}
-
-func copiedAPIVersionsXmlPath(ctx android.PathContext, name string) android.WritablePath {
+func copiedLintDatabaseFilesPath(ctx android.PathContext, name string) android.WritablePath {
 	return android.PathForOutput(ctx, "lint", name)
 }
 
@@ -658,12 +690,15 @@
 	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
 	zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
 
-	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+	l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip")
+	zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline })
+
+	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
 }
 
 func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
 	if !ctx.Config().UnbundledBuild() {
-		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
 	}
 }
 
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 061f4d0..1bb4996 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -40,6 +40,13 @@
 # NewApi checks will continue to be enforced for apex deps since
 # lint.strict_updatability_linting will be true for those Soong modules
 --disable_check NewApi
+# Disable ChromeOS specific checks
+--disable_check PermissionImpliesUnsupportedChromeOsHardware
+# Disable UnsafeImplicitIntentLaunch until it can avoid false positives/crash
+# TODO(265425607)
+--disable_check UnsafeImplicitIntentLaunch
+# InvalidId will give errors on ids defined like android:id="@androidprv:id/contentPanel"
+--disable_check InvalidId
 
 # Downgrade existing errors to warnings
 --warning_check AppCompatResource                  # 55 occurences in 10 modules
diff --git a/java/lint_test.go b/java/lint_test.go
index 62450d5..ec901aa 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -113,8 +113,9 @@
 		t.Error("did not use the correct file for baseline")
 	}
 
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
-		t.Error("should check NewApi errors")
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--warning_check NewApi") {
+		// TODO(b/268261262): Change this to check for --error_check
+		t.Error("should check NewApi warnings")
 	}
 
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
@@ -174,55 +175,83 @@
 	}
 }
 
-func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
-	bp := `
-		java_library {
-			name: "foo",
-			srcs: [
-				"a.java",
-			],
-			static_libs: ["bar"],
-			min_sdk_version: "29",
-			sdk_version: "current",
-			lint: {
-				strict_updatability_linting: true,
-			},
-		}
-
-		java_library {
-			name: "bar",
-			srcs: [
-				"a.java",
-			],
-			min_sdk_version: "29",
-			sdk_version: "current",
-		}
-	`
-	fs := android.MockFS{
-		"lint-baseline.xml": nil,
-	}
-
-	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
-		RunTestWithBp(t, bp)
-
-	foo := result.ModuleForTests("foo", "android_common")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Error("did not restrict baselining NewApi")
-	}
-
-	bar := result.ModuleForTests("bar", "android_common")
-	sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Error("did not restrict baselining NewApi")
-	}
-}
+// TODO(b/193460475): Re-enable this test
+//func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+//	bp := `
+//		java_library {
+//			name: "foo",
+//			srcs: [
+//				"a.java",
+//			],
+//			static_libs: ["bar"],
+//			min_sdk_version: "29",
+//			sdk_version: "current",
+//			lint: {
+//				strict_updatability_linting: true,
+//			},
+//		}
+//
+//		java_library {
+//			name: "bar",
+//			srcs: [
+//				"a.java",
+//			],
+//			min_sdk_version: "29",
+//			sdk_version: "current",
+//		}
+//	`
+//	fs := android.MockFS{
+//		"lint-baseline.xml": nil,
+//	}
+//
+//	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
+//		RunTestWithBp(t, bp)
+//
+//	foo := result.ModuleForTests("foo", "android_common")
+//	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+//	if !strings.Contains(*sboxProto.Commands[0].Command,
+//		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+//		t.Error("did not restrict baselining NewApi")
+//	}
+//
+//	bar := result.ModuleForTests("bar", "android_common")
+//	sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto"))
+//	if !strings.Contains(*sboxProto.Commands[0].Command,
+//		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+//		t.Error("did not restrict baselining NewApi")
+//	}
+//}
 
 func TestJavaLintDatabaseSelectionFull(t *testing.T) {
-	testCases := []string{
-		"current", "core_platform", "system_current", "S", "30", "10000",
+	testCases := []struct {
+		sdk_version   string
+		expected_file string
+	}{
+		{
+			"current",
+			"api_versions_public.xml",
+		}, {
+			"core_platform",
+			"api_versions_public.xml",
+		}, {
+			"system_current",
+			"api_versions_system.xml",
+		}, {
+			"module_current",
+			"api_versions_module_lib.xml",
+		}, {
+			"system_server_current",
+			"api_versions_system_server.xml",
+		}, {
+			"S",
+			"api_versions_public.xml",
+		}, {
+			"30",
+			"api_versions_public.xml",
+		}, {
+			"10000",
+			"api_versions_public.xml",
+		},
 	}
 	bp := `
 		java_library {
@@ -238,7 +267,7 @@
 		}
 `
 	for _, testCase := range testCases {
-		thisBp := strings.Replace(bp, "XXX", testCase, 1)
+		thisBp := strings.Replace(bp, "XXX", testCase.sdk_version, 1)
 
 		result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, FixtureWithPrebuiltApis(map[string][]string{
 			"30":    {"foo"},
@@ -248,49 +277,8 @@
 
 		foo := result.ModuleForTests("foo", "android_common")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-		if strings.Contains(*sboxProto.Commands[0].Command,
-			"/api_versions_public_filtered.xml") {
-			t.Error("used public-filtered lint api database for case", testCase)
-		}
-		if !strings.Contains(*sboxProto.Commands[0].Command,
-			"/api_versions.xml") {
+		if !strings.Contains(*sboxProto.Commands[0].Command, "/"+testCase.expected_file) {
 			t.Error("did not use full api database for case", testCase)
 		}
 	}
-
-}
-
-func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) {
-	testCases := []string{
-		"module_current", "system_server_current",
-	}
-	bp := `
-		java_library {
-			name: "foo",
-			srcs: [
-				"a.java",
-			],
-			min_sdk_version: "29",
-			sdk_version: "XXX",
-			lint: {
-				strict_updatability_linting: true,
-			},
-		}
-`
-	for _, testCase := range testCases {
-		thisBp := strings.Replace(bp, "XXX", testCase, 1)
-		result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
-			RunTestWithBp(t, thisBp)
-
-		foo := result.ModuleForTests("foo", "android_common")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-		if !strings.Contains(*sboxProto.Commands[0].Command,
-			"/api_versions_public_filtered.xml") {
-			t.Error("did not use public-filtered lint api database for case", testCase)
-		}
-		if strings.Contains(*sboxProto.Commands[0].Command,
-			"/api_versions.xml") {
-			t.Error("used full api database for case", testCase)
-		}
-	}
 }
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index f0de7a4..0ea3609 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -129,7 +129,7 @@
 
 	// 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.
-	bootImageConfig := b.getImageConfig(ctx)
+	bootImageConfig := defaultBootImageConfig(ctx)
 	addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathBootJarDepTag)
 
 	// Add dependencies on all the apex jars.
@@ -205,7 +205,7 @@
 
 func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	// Include all non APEX jars
-	jars := b.getImageConfig(ctx).modules
+	jars := defaultBootImageConfig(ctx).modules
 
 	// Include jars from APEXes that don't populate their classpath proto config.
 	remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -266,10 +266,6 @@
 	}
 }
 
-func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
-	return defaultBootImageConfig(ctx)
-}
-
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
 
@@ -410,27 +406,25 @@
 	// GenerateSingletonBuildActions method as it cannot create it for itself.
 	dexpreopt.GetGlobalSoongConfig(ctx)
 
-	imageConfig := b.getImageConfig(ctx)
-	if imageConfig == nil {
-		return
-	}
-
 	global := dexpreopt.GetGlobalConfig(ctx)
 	if !shouldBuildBootImages(ctx.Config(), global) {
 		return
 	}
 
-	// Generate the framework profile rule
-	bootFrameworkProfileRule(ctx, imageConfig)
+	frameworkBootImageConfig := defaultBootImageConfig(ctx)
+	bootFrameworkProfileRule(ctx, frameworkBootImageConfig)
+	b.generateBootImage(ctx, frameworkBootImageName, platformModules)
+	b.generateBootImage(ctx, mainlineBootImageName, apexModules)
+	b.copyApexBootJarsForAppsDexpreopt(ctx, apexModules)
+	dumpOatRules(ctx, frameworkBootImageConfig)
+}
 
-	// Copy platform module dex jars to their predefined locations.
-	platformBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, platformModules)
-	copyBootJarsToPredefinedLocations(ctx, platformBootDexJarsByModule, imageConfig.dexPathsByModule)
+func (b *platformBootclasspathModule) generateBootImage(ctx android.ModuleContext, imageName string, modules []android.Module) {
+	imageConfig := genBootImageConfigs(ctx)[imageName]
 
-	// Copy apex module dex jars to their predefined locations.
-	config := GetApexBootConfig(ctx)
-	apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules)
-	copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule)
+	// 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)
@@ -443,6 +437,11 @@
 
 	// Build boot image files for the host variants. There are use directly by ART host side tests.
 	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
+}
 
-	dumpOatRules(ctx, imageConfig)
+// 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)
 }
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c6acd55..206d995 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -264,7 +264,7 @@
 	}
 
 	// Sort the keys in order to make build.ninja stable
-	for _, k := range android.SortedStringKeys(latest) {
+	for _, k := range android.SortedKeys(latest) {
 		info := latest[k]
 		name := PrebuiltApiModuleName(info.module, info.scope, "latest")
 		createApiModule(mctx, name, info.path)
@@ -284,7 +284,7 @@
 		}
 	}
 	// Create empty incompatibilities files for remaining modules
-	for _, k := range android.SortedStringKeys(latest) {
+	for _, k := range android.SortedKeys(latest) {
 		if _, ok := incompatibilities[k]; !ok {
 			createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
 		}
diff --git a/java/robolectric.go b/java/robolectric.go
index 68f27b8..008b8b1 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -302,6 +302,9 @@
 		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 			entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "robolectric-tests")
+			if r.testConfig != nil {
+				entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
+			}
 		})
 
 	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
diff --git a/java/rro.go b/java/rro.go
index 9d0667c..6a9ad9a 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -175,19 +175,19 @@
 	return ""
 }
 
-func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
 	if r.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(ctx, *r.properties.Min_sdk_version)
+		return android.ApiLevelFrom(ctx, *r.properties.Min_sdk_version)
 	}
-	return r.SdkVersion(ctx)
+	return r.SdkVersion(ctx).ApiLevel
 }
 
 func (r *RuntimeResourceOverlay) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, "")
 }
 
-func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return r.SdkVersion(ctx)
+func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return r.SdkVersion(ctx).ApiLevel
 }
 
 func (r *RuntimeResourceOverlay) Certificate() Certificate {
diff --git a/java/sdk.go b/java/sdk.go
index b0da5af..72a5006 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -57,14 +57,10 @@
 		return JAVA_VERSION_8
 	} else if sdk.FinalOrFutureInt() <= 31 {
 		return JAVA_VERSION_9
-	} else if ctx.Config().TargetsJava17() {
-		// Temporary experimental flag to be able to try and build with
-		// java version 17 options.  The flag, if used, just sets Java
-		// 17 as the default version, leaving any components that
-		// target an older version intact.
-		return JAVA_VERSION_17
-	} else {
+	} else if sdk.FinalOrFutureInt() <= 33 {
 		return JAVA_VERSION_11
+	} else {
+		return JAVA_VERSION_17
 	}
 }
 
@@ -195,12 +191,8 @@
 			bootclasspath:    corePlatformBootclasspathLibraries(ctx),
 			noFrameworksLibs: true,
 		}
-	case android.SdkPublic:
-		return toModule("android_stubs_current", sdkFrameworkAidlPath(ctx))
-	case android.SdkSystem:
-		return toModule("android_system_stubs_current", sdkFrameworkAidlPath(ctx))
-	case android.SdkTest:
-		return toModule("android_test_stubs_current", sdkFrameworkAidlPath(ctx))
+	case android.SdkPublic, android.SdkSystem, android.SdkTest:
+		return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx))
 	case android.SdkCore:
 		return sdkDep{
 			useModule:        true,
@@ -210,10 +202,10 @@
 		}
 	case android.SdkModule:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return toModule("android_module_lib_stubs_current", nonUpdatableFrameworkAidlPath(ctx))
+		return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), nonUpdatableFrameworkAidlPath(ctx))
 	case android.SdkSystemServer:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return toModule("android_system_server_stubs_current", sdkFrameworkAidlPath(ctx))
+		return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx))
 	default:
 		panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
 	}
@@ -276,9 +268,9 @@
 // Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules.
 func createSdkFrameworkAidl(ctx android.SingletonContext) {
 	stubsModules := []string{
-		"android_stubs_current",
-		"android_test_stubs_current",
-		"android_system_stubs_current",
+		android.SdkPublic.JavaLibraryName(ctx.Config()),
+		android.SdkTest.JavaLibraryName(ctx.Config()),
+		android.SdkSystem.JavaLibraryName(ctx.Config()),
 	}
 
 	combinedAidl := sdkFrameworkAidlPath(ctx)
@@ -293,7 +285,7 @@
 
 // Creates a version of framework.aidl for the non-updatable part of the platform.
 func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) {
-	stubsModules := []string{"android_module_lib_stubs_current"}
+	stubsModules := []string{android.SdkModule.JavaLibraryName(ctx.Config())}
 
 	combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
 	tempPath := tempPathForRestat(ctx, combinedAidl)
@@ -388,10 +380,7 @@
 	} else if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().AlwaysUsePrebuiltSdks() {
 		cmd.Text("cat")
 		apiTxtFileModules := []string{
-			"frameworks-base-api-current.txt",
-			"frameworks-base-api-system-current.txt",
-			"frameworks-base-api-module-lib-current.txt",
-			"frameworks-base-api-system-server-current.txt",
+			"api_fingerprint",
 		}
 		count := 0
 		ctx.VisitAllModules(func(module android.Module) {
@@ -402,10 +391,10 @@
 			}
 		})
 		if count != len(apiTxtFileModules) {
-			ctx.Errorf("Could not find all the expected API modules %v, found %d\n", apiTxtFileModules, count)
+			ctx.Errorf("Could not find expected API module %v, found %d\n", apiTxtFileModules, count)
 			return
 		}
-		cmd.Text("| md5sum | cut -d' ' -f1 >").
+		cmd.Text(">").
 			Output(out)
 	} else {
 		// Unbundled build
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 3b64bf7..5477ed6 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -28,6 +28,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/dexpreopt"
 )
 
@@ -546,14 +547,14 @@
 
 	// The properties specific to the module-lib api scope
 	//
-	// Unless explicitly specified by using test.enabled the module-lib api scope is
-	// disabled by default.
+	// Unless explicitly specified by using module_lib.enabled the module_lib api
+	// scope is disabled by default.
 	Module_lib ApiScopeProperties
 
 	// The properties specific to the system-server api scope
 	//
-	// Unless explicitly specified by using test.enabled the module-lib api scope is
-	// disabled by default.
+	// Unless explicitly specified by using system_server.enabled the
+	// system_server api scope is disabled by default.
 	System_server ApiScopeProperties
 
 	// Determines if the stubs are preferred over the implementation library
@@ -1163,6 +1164,8 @@
 type SdkLibrary struct {
 	Library
 
+	android.BazelModuleBase
+
 	sdkLibraryProperties sdkLibraryProperties
 
 	// Map from api scope to the scope specific property structure.
@@ -1228,7 +1231,7 @@
 var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil)
 
 func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) {
-	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) {
+	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) {
 		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 			isExternal := !module.depIsInSameApex(ctx, child)
 			if am, ok := child.(android.ApexModule); ok {
@@ -1376,7 +1379,7 @@
 	})
 
 	// Make the set of components exported by this module available for use elsewhere.
-	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
+	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedKeys(exportedComponents)}
 	ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
 
 	// Provide additional information for inclusion in an sdk's generated .info file.
@@ -1599,6 +1602,7 @@
 		Srcs                             []string
 		Installable                      *bool
 		Sdk_version                      *string
+		Api_surface                      *string
 		System_modules                   *string
 		Libs                             []string
 		Output_javadoc_comments          *bool
@@ -1638,6 +1642,7 @@
 	props.Srcs = append(props.Srcs, module.properties.Srcs...)
 	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
 	props.Sdk_version = module.deviceProperties.Sdk_version
+	props.Api_surface = &apiScope.name
 	props.System_modules = module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
@@ -1747,7 +1752,7 @@
 		}
 	}
 
-	mctx.CreateModule(DroidstubsFactory, &props)
+	mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx)
 }
 
 func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
@@ -1770,7 +1775,7 @@
 
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
-	moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel
+	moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
 	var moduleMinApiLevelStr = moduleMinApiLevel.String()
 	if moduleMinApiLevel == android.NoneApiLevel {
 		moduleMinApiLevelStr = "current"
@@ -2079,9 +2084,48 @@
 			module.CreateInternalModules(ctx)
 		}
 	})
+	android.InitBazelModule(module)
 	return module
 }
 
+type bazelSdkLibraryAttributes struct {
+	Public        bazel.StringAttribute
+	System        bazel.StringAttribute
+	Test          bazel.StringAttribute
+	Module_lib    bazel.StringAttribute
+	System_server bazel.StringAttribute
+}
+
+// java_sdk_library bp2build converter
+func (module *SdkLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if ctx.ModuleType() != "java_sdk_library" {
+		return
+	}
+
+	nameToAttr := make(map[string]bazel.StringAttribute)
+
+	for _, scope := range module.getGeneratedApiScopes(ctx) {
+		apiSurfaceFile := path.Join(module.getApiDir(), scope.apiFilePrefix+"current.txt")
+		var scopeStringAttribute bazel.StringAttribute
+		scopeStringAttribute.SetValue(apiSurfaceFile)
+		nameToAttr[scope.name] = scopeStringAttribute
+	}
+
+	attrs := bazelSdkLibraryAttributes{
+		Public:        nameToAttr["public"],
+		System:        nameToAttr["system"],
+		Test:          nameToAttr["test"],
+		Module_lib:    nameToAttr["module-lib"],
+		System_server: nameToAttr["system-server"],
+	}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_sdk_library",
+		Bzl_load_location: "//build/bazel/rules/java:sdk_library.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, &attrs)
+}
+
 //
 // SDK library prebuilts
 //
@@ -2199,7 +2243,7 @@
 
 	allScopeProperties, scopeToProperties := createPropertiesInstance()
 	module.scopeProperties = scopeToProperties
-	module.AddProperties(&module.properties, allScopeProperties)
+	module.AddProperties(&module.properties, allScopeProperties, &module.importDexpreoptProperties)
 
 	// Initialize information common between source and prebuilt.
 	module.initCommon(module)
@@ -2370,8 +2414,8 @@
 }
 
 // MinSdkVersion - Implements hiddenAPIModule
-func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return android.SdkSpecNone
+func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return android.NoneApiLevel
 }
 
 var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
@@ -2443,18 +2487,24 @@
 			if di == nil {
 				return // An error has been reported by FindDeapexerProviderForModule.
 			}
-			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
+			dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(module.BaseModuleName())
+			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
 				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
 				module.dexJarFile = dexJarFile
 				installPath := android.PathForModuleInPartitionInstall(
-					ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+					ctx, "apex", ai.ApexVariationName, dexJarFileApexRootRelative)
 				module.installFile = installPath
 				module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 
-				// Dexpreopting.
 				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
 				module.dexpreopter.isSDKLibrary = true
 				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter)
+
+				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
+					module.dexpreopter.inputProfilePathOnHost = profilePath
+				}
+
+				// Dexpreopting.
 				module.dexpreopt(ctx, dexOutputPath)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
@@ -2583,7 +2633,7 @@
 
 func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
 	name := module.BaseModuleName()
-	return requiredFilesFromPrebuiltApexForImport(name)
+	return requiredFilesFromPrebuiltApexForImport(name, &module.dexpreopter)
 }
 
 // java_sdk_library_xml
@@ -2992,6 +3042,8 @@
 	//
 	// This means that the device won't recognise this library as installed.
 	Max_device_sdk *string
+
+	DexPreoptProfileGuided *bool `supported_build_releases:"UpsideDownCake+"`
 }
 
 type scopeProperties struct {
@@ -3045,6 +3097,10 @@
 	s.On_bootclasspath_before = sdk.commonSdkLibraryProperties.On_bootclasspath_before
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
+
+	if sdk.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
+	}
 }
 
 func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -3060,6 +3116,10 @@
 	if len(s.Permitted_packages) > 0 {
 		propertySet.AddProperty("permitted_packages", s.Permitted_packages)
 	}
+	dexPreoptSet := propertySet.AddPropertySet("dex_preopt")
+	if s.DexPreoptProfileGuided != nil {
+		dexPreoptSet.AddProperty("profile_guided", proptools.Bool(s.DexPreoptProfileGuided))
+	}
 
 	stem := s.Stem
 
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 210bfc3..1d0c13d 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -120,6 +120,7 @@
 	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
 	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
 	result.ModuleForTests("foo.api.public.28", "")
 	result.ModuleForTests("foo.api.system.28", "")
diff --git a/java/testing.go b/java/testing.go
index e6f76e1..63d7dba 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -198,7 +198,7 @@
 				imports_sdk_version: "none",
 				imports_compile_dex: true,
 			}
-		`, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`))
+		`, strings.Join(android.SortedKeys(apiLevel2Modules), `", "`))
 
 	for release, modules := range apiLevel2Modules {
 		mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
diff --git a/licenses/OWNERS b/licenses/OWNERS
deleted file mode 100644
index fddfc48..0000000
--- a/licenses/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file * = bbadour@google.com
-
diff --git a/linkerconfig/proto/OWNERS b/linkerconfig/proto/OWNERS
deleted file mode 100644
index 31f0460..0000000
--- a/linkerconfig/proto/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-kiyoungkim@google.com
-jiyong@google.com
-jooyung@google.com
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
index 4fa3eb6..cd80a4d 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -19,7 +19,7 @@
 
 blueprint_go_binary {
     name: "mk2rbc",
-    srcs: ["cmd/mk2rbc.go"],
+    srcs: ["mk2rbc/mk2rbc.go"],
     deps: [
         "mk2rbc-lib",
         "androidmk-parser",
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
index f8c930a..5f55f6a 100644
--- a/mk2rbc/android_products_test.go
+++ b/mk2rbc/android_products_test.go
@@ -29,7 +29,6 @@
 	}
 	expectedProducts := map[string]string{
 		"aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
-		"aosp_tv_arm":    abspath("aosp_tv_arm.mk"),
 		"aosp_tv_arm64":  abspath("aosp_tv_arm64.mk"),
 	}
 	if !reflect.DeepEqual(actualProducts, expectedProducts) {
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/mk2rbc/mk2rbc.go
similarity index 100%
rename from mk2rbc/cmd/mk2rbc.go
rename to mk2rbc/mk2rbc/mk2rbc.go
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
index a2220ed..400ec35 100644
--- a/mk2rbc/test/android_products.mk.test
+++ b/mk2rbc/test/android_products.mk.test
@@ -1,4 +1,3 @@
 PRODUCT_MAKEFILES := \
-    $(LOCAL_DIR)/aosp_tv_arm.mk \
     $(LOCAL_DIR)/aosp_tv_arm64.mk \
     aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file
diff --git a/multitree/api_imports.go b/multitree/api_imports.go
index 6674d3e..07ec7bc 100644
--- a/multitree/api_imports.go
+++ b/multitree/api_imports.go
@@ -40,8 +40,9 @@
 }
 
 type apiImportsProperties struct {
-	Shared_libs []string // List of C shared libraries from API surfaces
-	Header_libs []string // List of C header libraries from API surfaces
+	Shared_libs      []string // List of C shared libraries from API surfaces
+	Header_libs      []string // List of C header libraries from API surfaces
+	Apex_shared_libs []string // List of C shared libraries with APEX stubs
 }
 
 // 'api_imports' is a module which describes modules available from API surfaces.
@@ -60,7 +61,7 @@
 }
 
 type ApiImportInfo struct {
-	SharedLibs, HeaderLibs map[string]string
+	SharedLibs, HeaderLibs, ApexSharedLibs map[string]string
 }
 
 var ApiImportsProvider = blueprint.NewMutatorProvider(ApiImportInfo{}, "deps")
@@ -78,10 +79,12 @@
 
 	sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs)
 	headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
+	apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs)
 
 	ctx.SetProvider(ApiImportsProvider, ApiImportInfo{
-		SharedLibs: sharedLibs,
-		HeaderLibs: headerLibs,
+		SharedLibs:     sharedLibs,
+		HeaderLibs:     headerLibs,
+		ApexSharedLibs: apexSharedLibs,
 	})
 }
 
diff --git a/python/Android.bp b/python/Android.bp
index e49fa6a..7578673 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -9,13 +9,13 @@
         "blueprint",
         "soong-android",
         "soong-tradefed",
+        "soong-cc",
     ],
     srcs: [
-        "androidmk.go",
         "binary.go",
+        "bp2build.go",
         "builder.go",
         "defaults.go",
-        "installer.go",
         "library.go",
         "proto.go",
         "python.go",
diff --git a/python/androidmk.go b/python/androidmk.go
deleted file mode 100644
index 7dc4713..0000000
--- a/python/androidmk.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 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 python
-
-import (
-	"path/filepath"
-	"strings"
-
-	"android/soong/android"
-)
-
-type subAndroidMkProvider interface {
-	AndroidMk(*Module, *android.AndroidMkEntries)
-}
-
-func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
-	if p.subAndroidMkOnce == nil {
-		p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
-	}
-	if androidmk, ok := obj.(subAndroidMkProvider); ok {
-		if !p.subAndroidMkOnce[androidmk] {
-			p.subAndroidMkOnce[androidmk] = true
-			androidmk.AndroidMk(p, entries)
-		}
-	}
-}
-
-func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
-	entries := android.AndroidMkEntries{OutputFile: p.installSource}
-
-	p.subAndroidMk(&entries, p.installer)
-
-	return []android.AndroidMkEntries{entries}
-}
-
-func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
-	entries.Class = "EXECUTABLES"
-
-	entries.ExtraEntries = append(entries.ExtraEntries,
-		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
-		})
-	base.subAndroidMk(entries, p.pythonInstaller)
-}
-
-func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
-	entries.Class = "NATIVE_TESTS"
-
-	entries.ExtraEntries = append(entries.ExtraEntries,
-		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
-			if p.testConfig != nil {
-				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
-			}
-
-			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-
-			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-
-			p.testProperties.Test_options.SetAndroidMkEntries(entries)
-		})
-	base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
-}
-
-func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
-	entries.Required = append(entries.Required, "libc++")
-	entries.ExtraEntries = append(entries.ExtraEntries,
-		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			path, file := filepath.Split(installer.path.String())
-			stem := strings.TrimSuffix(file, filepath.Ext(file))
-
-			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
-			entries.SetString("LOCAL_MODULE_PATH", path)
-			entries.SetString("LOCAL_MODULE_STEM", stem)
-			entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
-			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
-		})
-}
diff --git a/python/binary.go b/python/binary.go
index 670e0d3..75135f3 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,11 +18,10 @@
 
 import (
 	"fmt"
+	"path/filepath"
+	"strings"
 
 	"android/soong/android"
-	"android/soong/bazel"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -33,63 +32,6 @@
 	ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
 }
 
-type bazelPythonBinaryAttributes struct {
-	Main           *bazel.Label
-	Srcs           bazel.LabelListAttribute
-	Deps           bazel.LabelListAttribute
-	Python_version *string
-	Imports        bazel.StringListAttribute
-}
-
-func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
-	// TODO(b/182306917): this doesn't fully handle all nested props versioned
-	// by the python version, which would have been handled by the version split
-	// mutator. This is sufficient for very simple python_binary_host modules
-	// under Bionic.
-	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
-	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
-	var python_version *string
-	if py3Enabled && py2Enabled {
-		panic(fmt.Errorf(
-			"error for '%s' module: bp2build's python_binary_host converter does not support "+
-				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
-	} else if py2Enabled {
-		python_version = &pyVersion2
-	} else {
-		// do nothing, since python_version defaults to PY3.
-	}
-
-	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-	attrs := &bazelPythonBinaryAttributes{
-		Main:           nil,
-		Srcs:           baseAttrs.Srcs,
-		Deps:           baseAttrs.Deps,
-		Python_version: python_version,
-		Imports:        baseAttrs.Imports,
-	}
-
-	for _, propIntf := range m.GetProperties() {
-		if props, ok := propIntf.(*BinaryProperties); ok {
-			// main is optional.
-			if props.Main != nil {
-				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
-				attrs.Main = &main
-				break
-			}
-		}
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		// Use the native py_binary rule.
-		Rule_class: "py_binary",
-	}
-
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: m.Name(),
-		Data: baseAttrs.Data,
-	}, attrs)
-}
-
 type BinaryProperties struct {
 	// the name of the source file that is the main entry point of the program.
 	// this file must also be listed in srcs.
@@ -118,49 +60,58 @@
 	Auto_gen_config *bool
 }
 
-type binaryDecorator struct {
+type PythonBinaryModule struct {
+	PythonLibraryModule
 	binaryProperties BinaryProperties
 
-	*pythonInstaller
+	// (.intermediate) module output path as installation source.
+	installSource android.Path
+
+	// Final installation path.
+	installedDest android.Path
+
+	androidMkSharedLibs []string
 }
 
+var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
+var _ android.Module = (*PythonBinaryModule)(nil)
+
 type IntermPathProvider interface {
 	IntermPathForModuleOut() android.OptionalPath
 }
 
-func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
-	module := newModule(hod, android.MultilibFirst)
-	decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
-
-	module.bootstrapper = decorator
-	module.installer = decorator
-
-	return module, decorator
+func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
+	return &PythonBinaryModule{
+		PythonLibraryModule: *newModule(hod, android.MultilibFirst),
+	}
 }
 
 func PythonBinaryHostFactory() android.Module {
-	module, _ := NewBinary(android.HostSupported)
-
-	android.InitBazelModule(module)
-
-	return module.init()
+	return NewBinary(android.HostSupported).init()
 }
 
-func (binary *binaryDecorator) autorun() bool {
-	return BoolDefault(binary.binaryProperties.Autorun, true)
+func (p *PythonBinaryModule) init() android.Module {
+	p.AddProperties(&p.properties, &p.protoProperties)
+	p.AddProperties(&p.binaryProperties)
+	android.InitAndroidArchModule(p, p.hod, p.multilib)
+	android.InitDefaultableModule(p)
+	android.InitBazelModule(p)
+	return p
 }
 
-func (binary *binaryDecorator) bootstrapperProps() []interface{} {
-	return []interface{}{&binary.binaryProperties}
+func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+	p.buildBinary(ctx)
+	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
+		p.installSource.Base(), p.installSource)
 }
 
-func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
-	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
-	depsSrcsZips android.Paths) android.OptionalPath {
-
+func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
+	embeddedLauncher := p.isEmbeddedLauncherEnabled()
+	depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
 	main := ""
-	if binary.autorun() {
-		main = binary.getPyMainFile(ctx, srcsPathMappings)
+	if p.autorun() {
+		main = p.getPyMainFile(ctx, p.srcsPathMappings)
 	}
 
 	var launcherPath android.OptionalPath
@@ -175,15 +126,88 @@
 			}
 		})
 	}
-	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
-		binary.getHostInterpreterName(ctx, actualVersion),
-		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+	srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1)
+	if embeddedLauncher {
+		srcsZips = append(srcsZips, p.precompiledSrcsZip)
+	} else {
+		srcsZips = append(srcsZips, p.srcsZip)
+	}
+	srcsZips = append(srcsZips, depsSrcsZips...)
+	p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+		p.getHostInterpreterName(ctx, p.properties.Actual_version),
+		main, p.getStem(ctx), srcsZips)
 
-	return android.OptionalPathForPath(binFile)
+	var sharedLibs []string
+	// if embedded launcher is enabled, we need to collect the shared library dependencies of the
+	// launcher
+	for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+		sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+	}
+	p.androidMkSharedLibs = sharedLibs
+}
+
+func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
+	entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}
+
+	entries.Class = "EXECUTABLES"
+
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+		})
+
+	entries.Required = append(entries.Required, "libc++")
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			path, file := filepath.Split(p.installedDest.String())
+			stem := strings.TrimSuffix(file, filepath.Ext(file))
+
+			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+			entries.SetString("LOCAL_MODULE_PATH", path)
+			entries.SetString("LOCAL_MODULE_STEM", stem)
+			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
+			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+		})
+
+	return []android.AndroidMkEntries{entries}
+}
+
+func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	p.PythonLibraryModule.DepsMutator(ctx)
+
+	if p.isEmbeddedLauncherEnabled() {
+		p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target())
+	}
+}
+
+// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
+// fulfilling the android.HostToolProvider interface.
+func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
+	// TODO: This should only be set when building host binaries -- tests built for device would be
+	// setting this incorrectly.
+	return android.OptionalPathForPath(p.installedDest)
+}
+
+// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
+func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.installSource}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
+	return Bool(p.properties.Embedded_launcher)
+}
+
+func (b *PythonBinaryModule) autorun() bool {
+	return BoolDefault(b.binaryProperties.Autorun, true)
 }
 
 // get host interpreter name.
-func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
 	actualVersion string) string {
 	var interp string
 	switch actualVersion {
@@ -200,13 +224,13 @@
 }
 
 // find main program path within runfiles tree.
-func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
 	srcsPathMappings []pathMapping) string {
 	var main string
-	if String(binary.binaryProperties.Main) == "" {
+	if String(p.binaryProperties.Main) == "" {
 		main = ctx.ModuleName() + pyExt
 	} else {
-		main = String(binary.binaryProperties.Main)
+		main = String(p.binaryProperties.Main)
 	}
 
 	for _, path := range srcsPathMappings {
@@ -219,11 +243,21 @@
 	return ""
 }
 
-func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
+func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
 	stem := ctx.ModuleName()
-	if String(binary.binaryProperties.Stem) != "" {
-		stem = String(binary.binaryProperties.Stem)
+	if String(p.binaryProperties.Stem) != "" {
+		stem = String(p.binaryProperties.Stem)
 	}
 
-	return stem + String(binary.binaryProperties.Suffix)
+	return stem + String(p.binaryProperties.Suffix)
+}
+
+func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
+	if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
+		dir = dir64
+	}
+	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+	}
+	return android.PathForModuleInstall(ctx, dir, relative)
 }
diff --git a/python/bp2build.go b/python/bp2build.go
new file mode 100644
index 0000000..bdac2dc
--- /dev/null
+++ b/python/bp2build.go
@@ -0,0 +1,226 @@
+// 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 python
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/bazel"
+)
+
+type bazelPythonLibraryAttributes struct {
+	Srcs         bazel.LabelListAttribute
+	Deps         bazel.LabelListAttribute
+	Imports      bazel.StringListAttribute
+	Srcs_version *string
+}
+
+type bazelPythonProtoLibraryAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
+type baseAttributes struct {
+	// TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
+	//Pkg_path    bazel.StringAttribute
+	// TODO: Related to Pkg_bath and similarLy gated
+	//Is_internal bazel.BoolAttribute
+	// Combines Srcs and Exclude_srcs
+	Srcs bazel.LabelListAttribute
+	Deps bazel.LabelListAttribute
+	// Combines Data and Java_data (invariant)
+	Data    bazel.LabelListAttribute
+	Imports bazel.StringListAttribute
+}
+
+func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
+	var attrs baseAttributes
+	archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
+	for axis, configToProps := range archVariantBaseProps {
+		for config, props := range configToProps {
+			if baseProps, ok := props.(*BaseProperties); ok {
+				attrs.Srcs.SetSelectValue(axis, config,
+					android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
+				attrs.Deps.SetSelectValue(axis, config,
+					android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
+				data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
+				data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
+				attrs.Data.SetSelectValue(axis, config, data)
+			}
+		}
+	}
+
+	partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
+		"proto": android.ProtoSrcLabelPartition,
+		"py":    bazel.LabelPartition{Keep_remainder: true},
+	})
+	attrs.Srcs = partitionedSrcs["py"]
+
+	if !partitionedSrcs["proto"].IsEmpty() {
+		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
+		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+
+		pyProtoLibraryName := m.Name() + "_py_proto"
+		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+			Rule_class:        "py_proto_library",
+			Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
+		}, android.CommonAttributes{
+			Name: pyProtoLibraryName,
+		}, &bazelPythonProtoLibraryAttributes{
+			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+		})
+
+		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
+	}
+
+	// Bazel normally requires `import path.from.top.of.tree` statements in
+	// python code, but with soong you can directly import modules from libraries.
+	// Add "imports" attributes to the bazel library so it matches soong's behavior.
+	imports := "."
+	if m.properties.Pkg_path != nil {
+		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
+		// pkg_path properly right now. If the folder structure that contains this
+		// Android.bp file matches pkg_path, we can set imports to an appropriate
+		// number of ../..s to emulate moving the files under a pkg_path folder.
+		pkg_path := filepath.Clean(*m.properties.Pkg_path)
+		if strings.HasPrefix(pkg_path, "/") {
+			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
+		}
+
+		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
+			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
+		}
+		numFolders := strings.Count(pkg_path, "/") + 1
+		dots := make([]string, numFolders)
+		for i := 0; i < numFolders; i++ {
+			dots[i] = ".."
+		}
+		imports = strings.Join(dots, "/")
+	}
+	attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
+
+	return attrs
+}
+
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *PythonLibraryModule) {
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_library modules under
+	// Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version *string
+	if py2Enabled && !py3Enabled {
+		python_version = &pyVersion2
+	} else if !py2Enabled && py3Enabled {
+		python_version = &pyVersion3
+	} else if !py2Enabled && !py3Enabled {
+		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
+	} else {
+		// do nothing, since python_version defaults to PY2ANDPY3
+	}
+
+	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+
+	attrs := &bazelPythonLibraryAttributes{
+		Srcs:         baseAttrs.Srcs,
+		Deps:         baseAttrs.Deps,
+		Srcs_version: python_version,
+		Imports:      baseAttrs.Imports,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_library rule.
+		Rule_class: "py_library",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: m.Name(),
+		Data: baseAttrs.Data,
+	}, attrs)
+}
+
+type bazelPythonBinaryAttributes struct {
+	Main           *bazel.Label
+	Srcs           bazel.LabelListAttribute
+	Deps           bazel.LabelListAttribute
+	Python_version *string
+	Imports        bazel.StringListAttribute
+}
+
+func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *PythonBinaryModule) {
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_binary_host modules
+	// under Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version *string
+	if py3Enabled && py2Enabled {
+		panic(fmt.Errorf(
+			"error for '%s' module: bp2build's python_binary_host converter does not support "+
+				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
+	} else if py2Enabled {
+		python_version = &pyVersion2
+	} else {
+		// do nothing, since python_version defaults to PY3.
+	}
+
+	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+	attrs := &bazelPythonBinaryAttributes{
+		Main:           nil,
+		Srcs:           baseAttrs.Srcs,
+		Deps:           baseAttrs.Deps,
+		Python_version: python_version,
+		Imports:        baseAttrs.Imports,
+	}
+
+	for _, propIntf := range m.GetProperties() {
+		if props, ok := propIntf.(*BinaryProperties); ok {
+			// main is optional.
+			if props.Main != nil {
+				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
+				attrs.Main = &main
+				break
+			}
+		}
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_binary rule.
+		Rule_class: "py_binary",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: m.Name(),
+		Data: baseAttrs.Data,
+	}, attrs)
+}
+
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	pythonLibBp2Build(ctx, p)
+}
+
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	pythonBinaryBp2Build(ctx, p)
+}
+
+func (p *PythonTestModule) ConvertWithBp2build(_ android.TopDownMutatorContext) {
+	// Tests are currently unsupported
+}
diff --git a/python/builder.go b/python/builder.go
index b4ab206..1066493 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -70,6 +70,17 @@
 			CommandDeps: []string{"$mergeParCmd"},
 		},
 		"srcsZips", "launcher")
+
+	precompile = pctx.AndroidStaticRule("precompilePython", blueprint.RuleParams{
+		Command: `LD_LIBRARY_PATH="$ldLibraryPath" ` +
+			`PYTHONPATH=$stdlibZip/internal/stdlib ` +
+			`$launcher build/soong/python/scripts/precompile_python.py $in $out`,
+		CommandDeps: []string{
+			"$stdlibZip",
+			"$launcher",
+			"build/soong/python/scripts/precompile_python.py",
+		},
+	}, "stdlibZip", "launcher", "ldLibraryPath")
 )
 
 func init() {
diff --git a/python/installer.go b/python/installer.go
deleted file mode 100644
index 396f036..0000000
--- a/python/installer.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 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 python
-
-import (
-	"path/filepath"
-
-	"android/soong/android"
-)
-
-// This file handles installing python executables into their final location
-
-type installLocation int
-
-const (
-	InstallInData installLocation = iota
-)
-
-type pythonInstaller struct {
-	dir      string
-	dir64    string
-	relative string
-
-	path android.InstallPath
-
-	androidMkSharedLibs []string
-}
-
-func NewPythonInstaller(dir, dir64 string) *pythonInstaller {
-	return &pythonInstaller{
-		dir:   dir,
-		dir64: dir64,
-	}
-}
-
-var _ installer = (*pythonInstaller)(nil)
-
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
-	dir := installer.dir
-	if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
-		dir = installer.dir64
-	}
-	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
-		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
-	}
-	return android.PathForModuleInstall(ctx, dir, installer.relative)
-}
-
-func (installer *pythonInstaller) install(ctx android.ModuleContext, file android.Path) {
-	installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
-}
-
-func (installer *pythonInstaller) setAndroidMkSharedLibs(sharedLibs []string) {
-	installer.androidMkSharedLibs = sharedLibs
-}
diff --git a/python/library.go b/python/library.go
index df92df4..7cdb80b 100644
--- a/python/library.go
+++ b/python/library.go
@@ -18,9 +18,6 @@
 
 import (
 	"android/soong/android"
-	"android/soong/bazel"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -33,66 +30,9 @@
 }
 
 func PythonLibraryHostFactory() android.Module {
-	module := newModule(android.HostSupported, android.MultilibFirst)
-
-	android.InitBazelModule(module)
-
-	return module.init()
-}
-
-type bazelPythonLibraryAttributes struct {
-	Srcs         bazel.LabelListAttribute
-	Deps         bazel.LabelListAttribute
-	Imports      bazel.StringListAttribute
-	Srcs_version *string
-}
-
-type bazelPythonProtoLibraryAttributes struct {
-	Deps bazel.LabelListAttribute
-}
-
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
-	// TODO(b/182306917): this doesn't fully handle all nested props versioned
-	// by the python version, which would have been handled by the version split
-	// mutator. This is sufficient for very simple python_library modules under
-	// Bionic.
-	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
-	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
-	var python_version *string
-	if py2Enabled && !py3Enabled {
-		python_version = &pyVersion2
-	} else if !py2Enabled && py3Enabled {
-		python_version = &pyVersion3
-	} else if !py2Enabled && !py3Enabled {
-		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
-	} else {
-		// do nothing, since python_version defaults to PY2ANDPY3
-	}
-
-	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-
-	attrs := &bazelPythonLibraryAttributes{
-		Srcs:         baseAttrs.Srcs,
-		Deps:         baseAttrs.Deps,
-		Srcs_version: python_version,
-		Imports:      baseAttrs.Imports,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		// Use the native py_library rule.
-		Rule_class: "py_library",
-	}
-
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: m.Name(),
-		Data: baseAttrs.Data,
-	}, attrs)
+	return newModule(android.HostSupported, android.MultilibFirst).init()
 }
 
 func PythonLibraryFactory() android.Module {
-	module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
-
-	android.InitBazelModule(module)
-
-	return module.init()
+	return newModule(android.HostAndDeviceSupported, android.MultilibBoth).init()
 }
diff --git a/python/python.go b/python/python.go
index 24e1bb2..c7c523d 100644
--- a/python/python.go
+++ b/python/python.go
@@ -22,8 +22,6 @@
 	"regexp"
 	"strings"
 
-	"android/soong/bazel"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -122,26 +120,13 @@
 	Embedded_launcher *bool `blueprint:"mutated"`
 }
 
-type baseAttributes struct {
-	// TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
-	//Pkg_path    bazel.StringAttribute
-	// TODO: Related to Pkg_bath and similarLy gated
-	//Is_internal bazel.BoolAttribute
-	// Combines Srcs and Exclude_srcs
-	Srcs bazel.LabelListAttribute
-	Deps bazel.LabelListAttribute
-	// Combines Data and Java_data (invariant)
-	Data    bazel.LabelListAttribute
-	Imports bazel.StringListAttribute
-}
-
 // Used to store files of current module after expanding dependencies
 type pathMapping struct {
 	dest string
 	src  android.Path
 }
 
-type Module struct {
+type PythonLibraryModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.BazelModuleBase
@@ -153,16 +138,6 @@
 	hod      android.HostOrDeviceSupported
 	multilib android.Multilib
 
-	// interface used to bootstrap .par executable when embedded_launcher is true
-	// this should be set by Python modules which are runnable, e.g. binaries and tests
-	// bootstrapper might be nil (e.g. Python library module).
-	bootstrapper bootstrapper
-
-	// interface that implements functions required for installation
-	// this should be set by Python modules which are runnable, e.g. binaries and tests
-	// installer might be nil (e.g. Python library module).
-	installer installer
-
 	// the Python files of current module after expanding source dependencies.
 	// pathMapping: <dest: runfile_path, src: source_path>
 	srcsPathMappings []pathMapping
@@ -171,152 +146,62 @@
 	// pathMapping: <dest: runfile_path, src: source_path>
 	dataPathMappings []pathMapping
 
-	// the zip filepath for zipping current module source/data files.
+	// The zip file containing the current module's source/data files.
 	srcsZip android.Path
 
-	// dependency modules' zip filepath for zipping current module source/data files.
-	depsSrcsZips android.Paths
-
-	// (.intermediate) module output path as installation source.
-	installSource android.OptionalPath
-
-	// Map to ensure sub-part of the AndroidMk for this module is only added once
-	subAndroidMkOnce map[subAndroidMkProvider]bool
+	// The zip file containing the current module's source/data files, with the
+	// source files precompiled.
+	precompiledSrcsZip android.Path
 }
 
 // newModule generates new Python base module
-func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
-	return &Module{
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *PythonLibraryModule {
+	return &PythonLibraryModule{
 		hod:      hod,
 		multilib: multilib,
 	}
 }
 
-func (m *Module) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
-	var attrs baseAttributes
-	archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
-	for axis, configToProps := range archVariantBaseProps {
-		for config, props := range configToProps {
-			if baseProps, ok := props.(*BaseProperties); ok {
-				attrs.Srcs.SetSelectValue(axis, config,
-					android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
-				attrs.Deps.SetSelectValue(axis, config,
-					android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
-				data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
-				data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
-				attrs.Data.SetSelectValue(axis, config, data)
-			}
-		}
-	}
-
-	partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
-		"proto": android.ProtoSrcLabelPartition,
-		"py":    bazel.LabelPartition{Keep_remainder: true},
-	})
-	attrs.Srcs = partitionedSrcs["py"]
-
-	if !partitionedSrcs["proto"].IsEmpty() {
-		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
-		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
-
-		pyProtoLibraryName := m.Name() + "_py_proto"
-		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
-			Rule_class:        "py_proto_library",
-			Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
-		}, android.CommonAttributes{
-			Name: pyProtoLibraryName,
-		}, &bazelPythonProtoLibraryAttributes{
-			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
-		})
-
-		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
-	}
-
-	// Bazel normally requires `import path.from.top.of.tree` statements in
-	// python code, but with soong you can directly import modules from libraries.
-	// Add "imports" attributes to the bazel library so it matches soong's behavior.
-	imports := "."
-	if m.properties.Pkg_path != nil {
-		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
-		// pkg_path properly right now. If the folder structure that contains this
-		// Android.bp file matches pkg_path, we can set imports to an appropriate
-		// number of ../..s to emulate moving the files under a pkg_path folder.
-		pkg_path := filepath.Clean(*m.properties.Pkg_path)
-		if strings.HasPrefix(pkg_path, "/") {
-			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
-		}
-
-		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
-			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
-		}
-		numFolders := strings.Count(pkg_path, "/") + 1
-		dots := make([]string, numFolders)
-		for i := 0; i < numFolders; i++ {
-			dots[i] = ".."
-		}
-		imports = strings.Join(dots, "/")
-	}
-	attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
-
-	return attrs
-}
-
-// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
-type bootstrapper interface {
-	bootstrapperProps() []interface{}
-	bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
-		srcsPathMappings []pathMapping, srcsZip android.Path,
-		depsSrcsZips android.Paths) android.OptionalPath
-
-	autorun() bool
-}
-
-// installer interface should be implemented for installable modules, e.g. binary and test
-type installer interface {
-	install(ctx android.ModuleContext, path android.Path)
-	setAndroidMkSharedLibs(sharedLibs []string)
-}
-
 // interface implemented by Python modules to provide source and data mappings and zip to python
 // modules that depend on it
 type pythonDependency interface {
 	getSrcsPathMappings() []pathMapping
 	getDataPathMappings() []pathMapping
 	getSrcsZip() android.Path
+	getPrecompiledSrcsZip() android.Path
 }
 
 // getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
-func (p *Module) getSrcsPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping {
 	return p.srcsPathMappings
 }
 
 // getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
-func (p *Module) getDataPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getDataPathMappings() []pathMapping {
 	return p.dataPathMappings
 }
 
 // getSrcsZip returns the filepath where the current module's source/data files are zipped.
-func (p *Module) getSrcsZip() android.Path {
+func (p *PythonLibraryModule) getSrcsZip() android.Path {
 	return p.srcsZip
 }
 
-var _ pythonDependency = (*Module)(nil)
+// getSrcsZip returns the filepath where the current module's source/data files are zipped.
+func (p *PythonLibraryModule) getPrecompiledSrcsZip() android.Path {
+	return p.precompiledSrcsZip
+}
 
-var _ android.AndroidMkEntriesProvider = (*Module)(nil)
+func (p *PythonLibraryModule) getBaseProperties() *BaseProperties {
+	return &p.properties
+}
 
-func (p *Module) init(additionalProps ...interface{}) android.Module {
+var _ pythonDependency = (*PythonLibraryModule)(nil)
+
+func (p *PythonLibraryModule) init() android.Module {
 	p.AddProperties(&p.properties, &p.protoProperties)
-
-	// Add additional properties for bootstrapping/installation
-	// This is currently tied to the bootstrapper interface;
-	// however, these are a combination of properties for the installation and bootstrapping of a module
-	if p.bootstrapper != nil {
-		p.AddProperties(p.bootstrapper.bootstrapperProps()...)
-	}
-
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
 	android.InitDefaultableModule(p)
-
+	android.InitBazelModule(p)
 	return p
 }
 
@@ -338,36 +223,54 @@
 }
 
 var (
-	pythonLibTag         = dependencyTag{name: "pythonLib"}
-	javaDataTag          = dependencyTag{name: "javaData"}
+	pythonLibTag = dependencyTag{name: "pythonLib"}
+	javaDataTag  = dependencyTag{name: "javaData"}
+	// The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
 	launcherTag          = dependencyTag{name: "launcher"}
 	launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
-	pathComponentRegexp  = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
-	pyExt                = ".py"
-	protoExt             = ".proto"
-	pyVersion2           = "PY2"
-	pyVersion3           = "PY3"
-	internalPath         = "internal"
+	// The python interpreter built for host so that we can precompile python sources.
+	// This only works because the precompiled sources don't vary by architecture.
+	// The soong module name is "py3-launcher".
+	hostLauncherTag          = dependencyTag{name: "hostLauncher"}
+	hostlauncherSharedLibTag = dependencyTag{name: "hostlauncherSharedLib"}
+	hostStdLibTag            = dependencyTag{name: "hostStdLib"}
+	pathComponentRegexp      = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+	pyExt                    = ".py"
+	protoExt                 = ".proto"
+	pyVersion2               = "PY2"
+	pyVersion3               = "PY3"
+	internalPath             = "internal"
 )
 
+type basePropertiesProvider interface {
+	getBaseProperties() *BaseProperties
+}
+
 // versionSplitMutator creates version variants for modules and appends the version-specific
 // properties for a given variant to the properties in the variant module
 func versionSplitMutator() func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
-		if base, ok := mctx.Module().(*Module); ok {
-			versionNames := []string{}
+		if base, ok := mctx.Module().(basePropertiesProvider); ok {
+			props := base.getBaseProperties()
+			var versionNames []string
 			// collect version specific properties, so that we can merge version-specific properties
 			// into the module's overall properties
-			versionProps := []VersionProperties{}
+			var versionProps []VersionProperties
 			// PY3 is first so that we alias the PY3 variant rather than PY2 if both
 			// are available
-			if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) {
+			if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
 				versionNames = append(versionNames, pyVersion3)
-				versionProps = append(versionProps, base.properties.Version.Py3)
+				versionProps = append(versionProps, props.Version.Py3)
 			}
-			if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) {
+			if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
+				if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() &&
+					mctx.ModuleName() != "par_test" &&
+					mctx.ModuleName() != "py2-cmd" &&
+					mctx.ModuleName() != "py2-stdlib" {
+					mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration")
+				}
 				versionNames = append(versionNames, pyVersion2)
-				versionProps = append(versionProps, base.properties.Version.Py2)
+				versionProps = append(versionProps, props.Version.Py2)
 			}
 			modules := mctx.CreateLocalVariations(versionNames...)
 			// Alias module to the first variant
@@ -376,9 +279,10 @@
 			}
 			for i, v := range versionNames {
 				// set the actual version for Python module.
-				modules[i].(*Module).properties.Actual_version = v
+				newProps := modules[i].(basePropertiesProvider).getBaseProperties()
+				newProps.Actual_version = v
 				// append versioned properties for the Python module to the overall properties
-				err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
+				err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil)
 				if err != nil {
 					panic(err)
 				}
@@ -387,38 +291,6 @@
 	}
 }
 
-// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
-// fulfilling HostToolProvider interface.
-func (p *Module) HostToolPath() android.OptionalPath {
-	if p.installer != nil {
-		if bin, ok := p.installer.(*binaryDecorator); ok {
-			// TODO: This should only be set when building host binaries -- tests built for device would be
-			// setting this incorrectly.
-			return android.OptionalPathForPath(bin.path)
-		}
-	}
-
-	return android.OptionalPath{}
-
-}
-
-// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
-func (p *Module) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		if outputFile := p.installSource; outputFile.Valid() {
-			return android.Paths{outputFile.Path()}, nil
-		}
-		return android.Paths{}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-func (p *Module) isEmbeddedLauncherEnabled() bool {
-	return p.installer != nil && Bool(p.properties.Embedded_launcher)
-}
-
 func anyHasExt(paths []string, ext string) bool {
 	for _, p := range paths {
 		if filepath.Ext(p) == ext {
@@ -429,7 +301,7 @@
 	return false
 }
 
-func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
+func (p *PythonLibraryModule) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
 	return anyHasExt(p.properties.Srcs, ext)
 }
 
@@ -437,7 +309,7 @@
 //   - handles proto dependencies,
 //   - if required, specifies launcher and adds launcher dependencies,
 //   - applies python version mutations to Python dependencies
-func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	android.ProtoDeps(ctx, &p.protoProperties)
 
 	versionVariation := []blueprint.Variation{
@@ -452,111 +324,85 @@
 	// Add python library dependencies for this python version variation
 	ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
 
-	// If this module will be installed and has an embedded launcher, we need to add dependencies for:
-	//   * standard library
-	//   * launcher
-	//   * shared dependencies of the launcher
-	if p.installer != nil && p.isEmbeddedLauncherEnabled() {
-		var stdLib string
-		var launcherModule string
-		// Add launcher shared lib dependencies. Ideally, these should be
-		// derived from the `shared_libs` property of the launcher. However, we
-		// cannot read the property at this stage and it will be too late to add
-		// dependencies later.
-		launcherSharedLibDeps := []string{
-			"libsqlite",
-		}
-		// Add launcher-specific dependencies for bionic
-		if ctx.Target().Os.Bionic() {
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
-		}
-		if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
-		}
-
-		switch p.properties.Actual_version {
-		case pyVersion2:
-			stdLib = "py2-stdlib"
-
-			launcherModule = "py2-launcher"
-			if p.bootstrapper.autorun() {
-				launcherModule = "py2-launcher-autorun"
-			}
-
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
-
-		case pyVersion3:
-			stdLib = "py3-stdlib"
-
-			launcherModule = "py3-launcher"
-			if p.bootstrapper.autorun() {
-				launcherModule = "py3-launcher-autorun"
-			}
-			if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
-				launcherModule += "-static"
-			}
-
-			if ctx.Device() {
-				launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
-			}
-		default:
-			panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
-				p.properties.Actual_version, ctx.ModuleName()))
-		}
-		ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
-		ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
-		ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
-	}
-
 	// Emulate the data property for java_data but with the arch variation overridden to "common"
 	// so that it can point to java modules.
 	javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
 	ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
+
+	p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget)
 }
 
-func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.generatePythonBuildActions(ctx)
-
-	// Only Python binary and test modules have non-empty bootstrapper.
-	if p.bootstrapper != nil {
-		// if the module is being installed, we need to collect all transitive dependencies to embed in
-		// the final par
-		p.collectPathsFromTransitiveDeps(ctx)
-		// bootstrap the module, including resolving main file, getting launcher path, and
-		// registering actions to build the par file
-		// bootstrap returns the binary output path
-		p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
-			p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
+// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib,
+// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use
+// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument
+// as the target to use for these dependencies. For embedded launcher python binaries, the launcher
+// that will be embedded will be under the same target as the python module itself. But when
+// precompiling python code, we need to get the python launcher built for host, even if we're
+// compiling the python module for device, so we pass a different target to this function.
+func (p *PythonLibraryModule) AddDepsOnPythonLauncherAndStdlib(ctx android.BottomUpMutatorContext,
+	stdLibTag, launcherTag, launcherSharedLibTag blueprint.DependencyTag,
+	autorun bool, targetForDeps android.Target) {
+	var stdLib string
+	var launcherModule string
+	// Add launcher shared lib dependencies. Ideally, these should be
+	// derived from the `shared_libs` property of the launcher. TODO: read these from
+	// the python launcher itself using ctx.OtherModuleProvider() or similar on the result
+	// of ctx.AddFarVariationDependencies()
+	launcherSharedLibDeps := []string{
+		"libsqlite",
+	}
+	// Add launcher-specific dependencies for bionic
+	if targetForDeps.Os.Bionic() {
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
+	}
+	if targetForDeps.Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
 	}
 
-	// Only Python binary and test modules have non-empty installer.
-	if p.installer != nil {
-		var sharedLibs []string
-		// if embedded launcher is enabled, we need to collect the shared library depenendencies of the
-		// launcher
-		for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
-			sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+	switch p.properties.Actual_version {
+	case pyVersion2:
+		stdLib = "py2-stdlib"
+
+		launcherModule = "py2-launcher"
+		if autorun {
+			launcherModule = "py2-launcher-autorun"
 		}
 
-		p.installer.setAndroidMkSharedLibs(sharedLibs)
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
+	case pyVersion3:
+		stdLib = "py3-stdlib"
 
-		// Install the par file from installSource
-		if p.installSource.Valid() {
-			p.installer.install(ctx, p.installSource.Path())
+		launcherModule = "py3-launcher"
+		if autorun {
+			launcherModule = "py3-launcher-autorun"
 		}
+		if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
+			launcherModule += "-static"
+		}
+		if ctx.Device() {
+			launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
+		}
+	default:
+		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+			p.properties.Actual_version, ctx.ModuleName()))
 	}
+	targetVariations := targetForDeps.Variations()
+	if ctx.ModuleName() != stdLib {
+		stdLibVariations := make([]blueprint.Variation, 0, len(targetVariations)+1)
+		stdLibVariations = append(stdLibVariations, blueprint.Variation{Mutator: "python_version", Variation: p.properties.Actual_version})
+		stdLibVariations = append(stdLibVariations, targetVariations...)
+		// Using AddFarVariationDependencies for all of these because they can be for a different
+		// platform, like if the python module itself was being compiled for device, we may want
+		// the python interpreter built for host so that we can precompile python sources.
+		ctx.AddFarVariationDependencies(stdLibVariations, stdLibTag, stdLib)
+	}
+	ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule)
+	ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...)
 }
 
-// generatePythonBuildActions performs build actions common to all Python modules
-func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) {
+// GenerateAndroidBuildActions performs build actions common to all Python modules
+func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
-	requiresSrcs := true
-	if p.bootstrapper != nil && !p.bootstrapper.autorun() {
-		requiresSrcs = false
-	}
-	if len(expandedSrcs) == 0 && requiresSrcs {
-		ctx.ModuleErrorf("doesn't have any source files!")
-	}
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
@@ -589,6 +435,7 @@
 
 	// generate the zipfile of all source and data files
 	p.srcsZip = p.createSrcsZip(ctx, pkgPath)
+	p.precompiledSrcsZip = p.precompileSrcs(ctx)
 }
 
 func isValidPythonPath(path string) error {
@@ -607,7 +454,7 @@
 
 // For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
 // for python/data files expanded from properties.
-func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
+func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
 	expandedSrcs, expandedData android.Paths) {
 	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
 	// check current module duplicates.
@@ -642,27 +489,28 @@
 }
 
 // createSrcsZip registers build actions to zip current module's sources and data.
-func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
+func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
 	relativeRootMap := make(map[string]android.Paths)
-	pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
-
 	var protoSrcs android.Paths
-	// "srcs" or "data" properties may contain filegroup so it might happen that
-	// the root directory for each source path is different.
-	for _, path := range pathMappings {
+	addPathMapping := func(path pathMapping) {
 		// handle proto sources separately
 		if path.src.Ext() == protoExt {
 			protoSrcs = append(protoSrcs, path.src)
 		} else {
-			var relativeRoot string
-			relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
-			if v, found := relativeRootMap[relativeRoot]; found {
-				relativeRootMap[relativeRoot] = append(v, path.src)
-			} else {
-				relativeRootMap[relativeRoot] = android.Paths{path.src}
-			}
+			relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+			relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
 		}
 	}
+
+	// "srcs" or "data" properties may contain filegroups so it might happen that
+	// the root directory for each source path is different.
+	for _, path := range p.srcsPathMappings {
+		addPathMapping(path)
+	}
+	for _, path := range p.dataPathMappings {
+		addPathMapping(path)
+	}
+
 	var zips android.Paths
 	if len(protoSrcs) > 0 {
 		protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
@@ -690,7 +538,7 @@
 
 	if len(relativeRootMap) > 0 {
 		// in order to keep stable order of soong_zip params, we sort the keys here.
-		roots := android.SortedStringKeys(relativeRootMap)
+		roots := android.SortedKeys(relativeRootMap)
 
 		// Use -symlinks=false so that the symlinks in the bazel output directory are followed
 		parArgs := []string{"-symlinks=false"}
@@ -736,30 +584,79 @@
 	}
 }
 
-// isPythonLibModule returns whether the given module is a Python library Module or not
+func (p *PythonLibraryModule) precompileSrcs(ctx android.ModuleContext) android.Path {
+	// To precompile the python sources, we need a python interpreter and stdlib built
+	// for host. We then use those to compile the python sources, which may be used on either
+	// host of device. Python bytecode is architecture agnostic, so we're essentially
+	// "cross compiling" for device here purely by virtue of host and device python bytecode
+	// being the same.
+	var stdLib android.Path
+	var launcher android.Path
+	if ctx.ModuleName() == "py3-stdlib" || ctx.ModuleName() == "py2-stdlib" {
+		stdLib = p.srcsZip
+	} else {
+		ctx.VisitDirectDepsWithTag(hostStdLibTag, func(module android.Module) {
+			if dep, ok := module.(pythonDependency); ok {
+				stdLib = dep.getPrecompiledSrcsZip()
+			}
+		})
+	}
+	ctx.VisitDirectDepsWithTag(hostLauncherTag, func(module android.Module) {
+		if dep, ok := module.(IntermPathProvider); ok {
+			optionalLauncher := dep.IntermPathForModuleOut()
+			if optionalLauncher.Valid() {
+				launcher = optionalLauncher.Path()
+			}
+		}
+	})
+	var launcherSharedLibs android.Paths
+	var ldLibraryPath []string
+	ctx.VisitDirectDepsWithTag(hostlauncherSharedLibTag, func(module android.Module) {
+		if dep, ok := module.(IntermPathProvider); ok {
+			optionalPath := dep.IntermPathForModuleOut()
+			if optionalPath.Valid() {
+				launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path())
+				ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String()))
+			}
+		}
+	})
+
+	out := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszipprecompiled")
+	if stdLib == nil || launcher == nil {
+		// This shouldn't happen in a real build because we'll error out when adding dependencies
+		// on the stdlib and launcher if they don't exist. But some tests set
+		// AllowMissingDependencies.
+		return out
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        precompile,
+		Input:       p.srcsZip,
+		Output:      out,
+		Implicits:   launcherSharedLibs,
+		Description: "Precompile the python sources of " + ctx.ModuleName(),
+		Args: map[string]string{
+			"stdlibZip":     stdLib.String(),
+			"launcher":      launcher.String(),
+			"ldLibraryPath": strings.Join(ldLibraryPath, ":"),
+		},
+	})
+	return out
+}
+
+// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
 func isPythonLibModule(module blueprint.Module) bool {
-	if m, ok := module.(*Module); ok {
-		return m.isLibrary()
+	if _, ok := module.(*PythonLibraryModule); ok {
+		if _, ok := module.(*PythonBinaryModule); !ok {
+			return true
+		}
 	}
 	return false
 }
 
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
-func (p *Module) isLibrary() bool {
-	// Python library has no bootstrapper or installer
-	return p.bootstrapper == nil && p.installer == nil
-}
-
-func (p *Module) isBinary() bool {
-	_, ok := p.bootstrapper.(*binaryDecorator)
-	return ok
-}
-
 // collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
 // for module and its transitive dependencies and collects list of data/source file
 // zips for transitive dependencies.
-func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) {
+func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleContext, precompiled bool) android.Paths {
 	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
 	// check duplicates.
 	destToPySrcs := make(map[string]string)
@@ -773,6 +670,8 @@
 
 	seen := make(map[android.Module]bool)
 
+	var result android.Paths
+
 	// visit all its dependencies in depth first.
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		// we only collect dependencies tagged as python library deps
@@ -801,10 +700,15 @@
 				checkForDuplicateOutputPath(ctx, destToPyData,
 					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
 			}
-			p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
+			if precompiled {
+				result = append(result, dep.getPrecompiledSrcsZip())
+			} else {
+				result = append(result, dep.getSrcsZip())
+			}
 		}
 		return true
 	})
+	return result
 }
 
 // chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
@@ -825,18 +729,10 @@
 }
 
 // InstallInData returns true as Python is not supported in the system partition
-func (p *Module) InstallInData() bool {
+func (p *PythonLibraryModule) InstallInData() bool {
 	return true
 }
 
-func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	if p.isLibrary() {
-		pythonLibBp2Build(ctx, p)
-	} else if p.isBinary() {
-		pythonBinaryBp2Build(ctx, p)
-	}
-}
-
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/python/python_test.go b/python/python_test.go
index 42a1ffb..75a6a89 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -18,10 +18,10 @@
 	"fmt"
 	"os"
 	"path/filepath"
-	"regexp"
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
 )
 
 type pyModule struct {
@@ -33,8 +33,10 @@
 }
 
 var (
-	buildNamePrefix          = "soong_python_test"
-	moduleVariantErrTemplate = "%s: module %q variant %q: "
+	buildNamePrefix = "soong_python_test"
+	// We allow maching almost anything before the actual variant so that the os/arch variant
+	// is matched.
+	moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
 	pkgPathErrTemplate       = moduleVariantErrTemplate +
 		"pkg_path: %q must be a relative path contained in par file."
 	badIdentifierErrTemplate = moduleVariantErrTemplate +
@@ -312,10 +314,6 @@
 						"e/file4.py",
 					},
 					srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
-					depsSrcsZips: []string{
-						"out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
-						"out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
-					},
 				},
 			},
 		},
@@ -327,17 +325,26 @@
 		if d.desc != "module with duplicate runfile path" {
 			continue
 		}
-		errorPatterns := make([]string, len(d.errors))
-		for i, s := range d.errors {
-			errorPatterns[i] = regexp.QuoteMeta(s)
-		}
+		d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
+python_library {
+  name: "py3-stdlib",
+  host_supported: true,
+}
+cc_binary {
+  name: "py3-launcher",
+  host_supported: true,
+}
+`)
 
 		t.Run(d.desc, func(t *testing.T) {
 			result := android.GroupFixturePreparers(
 				android.PrepareForTestWithDefaults,
+				android.PrepareForTestWithArchMutator,
+				android.PrepareForTestWithAllowMissingDependencies,
+				cc.PrepareForTestWithCcDefaultModules,
 				PrepareForTestWithPythonBuildComponents,
 				d.mockFiles.AddToFixture(),
-			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
 				RunTest(t)
 
 			if len(result.Errs) > 0 {
@@ -346,17 +353,17 @@
 
 			for _, e := range d.expectedBinaries {
 				t.Run(e.name, func(t *testing.T) {
-					expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips)
+					expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
 				})
 			}
 		})
 	}
 }
 
-func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) {
+func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
 	module := ctx.ModuleForTests(name, variant)
 
-	base, baseOk := module.Module().(*Module)
+	base, baseOk := module.Module().(*PythonLibraryModule)
 	if !baseOk {
 		t.Fatalf("%s is not Python module!", name)
 	}
@@ -369,8 +376,6 @@
 	android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
 
 	android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
-
-	android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
 }
 
 func TestMain(m *testing.M) {
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
new file mode 100644
index 0000000..e12e7d2
--- /dev/null
+++ b/python/scripts/precompile_python.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# 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.
+
+import argparse
+import py_compile
+import os
+import shutil
+import tempfile
+import zipfile
+
+# This file needs to support both python 2 and 3.
+
+
+def process_one_file(name, inf, outzip):
+    if not name.endswith('.py'):
+        outzip.writestr(name, inf.read())
+        return
+
+    # Unfortunately py_compile requires the input/output files to be written
+    # out to disk.
+    with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+        shutil.copyfileobj(inf, tmp)
+        in_name = tmp.name
+    with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+        out_name = tmp.name
+    try:
+        py_compile.compile(in_name, out_name, name, doraise=True)
+        with open(out_name, 'rb') as f:
+            outzip.writestr(name + 'c', f.read())
+    finally:
+        os.remove(in_name)
+        os.remove(out_name)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('src_zip')
+    parser.add_argument('dst_zip')
+    args = parser.parse_args()
+
+    with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
+        with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
+            for name in inzip.namelist():
+                with inzip.open(name, mode='r') as inzipf:
+                    process_one_file(name, inzipf, outzip)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index 5eedc18..2d1bd4a 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -3,6 +3,7 @@
 import os
 import tempfile
 import shutil
+import signal
 import sys
 import subprocess
 import zipfile
@@ -43,7 +44,18 @@
     sys.stdout.flush()
     # close_fds=False so that you can run binaries with files provided on the command line:
     # my_python_app --file <(echo foo)
-    sys.exit(subprocess.call(args, close_fds=False))
+    p = subprocess.Popen(args, close_fds=False)
+
+    def handler(sig, frame):
+      p.send_signal(sig)
+
+    # Redirect SIGINT and SIGTERM to subprocess
+    signal.signal(signal.SIGINT, handler)
+    signal.signal(signal.SIGTERM, handler)
+
+    p.wait()
+
+    sys.exit(p.returncode)
   finally:
     shutil.rmtree(runfiles_path, ignore_errors=True)
 
diff --git a/python/test.go b/python/test.go
index fc5c211..31da17e 100644
--- a/python/test.go
+++ b/python/test.go
@@ -15,6 +15,8 @@
 package python
 
 import (
+	"fmt"
+
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -32,6 +34,20 @@
 	ctx.RegisterModuleType("python_test", PythonTestFactory)
 }
 
+func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule {
+	return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+}
+
+func PythonTestHostFactory() android.Module {
+	return NewTest(android.HostSupportedNoCross).init()
+}
+
+func PythonTestFactory() android.Module {
+	module := NewTest(android.HostAndDeviceSupported)
+	module.multilib = android.MultilibBoth
+	return module.init()
+}
+
 type TestProperties struct {
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
 	// installed with the module.
@@ -49,79 +65,124 @@
 	Java_data []string
 
 	// Test options.
-	Test_options android.CommonTestOptions
+	Test_options TestOptions
 }
 
-type testDecorator struct {
-	*binaryDecorator
+type TestOptions struct {
+	android.CommonTestOptions
+
+	// Runner for the test. Supports "tradefed" and "mobly" (for multi-device tests). Default is "tradefed".
+	Runner *string
+
+	// Metadata to describe the test configuration.
+	Metadata []Metadata
+}
+
+type Metadata struct {
+	Name  string
+	Value string
+}
+
+type PythonTestModule struct {
+	PythonBinaryModule
 
 	testProperties TestProperties
-
-	testConfig android.Path
-
-	data []android.DataPath
+	testConfig     android.Path
+	data           []android.DataPath
 }
 
-func (test *testDecorator) bootstrapperProps() []interface{} {
-	return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
+func (p *PythonTestModule) init() android.Module {
+	p.AddProperties(&p.properties, &p.protoProperties)
+	p.AddProperties(&p.binaryProperties)
+	p.AddProperties(&p.testProperties)
+	android.InitAndroidArchModule(p, p.hod, p.multilib)
+	android.InitDefaultableModule(p)
+	android.InitBazelModule(p)
+	if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+		p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+	}
+	return p
 }
 
-func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
-	test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
-		TestConfigProp:         test.testProperties.Test_config,
-		TestConfigTemplateProp: test.testProperties.Test_config_template,
-		TestSuites:             test.binaryDecorator.binaryProperties.Test_suites,
-		AutoGenConfig:          test.binaryDecorator.binaryProperties.Auto_gen_config,
-		DeviceTemplate:         "${PythonBinaryHostTestConfigTemplate}",
-		HostTemplate:           "${PythonBinaryHostTestConfigTemplate}",
-	})
+func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// We inherit from only the library's GenerateAndroidBuildActions, and then
+	// just use buildBinary() so that the binary is not installed into the location
+	// it would be for regular binaries.
+	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+	p.buildBinary(ctx)
 
-	test.binaryDecorator.pythonInstaller.dir = "nativetest"
-	test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
+	var configs []tradefed.Option
+	for _, metadata := range p.testProperties.Test_options.Metadata {
+		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: metadata.Name, Value: metadata.Value})
+	}
 
-	test.binaryDecorator.pythonInstaller.relative = ctx.ModuleName()
+	runner := proptools.StringDefault(p.testProperties.Test_options.Runner, "tradefed")
+	if runner == "tradefed" {
+		p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+			TestConfigProp:          p.testProperties.Test_config,
+			TestConfigTemplateProp:  p.testProperties.Test_config_template,
+			TestSuites:              p.binaryProperties.Test_suites,
+			OptionsForAutogenerated: configs,
+			AutoGenConfig:           p.binaryProperties.Auto_gen_config,
+			DeviceTemplate:          "${PythonBinaryHostTestConfigTemplate}",
+			HostTemplate:            "${PythonBinaryHostTestConfigTemplate}",
+		})
+	} else if runner == "mobly" {
+		if p.testProperties.Test_config != nil || p.testProperties.Test_config_template != nil || p.binaryProperties.Auto_gen_config != nil {
+			panic(fmt.Errorf("cannot set test_config, test_config_template or auto_gen_config for mobly test"))
+		}
 
-	test.binaryDecorator.pythonInstaller.install(ctx, file)
+		for _, testSuite := range p.binaryProperties.Test_suites {
+			if testSuite == "cts" {
+				configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: "cts"})
+				break
+			}
+		}
+		p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+			OptionsForAutogenerated: configs,
+			DeviceTemplate:          "${PythonBinaryHostMoblyTestConfigTemplate}",
+			HostTemplate:            "${PythonBinaryHostMoblyTestConfigTemplate}",
+		})
+	} else {
+		panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner))
+	}
 
-	dataSrcPaths := android.PathsForModuleSrc(ctx, test.testProperties.Data)
+	p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource)
 
-	for _, dataSrcPath := range dataSrcPaths {
-		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
+		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
 		for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
-			test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+			p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
 		}
 	}
 }
 
-func NewTest(hod android.HostOrDeviceSupported) *Module {
-	module, binary := NewBinary(hod)
-
-	binary.pythonInstaller = NewPythonInstaller("nativetest", "nativetest64")
-
-	test := &testDecorator{binaryDecorator: binary}
-	if hod == android.HostSupportedNoCross && test.testProperties.Test_options.Unit_test == nil {
-		test.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := p.PythonBinaryModule.AndroidMkEntries()
+	if len(entriesList) != 1 {
+		panic("Expected 1 entry")
 	}
+	entries := &entriesList[0]
 
-	module.bootstrapper = test
-	module.installer = test
+	entries.Class = "NATIVE_TESTS"
 
-	return module
-}
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			//entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+			if p.testConfig != nil {
+				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+			}
 
-func PythonTestHostFactory() android.Module {
-	module := NewTest(android.HostSupportedNoCross)
+			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
 
-	return module.init()
-}
+			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
 
-func PythonTestFactory() android.Module {
-	module := NewTest(android.HostAndDeviceSupported)
-	module.multilib = android.MultilibBoth
+			p.testProperties.Test_options.SetAndroidMkEntries(entries)
+		})
 
-	return module.init()
+	return entriesList
 }
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
index 56a5063..1e03f16 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -27,7 +27,10 @@
         failed = True
 
 assert_equal("__name__", __name__, "__main__")
-assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "par_test.py")
+fileName = os.path.basename(__file__)
+if fileName.endswith('.pyc'):
+    fileName = fileName[:-1]
+assert_equal("os.path.basename(__file__)", fileName, "par_test.py")
 
 archive = os.path.dirname(__file__)
 
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index ffad430..b513409 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -28,7 +28,10 @@
 archive = sys.modules["__main__"].__loader__.archive
 
 assert_equal("__name__", __name__, "testpkg.par_test")
-assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
+fileName = __file__
+if fileName.endswith('.pyc'):
+    fileName = fileName[:-1]
+assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
 
 # Python3 is returning None here for me, and I haven't found any problems caused by this.
 if sys.version_info[0] == 2:
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 32c746e..5e680b0 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -43,6 +43,10 @@
 	}
 }
 
+func (mod *Module) AndroidMkSuffix() string {
+	return mod.Properties.RustSubName + mod.Properties.SubName
+}
+
 func (mod *Module) AndroidMkEntries() []android.AndroidMkEntries {
 	if mod.Properties.HideFromMake || mod.hideApexVariantFromMake {
 
@@ -79,8 +83,7 @@
 		mod.SubAndroidMk(&ret, mod.sanitize)
 	}
 
-	ret.SubName += mod.Properties.RustSubName
-	ret.SubName += mod.Properties.SubName
+	ret.SubName += mod.AndroidMkSuffix()
 
 	return []android.AndroidMkEntries{ret}
 }
@@ -152,6 +155,11 @@
 		})
 }
 
+func (library *snapshotLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+	ctx.SubAndroidMk(ret, library.libraryDecorator)
+	ret.SubName = library.SnapshotAndroidMkSuffix()
+}
+
 func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
 	ctx.SubAndroidMk(ret, procMacro.baseCompiler)
 
@@ -205,8 +213,8 @@
 		})
 }
 
-func (fuzz *fuzzDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.SubAndroidMk(entries, fuzz.binaryDecorator)
+func (fuzz *fuzzDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+	ctx.SubAndroidMk(ret, fuzz.binaryDecorator)
 
 	var fuzzFiles []string
 	for _, d := range fuzz.fuzzPackagedModule.Corpus {
@@ -229,11 +237,14 @@
 			filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
+	ret.ExtraEntries = append(ret.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
 		entries *android.AndroidMkEntries) {
 		entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
 		if len(fuzzFiles) > 0 {
 			entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
 		}
+		if fuzz.installedSharedDeps != nil {
+			entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
+		}
 	})
 }
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 17d80dd..13fa81e 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -15,7 +15,6 @@
 package rust
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -30,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r468909b"
+	bindgenClangVersion = "clang-r487747"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -53,7 +52,7 @@
 		if ctx.Config().UseHostMusl() {
 			return "musl/lib/"
 		} else {
-			return "lib64/"
+			return "lib/"
 		}
 	})
 	_ = pctx.SourcePathVariable("bindgenClang",
@@ -178,10 +177,6 @@
 
 	if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" {
 		cflags = append(cflags, "-D__ANDROID_APEX__")
-		if ctx.Device() {
-			cflags = append(cflags, fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
-				ctx.RustModule().apexSdkVersion.FinalOrFutureInt()))
-		}
 	}
 
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -318,7 +313,7 @@
 
 func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
-	if ctx.toolchain().Bionic() {
+	if ctx.toolchain().Bionic() && !ctx.RustModule().compiler.noStdlibs() {
 		deps = bionicDeps(ctx, deps, false)
 	} else if ctx.Os() == android.LinuxMusl {
 		deps = muslDeps(ctx, deps, false)
diff --git a/rust/builder.go b/rust/builder.go
index 7dd9dd2..4b20e2b 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -134,6 +134,8 @@
 
 func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
 }
 
@@ -247,7 +249,13 @@
 	if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") {
 		incrementalPath := android.PathForOutput(ctx, "rustc").String()
 
-		rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath)
+		rustcFlags = append(rustcFlags, "-Cincremental="+incrementalPath)
+	}
+
+	// Disallow experimental features
+	modulePath := android.PathForModuleSrc(ctx).String()
+	if !(android.IsThirdPartyPath(modulePath) || strings.HasPrefix(modulePath, "prebuilts")) {
+		rustcFlags = append(rustcFlags, "-Zallow-features=\"default_alloc_error_handler,custom_inner_attributes,mixed_integer_ops\"")
 	}
 
 	// Collect linker flags
@@ -399,7 +407,7 @@
 	// Silence warnings about renamed lints for third-party crates
 	modulePath := android.PathForModuleSrc(ctx).String()
 	if android.IsThirdPartyPath(modulePath) {
-		rustdocFlags = append(rustdocFlags, " -A renamed_and_removed_lints")
+		rustdocFlags = append(rustdocFlags, " -A warnings")
 	}
 
 	// Yes, the same out directory is used simultaneously by all rustdoc builds.
diff --git a/rust/compiler.go b/rust/compiler.go
index 6055158..06ae12f 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"android/soong/cc"
 	"fmt"
 	"path/filepath"
 	"strings"
@@ -207,6 +208,10 @@
 	panic("baseCompiler does not implement SetDisabled()")
 }
 
+func (compiler *baseCompiler) noStdlibs() bool {
+	return Bool(compiler.Properties.No_stdlibs)
+}
+
 func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
 	panic("baseCompiler does not implement coverageOutputZipPath()")
 }
@@ -264,6 +269,11 @@
 func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
 	if ctx.RustModule().UseVndk() {
 		compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk")
+		if ctx.RustModule().InVendor() {
+			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor")
+		} else if ctx.RustModule().InProduct() {
+			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_product")
+		}
 	}
 
 	flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
@@ -307,19 +317,7 @@
 	flags.EmitXrefs = ctx.Config().EmitXrefRules()
 
 	if ctx.Host() && !ctx.Windows() {
-		rpathPrefix := `\$$ORIGIN/`
-		if ctx.Darwin() {
-			rpathPrefix = "@loader_path/"
-		}
-
-		var rpath string
-		if ctx.toolchain().Is64Bit() {
-			rpath = "lib64"
-		} else {
-			rpath = "lib"
-		}
-		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
-		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath)
+		flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
 	}
 
 	return flags
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 186e571..ae783e8 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -30,6 +30,7 @@
 		"armv8-a-branchprot": []string{},
 		"armv8-2a":           []string{},
 		"armv8-2a-dotprod":   []string{},
+		"armv9-a":            []string{},
 	}
 )
 
diff --git a/rust/config/global.go b/rust/config/global.go
index 81aec7e..8894938 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.64.0"
+	RustDefaultVersion = "1.68.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -51,6 +51,10 @@
 		// Use v0 mangling to distinguish from C++ symbols
 		"-C symbol-mangling-version=v0",
 		"--color always",
+		// TODO (b/267698452): Temporary workaround until the "no unstable
+		// features" policy is enforced.
+		"-A stable-features",
+		"-Zdylib-lto",
 	}
 
 	deviceGlobalRustFlags = []string{
diff --git a/rust/config/x86_linux_bionic_host.go b/rust/config/x86_linux_bionic_host.go
index b1a2c17..79c40ce 100644
--- a/rust/config/x86_linux_bionic_host.go
+++ b/rust/config/x86_linux_bionic_host.go
@@ -21,7 +21,9 @@
 )
 
 var (
-	LinuxBionicRustFlags     = []string{}
+	LinuxBionicRustFlags = []string{
+		"-C panic=abort",
+	}
 	LinuxBionicRustLinkFlags = []string{
 		"-B${cc_config.ClangBin}",
 		"-fuse-ld=lld",
diff --git a/rust/coverage.go b/rust/coverage.go
index 5ea481f..bc6504d 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -21,6 +21,7 @@
 )
 
 var CovLibraryName = "libprofile-clang-extras"
+var ProfilerBuiltins = "libprofiler_builtins.rust_sysroot"
 
 // Add '%c' to default specifier after we resolve http://b/210012154
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
@@ -41,6 +42,11 @@
 		ctx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
 		}, cc.CoverageDepTag, CovLibraryName)
+
+		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
+		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
+			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
+		}
 	}
 
 	return deps
@@ -60,6 +66,13 @@
 		flags.LinkFlags = append(flags.LinkFlags,
 			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+
+		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
+		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
+			profiler_builtins := ctx.GetDirectDepWithTag(ProfilerBuiltins, rlibDepTag).(*Module)
+			deps.RLibs = append(deps.RLibs, RustLibrary{Path: profiler_builtins.OutputFile().Path(), CrateName: profiler_builtins.CrateName()})
+		}
+
 		if cc.EnableContinuousCoverage(ctx) {
 			flags.RustFlags = append(flags.RustFlags, "-C llvm-args=--runtime-counter-relocation")
 			flags.LinkFlags = append(flags.LinkFlags, "-Wl,-mllvm,-runtime-counter-relocation")
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 6faf55c..d7e7ddf 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -16,8 +16,6 @@
 
 import (
 	"path/filepath"
-	"sort"
-	"strings"
 
 	"android/soong/android"
 	"android/soong/cc"
@@ -27,14 +25,14 @@
 
 func init() {
 	android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
-	android.RegisterSingletonType("rust_fuzz_packaging", rustFuzzPackagingFactory)
 }
 
 type fuzzDecorator struct {
 	*binaryDecorator
 
-	fuzzPackagedModule fuzz.FuzzPackagedModule
-	sharedLibraries    android.Paths
+	fuzzPackagedModule  fuzz.FuzzPackagedModule
+	sharedLibraries     android.Paths
+	installedSharedDeps []string
 }
 
 var _ compiler = (*fuzzDecorator)(nil)
@@ -64,9 +62,14 @@
 	flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
 
 	// `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
-	flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
 	flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
 
+	if ctx.InstallInVendor() {
+		flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
+	} else {
+		flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+
+	}
 	return flags
 }
 
@@ -88,10 +91,8 @@
 }
 
 func (fuzzer *fuzzDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
-	out := fuzzer.binaryDecorator.compile(ctx, flags, deps)
 
-	// Grab the list of required shared libraries.
-	fuzzer.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
+	out := fuzzer.binaryDecorator.compile(ctx, flags, deps)
 
 	return out
 }
@@ -104,83 +105,6 @@
 	return rlibAutoDep
 }
 
-// Responsible for generating GNU Make rules that package fuzz targets into
-// their architecture & target/host specific zip file.
-type rustFuzzPackager struct {
-	fuzz.FuzzPackager
-}
-
-func rustFuzzPackagingFactory() android.Singleton {
-	return &rustFuzzPackager{}
-}
-
-func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
-	// Map between each architecture + host/device combination.
-	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
-
-	// List of individual fuzz targets.
-	s.FuzzTargets = make(map[string]bool)
-
-	// Map tracking whether each shared library has an install rule to avoid duplicate install rules from
-	// multiple fuzzers that depend on the same shared library.
-	sharedLibraryInstalled := make(map[string]bool)
-
-	ctx.VisitAllModules(func(module android.Module) {
-		// Discard non-fuzz targets.
-		rustModule, ok := module.(*Module)
-		if !ok {
-			return
-		}
-
-		if ok := fuzz.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
-			return
-		}
-
-		fuzzModule, ok := rustModule.compiler.(*fuzzDecorator)
-		if !ok {
-			return
-		}
-
-		hostOrTargetString := "target"
-		if rustModule.Host() {
-			hostOrTargetString = "host"
-		}
-
-		archString := rustModule.Arch().ArchType.String()
-		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
-		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
-
-		var files []fuzz.FileToZip
-		builder := android.NewRuleBuilder(pctx, ctx)
-
-		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
-		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
-
-		// The executable.
-		files = append(files, fuzz.FileToZip{rustModule.UnstrippedOutputFile(), ""})
-
-		// Package shared libraries
-		files = append(files, cc.GetSharedLibsToZip(fuzzModule.sharedLibraries, rustModule, &s.FuzzPackager, archString, "lib", &sharedLibraryInstalled)...)
-
-		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
-		if !ok {
-			return
-		}
-
-	})
-	s.CreateFuzzPackage(ctx, archDirs, fuzz.Rust, pctx)
-}
-
-func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
-	packages := s.Packages.Strings()
-	sort.Strings(packages)
-
-	ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
-	// Preallocate the slice of fuzz targets to minimize memory allocations.
-	s.PreallocateSlice(ctx, "ALL_RUST_FUZZ_TARGETS")
-}
-
 func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
 	fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
@@ -188,13 +112,22 @@
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
 	fuzz.binaryDecorator.baseCompiler.install(ctx)
 
-	if fuzz.fuzzPackagedModule.FuzzProperties.Corpus != nil {
-		fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
-	}
-	if fuzz.fuzzPackagedModule.FuzzProperties.Data != nil {
-		fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
-	}
-	if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
-		fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
+	fuzz.fuzzPackagedModule = cc.PackageFuzzModule(ctx, fuzz.fuzzPackagedModule, pctx)
+
+	installBase := "fuzz"
+
+	// Grab the list of required shared libraries.
+	fuzz.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
+
+	for _, lib := range fuzz.sharedLibraries {
+		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+			cc.SharedLibraryInstallLocation(
+				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+
+		// Also add the dependency on the shared library symbols dir.
+		if !ctx.Host() {
+			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+				cc.SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+		}
 	}
 }
diff --git a/rust/image.go b/rust/image.go
index dfc7f74..50bf02a 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -129,6 +129,10 @@
 	return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
 }
 
+func (ctx *moduleContext) SystemExtSpecific() bool {
+	return ctx.ModuleContext.SystemExtSpecific()
+}
+
 // Returns true when this module creates a vendor variant and wants to install the vendor variant
 // to the odm partition.
 func (c *Module) VendorVariantToOdm() bool {
@@ -158,22 +162,15 @@
 }
 
 func (mod *Module) OnlyInRamdisk() bool {
-	// TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InstallInRamdisk()
 }
 
 func (mod *Module) OnlyInRecovery() bool {
-	// TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InstallInRecovery()
 }
 
 func (mod *Module) OnlyInVendorRamdisk() bool {
-	return false
-}
-
-func (mod *Module) OnlyInProduct() bool {
-	//TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InstallInVendorRamdisk()
 }
 
 // Returns true when this module is configured to have core and vendor variants.
@@ -226,10 +223,7 @@
 	// Rust does not support installing to the product image yet.
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 
-	if mctx.ProductSpecific() {
-		mctx.PropertyErrorf("product_specific",
-			"Rust modules do not yet support installing to the product image.")
-	} else if Bool(mod.VendorProperties.Double_loadable) {
+	if Bool(mod.VendorProperties.Double_loadable) {
 		mctx.PropertyErrorf("double_loadable",
 			"Rust modules do not yet support double loading")
 	}
@@ -243,6 +237,11 @@
 			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/image_test.go b/rust/image_test.go
index 95e788f..fb4d9c1 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -53,6 +53,7 @@
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_available: true,
+				product_available: true,
 			}
 		`)
 
@@ -61,6 +62,35 @@
 	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
 	}
+	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vendor'") {
+		t.Errorf("missing \"--cfg 'android_vendor'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+	}
+	if strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_product'") {
+		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+	}
+
+	product := ctx.ModuleForTests("libfoo", "android_product.29_arm64_armv8-a_static").Rule("rustc")
+	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
+		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	}
+	if strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vendor'") {
+		t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	}
+	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_product'") {
+		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	}
+
+	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc")
+	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
+		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
+	}
+	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vendor'") {
+		t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
+	}
+	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_product'") {
+		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo system variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	}
+
 }
 
 // Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
@@ -103,3 +133,93 @@
 		}
        `)
 }
+
+func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
+	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+	partitionDefined := false
+	checkPartition := func(specific bool, partition string) {
+		if specific {
+			if expected != partition && !partitionDefined {
+				// The variant is installed to the 'partition'
+				t.Errorf("%s variant of %q must not be installed to %s partition", variant, name, partition)
+			}
+			partitionDefined = true
+		} else {
+			// The variant is not installed to the 'partition'
+			if expected == partition {
+				t.Errorf("%s variant of %q must be installed to %s partition", variant, name, partition)
+			}
+		}
+	}
+	socSpecific := func(m *Module) bool {
+		return m.SocSpecific()
+	}
+	deviceSpecific := func(m *Module) bool {
+		return m.DeviceSpecific()
+	}
+	productSpecific := func(m *Module) bool {
+		return m.ProductSpecific() || m.productSpecificModuleContext()
+	}
+	systemExtSpecific := func(m *Module) bool {
+		return m.SystemExtSpecific()
+	}
+	checkPartition(socSpecific(mod), "vendor")
+	checkPartition(deviceSpecific(mod), "odm")
+	checkPartition(productSpecific(mod), "product")
+	checkPartition(systemExtSpecific(mod), "system_ext")
+	if !partitionDefined && expected != "system" {
+		t.Errorf("%s variant of %q is expected to be installed to %s partition,"+
+			" but installed to system partition", variant, name, expected)
+	}
+}
+
+func TestInstallPartition(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := testRust(t, `
+		rust_binary {
+			name: "sample_system",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+		}
+		rust_binary {
+			name: "sample_system_ext",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			system_ext_specific: true,
+		}
+		rust_binary {
+			name: "sample_product",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			product_specific: true,
+		}
+		rust_binary {
+			name: "sample_vendor",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			vendor: true,
+		}
+		rust_binary {
+			name: "sample_odm",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			device_specific: true,
+		}
+		rust_binary {
+			name: "sample_all_available",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			vendor_available: true,
+			product_available: true,
+		}
+	`)
+
+	checkInstallPartition(t, ctx, "sample_system", binaryCoreVariant, "system")
+	checkInstallPartition(t, ctx, "sample_system_ext", binaryCoreVariant, "system_ext")
+	checkInstallPartition(t, ctx, "sample_product", binaryProductVariant, "product")
+	checkInstallPartition(t, ctx, "sample_vendor", binaryVendorVariant, "vendor")
+	checkInstallPartition(t, ctx, "sample_odm", binaryVendorVariant, "odm")
+
+	checkInstallPartition(t, ctx, "sample_all_available", binaryCoreVariant, "system")
+}
diff --git a/rust/rust.go b/rust/rust.go
index 28a300b..018cdab 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -26,6 +26,7 @@
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
 	"android/soong/fuzz"
+	"android/soong/multitree"
 	"android/soong/rust/config"
 	"android/soong/snapshot"
 )
@@ -62,11 +63,11 @@
 }
 
 type BaseProperties struct {
-	AndroidMkRlibs         []string
-	AndroidMkDylibs        []string
-	AndroidMkProcMacroLibs []string
-	AndroidMkSharedLibs    []string
-	AndroidMkStaticLibs    []string
+	AndroidMkRlibs         []string `blueprint:"mutated"`
+	AndroidMkDylibs        []string `blueprint:"mutated"`
+	AndroidMkProcMacroLibs []string `blueprint:"mutated"`
+	AndroidMkSharedLibs    []string `blueprint:"mutated"`
+	AndroidMkStaticLibs    []string `blueprint:"mutated"`
 
 	ImageVariationPrefix string `blueprint:"mutated"`
 	VndkVersion          string `blueprint:"mutated"`
@@ -208,6 +209,11 @@
 			}
 			return android.Paths{}, nil
 		}
+	case "unstripped":
+		if mod.compiler != nil {
+			return android.PathsIfNonNil(mod.compiler.unstrippedOutputFilePath()), nil
+		}
+		return nil, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
@@ -485,6 +491,7 @@
 	SetDisabled()
 
 	stdLinkage(ctx *depsContext) RustLinkage
+	noStdlibs() bool
 
 	unstrippedOutputFilePath() android.Path
 	strippedOutputFilePath() android.OptionalPath
@@ -619,6 +626,31 @@
 	return false
 }
 
+func (mod *Module) IsFuzzModule() bool {
+	if _, ok := mod.compiler.(*fuzzDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (mod *Module) FuzzModuleStruct() fuzz.FuzzModule {
+	return mod.FuzzModule
+}
+
+func (mod *Module) FuzzPackagedModule() fuzz.FuzzPackagedModule {
+	if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
+		return fuzzer.fuzzPackagedModule
+	}
+	panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) FuzzSharedLibraries() android.Paths {
+	if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
+		return fuzzer.sharedLibraries
+	}
+	panic(fmt.Errorf("FuzzSharedLibraries called on non-fuzz module: %q", mod.BaseModuleName()))
+}
+
 func (mod *Module) UnstrippedOutputFile() android.Path {
 	if mod.compiler != nil {
 		return mod.compiler.unstrippedOutputFilePath()
@@ -1080,6 +1112,17 @@
 	return nil
 }
 
+func rustMakeLibName(ctx android.ModuleContext, c cc.LinkableInterface, dep cc.LinkableInterface, depName string) string {
+	if rustDep, ok := dep.(*Module); ok {
+		// Use base module name for snapshots when exporting to Makefile.
+		if snapshotPrebuilt, ok := rustDep.compiler.(cc.SnapshotInterface); ok {
+			baseName := rustDep.BaseModuleName()
+			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix() + rustDep.AndroidMkSuffix()
+		}
+	}
+	return cc.MakeLibName(ctx, c, dep, depName)
+}
+
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
@@ -1105,13 +1148,59 @@
 		mod.apexSdkVersion = android.FutureApiLevel
 	}
 
+	skipModuleList := map[string]bool{}
+
+	var apiImportInfo multitree.ApiImportInfo
+	hasApiImportInfo := false
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if dep.Name() == "api_imports" {
+			apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+			hasApiImportInfo = true
+		}
+	})
+
+	if hasApiImportInfo {
+		targetStubModuleList := map[string]string{}
+		targetOrigModuleList := map[string]string{}
+
+		// Search for dependency which both original module and API imported library with APEX stub exists
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			depName := ctx.OtherModuleName(dep)
+			if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
+				targetStubModuleList[apiLibrary] = depName
+			}
+		})
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			depName := ctx.OtherModuleName(dep)
+			if origLibrary, ok := targetStubModuleList[depName]; ok {
+				targetOrigModuleList[origLibrary] = depName
+			}
+		})
+
+		// Decide which library should be used between original and API imported library
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			depName := ctx.OtherModuleName(dep)
+			if apiLibrary, ok := targetOrigModuleList[depName]; ok {
+				if cc.ShouldUseStubForApex(ctx, dep) {
+					skipModuleList[depName] = true
+				} else {
+					skipModuleList[apiLibrary] = true
+				}
+			}
+		})
+	}
+
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
+		if _, exists := skipModuleList[depName]; exists {
+			return
+		}
 		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
 			//Handle Rust Modules
-			makeLibName := cc.MakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
+			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
 
 			switch depTag {
 			case dylibDepTag:
@@ -1364,6 +1453,16 @@
 	return strings.Split(filepath.String(), filepath.Base())[0]
 }
 
+// usePublicApi returns true if the rust variant should link against NDK (publicapi)
+func (r *Module) usePublicApi() bool {
+	return r.Device() && r.UseSdk()
+}
+
+// useVendorApi returns true if the rust variant should link against LLNDK (vendorapi)
+func (r *Module) useVendorApi() bool {
+	return r.Device() && (r.InVendor() || r.InProduct())
+}
+
 func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	ctx := &depsContext{
 		BottomUpMutatorContext: actx,
@@ -1374,8 +1473,10 @@
 	var snapshotInfo *cc.SnapshotInfo
 
 	apiImportInfo := cc.GetApiImports(mod, actx)
-	for idx, lib := range deps.SharedLibs {
-		deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs)
+	if mod.usePublicApi() || mod.useVendorApi() {
+		for idx, lib := range deps.SharedLibs {
+			deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs)
+		}
 	}
 
 	if ctx.Os() == android.Android {
@@ -1455,7 +1556,15 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
+		// For core variant, add a dep on the implementation (if it exists) and its .apiimport (if it exists)
+		// GenerateAndroidBuildActions will pick the correct impl/stub based on the api_domain boundary
+		if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
+			cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
+		}
+
+		if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
+			cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, apiLibraryName, version, false)
+		}
 	}
 
 	for _, lib := range deps.WholeStaticLibs {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 97bd541..3bcd58c 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -83,6 +83,10 @@
 	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"
 )
 
 func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 4f8d22b..3236bce 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -31,9 +31,8 @@
 	Properties SourceProviderProperties
 
 	// The first file in OutputFiles must be the library entry point.
-	OutputFiles      android.Paths
-	subAndroidMkOnce map[SubAndroidMkProvider]bool
-	subName          string
+	OutputFiles android.Paths
+	subName     string
 }
 
 var _ SourceProvider = (*BaseSourceProvider)(nil)
diff --git a/rust/test.go b/rust/test.go
index 4f922b4..4b5296e 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -200,6 +200,7 @@
 	if ctx.Device() {
 		flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
 	}
+
 	return flags
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index 4796f69..24ca3d6 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -104,6 +104,7 @@
 			crate_name: "std",
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
+			product_available: true,
 			host_supported: true,
 			vendor_available: true,
 			vendor_ramdisk_available: true,
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 7be0042..e1b3c86 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -424,6 +424,14 @@
 		compile_multilib: "32",
 		srcs: ["bin.rs"],
 	}
+
+	rust_library {
+		name: "librust_vendor_available",
+		crate_name: "rust_vendor",
+		vendor_available: true,
+		srcs: ["client.rs"],
+	}
+
 `
 
 	vndkBp := `
@@ -499,13 +507,6 @@
 		system_shared_libs: [],
 	}
 
-	rust_library {
-		name: "librust_vendor_available",
-		crate_name: "rust_vendor",
-		vendor_available: true,
-		srcs: ["client.rs"],
-	}
-
 	rust_ffi_shared {
 		name: "libclient",
 		crate_name: "client",
@@ -963,7 +964,7 @@
 	}
 
 	libclientAndroidMkRlibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkRlibs
-	if g, w := libclientAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+	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)
 	}
 
@@ -978,10 +979,24 @@
 	}
 
 	libclientRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibVariant).Module().(*Module).Properties.AndroidMkRlibs
-	if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+	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)
 	}
 
+	// 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"
+	if librustVendorSnapshotMkName != expectedRustVendorSnapshotName {
+		t.Errorf("Unexpected rust vendor snapshot name in AndroidMk: %q, expected: %q\n", librustVendorSnapshotMkName, expectedRustVendorSnapshotName)
+	}
+
+	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)
+	}
+
 	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
 	libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
 	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 5dd45cd..26fe432 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -191,6 +191,17 @@
     ],
 }
 
+python_test_host {
+    name: "conv_linker_config_test",
+    main: "conv_linker_config_test.py",
+    srcs: [
+        "conv_linker_config_test.py",
+        "conv_linker_config.py",
+    ],
+    libs: ["linker_config_proto"],
+    test_suites: ["general-tests"],
+}
+
 python_binary_host {
     name: "get_clang_version",
     main: "get_clang_version.py",
@@ -220,3 +231,8 @@
     srcs: ["rustfmt.toml"],
     visibility: ["//visibility:public"],
 }
+
+sh_binary_host {
+    name: "jars-to-module-info-java",
+    src: "jars-to-module-info-java.sh",
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
deleted file mode 100644
index 7b003fd..0000000
--- a/scripts/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
-per-file construct_context.py = ngeoffray@google.com,calin@google.com,skvadrik@google.com
-per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
-per-file gen_ndk*.sh,gen_java*.sh = sophiez@google.com, allenhair@google.com
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index b57963b..0d14019 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -19,7 +19,12 @@
     exit 1
 fi
 
-TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
+# TODO: remove ALLOW_MISSING_DEPENDENCIES=true when all the riscv64
+# dependencies exist (currently blocked by http://b/273792258).
+# TODO: remove BUILD_BROKEN_DISABLE_BAZEL=1 when bazel supports riscv64 (http://b/262192655).
+ALLOW_MISSING_DEPENDENCIES=true \
+BUILD_BROKEN_DISABLE_BAZEL=1 \
+    TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
 
 if [ -n "${DIST_DIR}" ]; then
     mkdir -p ${DIST_DIR} || true
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index a02c195..869fd3f 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -8,9 +8,11 @@
 java\.io
 java\.lang
 java\.lang\.annotation
+java\.lang\.constant
 java\.lang\.invoke
 java\.lang\.ref
 java\.lang\.reflect
+java\.lang\.runtime
 java\.math
 java\.net
 java\.nio
@@ -75,6 +77,7 @@
 jdk\.internal\.ref
 jdk\.internal\.reflect
 jdk\.internal\.util
+jdk\.internal\.util\.jar
 jdk\.internal\.vm\.annotation
 jdk\.net
 org\.w3c\.dom
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index e46efe4..3ac1b7e 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -19,6 +19,7 @@
 import collections
 import json
 import os
+import sys
 
 import linker_config_pb2 #pylint: disable=import-error
 from google.protobuf.descriptor import FieldDescriptor
@@ -26,14 +27,41 @@
 from google.protobuf.text_format import MessageToString
 
 
-def Proto(args):
+def LoadJsonMessage(path):
+    """
+    Loads a message from a .json file with `//` comments strippedfor convenience.
+    """
     json_content = ''
-    with open(args.source) as f:
+    with open(path) as f:
         for line in f:
             if not line.lstrip().startswith('//'):
                 json_content += line
     obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
-    pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
+    return ParseDict(obj, linker_config_pb2.LinkerConfig())
+
+
+def Proto(args):
+    """
+    Merges input json files (--source) into a protobuf message (--output).
+    Fails if the output file exists. Set --force or --append to deal with the existing
+    output file.
+    --force to overwrite the output file with the input (.json files).
+    --append to append the input to the output file.
+    """
+    pb = linker_config_pb2.LinkerConfig()
+    if os.path.isfile(args.output):
+        if args.force:
+            pass
+        elif args.append:
+            with open(args.output, 'rb') as f:
+                pb.ParseFromString(f.read())
+        else:
+            sys.stderr.write(f'Error: {args.output} exists. Use --force or --append.\n')
+            sys.exit(1)
+
+    if args.source:
+        for input in args.source.split(':'):
+            pb.MergeFrom(LoadJsonMessage(input))
     with open(args.output, 'wb') as f:
         f.write(pb.SerializeToString())
 
@@ -102,15 +130,26 @@
     parser_proto.add_argument(
         '-s',
         '--source',
-        required=True,
+        nargs='?',
         type=str,
-        help='Source linker configuration file in JSON.')
+        help='Colon-separated list of linker configuration files in JSON.')
     parser_proto.add_argument(
         '-o',
         '--output',
         required=True,
         type=str,
         help='Target path to create protobuf file.')
+    option_for_existing_output = parser_proto.add_mutually_exclusive_group()
+    option_for_existing_output.add_argument(
+        '-f',
+        '--force',
+        action='store_true',
+        help='Overwrite if the output file exists.')
+    option_for_existing_output.add_argument(
+        '-a',
+        '--append',
+        action='store_true',
+        help='Append the input to the output file if the output file exists.')
     parser_proto.set_defaults(func=Proto)
 
     print_proto = subparsers.add_parser(
@@ -192,8 +231,12 @@
 
 
 def main():
-    args = GetArgParser().parse_args()
-    args.func(args)
+    parser = GetArgParser()
+    args = parser.parse_args()
+    if 'func' in args:
+        args.func(args)
+    else:
+        parser.print_help()
 
 
 if __name__ == '__main__':
diff --git a/scripts/conv_linker_config_test.py b/scripts/conv_linker_config_test.py
new file mode 100644
index 0000000..d19a47b
--- /dev/null
+++ b/scripts/conv_linker_config_test.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2023 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.
+#
+"""Unit tests for conv_linker_config.py."""
+
+import io
+import os
+import shutil
+import tempfile
+import unittest
+
+import conv_linker_config
+from contextlib import redirect_stderr
+from linker_config_pb2 import LinkerConfig
+
+class FileArgs:
+  def __init__(self, files, sep = ':'):
+    self.files = files
+    self.sep = sep
+
+
+class FileArg:
+  def __init__(self, file):
+    self.file = file
+
+
+class TempDirTest(unittest.TestCase):
+
+  def setUp(self):
+    self.tempdir = tempfile.mkdtemp()
+
+
+  def tearDown(self):
+    shutil.rmtree(self.tempdir)
+
+
+  def write(self, name, contents):
+    with open(os.path.join(self.tempdir, name), 'wb') as f:
+      f.write(contents)
+
+
+  def read(self, name):
+    with open(os.path.join(self.tempdir, name), 'rb') as f:
+      return f.read()
+
+
+  def resolve_paths(self, args):
+    for i in range(len(args)):
+      if isinstance(args[i], FileArgs):
+        args[i] = args[i].sep.join(os.path.join(self.tempdir, f.file) for f in args[i].files)
+      elif isinstance(args[i], FileArg):
+        args[i] = os.path.join(self.tempdir, args[i].file)
+    return args
+
+
+class ConvLinkerConfigTest(TempDirTest):
+  """Unit tests for conv_linker_config."""
+
+
+  def test_Proto_empty_input(self):
+    self.command(['proto', '-s', '-o', FileArg('out.pb')])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertEqual(pb, LinkerConfig())
+
+
+  def test_Proto_single_input(self):
+    self.write('foo.json', b'{ "provideLibs": ["libfoo.so"]}')
+    self.command(['proto', '-s', FileArg('foo.json'), '-o', FileArg('out.pb')])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSequenceEqual(pb.provideLibs, ['libfoo.so'])
+
+
+  def test_Proto_with_multiple_input(self):
+    self.write('foo.json', b'{ "provideLibs": ["libfoo.so"]}')
+    self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+    self.command(['proto', '-s', FileArgs([FileArg('foo.json'), FileArg('bar.json')]), '-o', FileArg('out.pb')])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSetEqual(set(pb.provideLibs), set(['libfoo.so', 'libbar.so']))
+
+
+  def test_Proto_with_existing_output(self):
+    self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+    buf = io.StringIO()
+    with self.assertRaises(SystemExit) as err:
+      with redirect_stderr(buf):
+        self.command(['proto', '-o', FileArg('out.pb')])
+    self.assertEqual(err.exception.code, 1)
+    self.assertRegex(buf.getvalue(), r'.*out\.pb exists')
+
+
+  def test_Proto_with_append(self):
+    self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+    self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+    self.command(['proto', '-s', FileArg('bar.json'), '-o', FileArg('out.pb'), '-a'])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSetEqual(set(pb.provideLibs), set(['libfoo.so', 'libbar.so']))
+
+
+  def test_Proto_with_force(self):
+    self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+    self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+    self.command(['proto', '-s', FileArg('bar.json'), '-o', FileArg('out.pb'), '-f'])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSetEqual(set(pb.provideLibs), set(['libbar.so']))
+
+
+  def command(self, args):
+    parser = conv_linker_config.GetArgParser()
+    parsed_args = parser.parse_args(self.resolve_paths(args))
+    parsed_args.func(parsed_args)
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
index 3650fa1..2ff0c5f 100644
--- a/scripts/hiddenapi/signature_trie.py
+++ b/scripts/hiddenapi/signature_trie.py
@@ -150,10 +150,6 @@
                                 f"wildcard '{last_element}' and "
                                 f"member signature '{member[0]}'")
             wildcard = [last_element]
-        elif last_element.islower():
-            raise Exception(f"Invalid signature '{signature}': last element "
-                            f"'{last_element}' is lower case but should be an "
-                            f"upper case class name or wildcard")
         else:
             packages = elements[0:-1]
             # Split the class name into outer / inner classes
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
index 6d4e660..bd4a9a8 100755
--- a/scripts/hiddenapi/signature_trie_test.py
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -117,14 +117,6 @@
         self.assertEqual(elements, self.signature_to_elements(signature))
         self.assertEqual(signature, self.elements_to_signature(elements))
 
-    def test_invalid_no_class_or_wildcard(self):
-        signature = "java/lang"
-        with self.assertRaises(Exception) as context:
-            self.signature_to_elements(signature)
-        self.assertIn(
-            "last element 'lang' is lower case but should be an "
-            "upper case class name or wildcard", str(context.exception))
-
     def test_non_standard_class_name(self):
         elements = [
             ("package", "javax"),
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index 3dbc22e..07e01a1 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -31,6 +31,8 @@
 KNOWN_PREPARERS = ['com.android.tradefed.targetprep.TestAppInstallSetup',
                    'com.android.tradefed.targetprep.suite.SuiteApkInstaller']
 
+MAINLINE_CONTROLLER = 'com.android.tradefed.testtype.suite.module.MainlineTestModuleController'
+
 def parse_args():
   """Parse commandline arguments."""
 
@@ -41,6 +43,8 @@
                       help=('overwrite package fields in the test config'))
   parser.add_argument('--test-file-name', default='', dest='test_file_name',
                       help=('overwrite test file name in the test config'))
+  parser.add_argument('--mainline-package-name', default='', dest='mainline_package_name',
+                      help=('overwrite mainline module package name in the test config'))
   parser.add_argument('input', help='input test config file')
   parser.add_argument('output', help='output test config file')
   return parser.parse_args()
@@ -72,6 +76,16 @@
         if option.getAttribute('name') == "test-file-name":
           option.setAttribute('value', test_file_name)
 
+def overwrite_mainline_module_package_name(test_config_doc, mainline_package_name):
+
+  test_config = parse_test_config(test_config_doc)
+
+  for obj in get_children_with_tag(test_config, 'object'):
+    if obj.getAttribute('class') == MAINLINE_CONTROLLER:
+      for option in get_children_with_tag(obj, 'option'):
+        if option.getAttribute('name') == "mainline-module-package-name":
+          option.setAttribute('value', mainline_package_name)
+
 def main():
   """Program entry point."""
   try:
@@ -88,6 +102,9 @@
     if args.test_file_name:
       overwrite_test_file_name(doc, args.test_file_name)
 
+    if args.mainline_package_name:
+      overwrite_mainline_module_package_name(doc, args.mainline_package_name)
+
     with open(args.output, 'w') as f:
       write_xml(f, doc)
 
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
index 39ce5b3..699f91e 100644
--- a/scripts/test_config_fixer_test.py
+++ b/scripts/test_config_fixer_test.py
@@ -23,6 +23,8 @@
 
 import test_config_fixer
 
+from manifest import write_xml
+
 sys.dont_write_bytecode = True
 
 
@@ -117,5 +119,39 @@
     self.assertEqual(expected, output.getvalue())
 
 
+class OverwriteMainlineModulePackageNameTest(unittest.TestCase):
+  """ Unit tests for overwrite_mainline_module_package_name function """
+
+  test_config = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<configuration description="Runs some tests.">\n'
+      '    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">\n'
+      '        <option name="test-file-name" value="foo.apk"/>\n'
+      '    </target_preparer>\n'
+      '    <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+      '        <option name="package" value="com.android.foo"/>\n'
+      '        <option name="runtime-hint" value="20s"/>\n'
+      '    </test>\n'
+      '    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">\n'
+      '        <option name="enable" value="true"/>\n'
+      '        <option name="mainline-module-package-name" value="%s"/>\n'
+      '    </object>\n'
+      '</configuration>\n')
+
+  def test_testappinstallsetup(self):
+    doc = minidom.parseString(self.test_config % ("com.android.old.package.name"))
+
+    test_config_fixer.overwrite_mainline_module_package_name(doc, "com.android.new.package.name")
+    output = io.StringIO()
+    test_config_fixer.write_xml(output, doc)
+
+    # Only the mainline module package name should be updated. Format the xml
+    # with minidom first to avoid mismatches due to trivial reformatting.
+    expected = io.StringIO()
+    write_xml(expected, minidom.parseString(self.test_config % ("com.android.new.package.name")))
+    self.maxDiff = None
+    self.assertEqual(expected.getvalue(), output.getvalue())
+
+
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index d81635e..0d6496d 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -27,6 +27,11 @@
 // fixtureAddPlatformBootclasspathForBootclasspathFragment adds a platform_bootclasspath module that
 // references the bootclasspath fragment.
 func fixtureAddPlatformBootclasspathForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
+	return fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, fragment, "")
+}
+
+// fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra is the same as above, but also adds extra fragments.
+func fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, fragment, extraFragments string) android.FixturePreparer {
 	return android.GroupFixturePreparers(
 		// Add a platform_bootclasspath module.
 		android.FixtureAddTextFile("frameworks/base/boot/Android.bp", fmt.Sprintf(`
@@ -37,9 +42,10 @@
 						apex: "%s",
 						module: "%s",
 					},
+					%s
 				],
 			}
-		`, apex, fragment)),
+		`, apex, fragment, extraFragments)),
 		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
 		android.FixtureAddFile("frameworks/base/config/boot-image-profile.txt", nil),
 		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
@@ -79,9 +85,11 @@
 		}),
 
 		// Add a platform_bootclasspath that depends on the fragment.
-		fixtureAddPlatformBootclasspathForBootclasspathFragment("com.android.art", "mybootclasspathfragment"),
+		fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(
+			"com.android.art", "mybootclasspathfragment", java.ApexBootJarFragmentsForPlatformBootclasspath),
 
 		java.PrepareForBootImageConfigTest,
+		java.PrepareApexBootJarConfigsAndModules,
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
@@ -196,9 +204,15 @@
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
 			// Make sure that the boot jars package check rule includes the dex jars retrieved from the prebuilt apex.
 			checkBootJarsPackageCheckRule(t, result,
-				"out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
-				"out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
-				"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar")
+				append(
+					[]string{
+						"out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
+						"out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
+						"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
+					},
+					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")
 		}),
@@ -222,9 +236,15 @@
 
 	// Make sure that the boot jars package check rule includes the dex jars created from the source.
 	checkBootJarsPackageCheckRule(t, result,
-		"out/soong/.intermediates/core1/android_common_apex10000/aligned/core1.jar",
-		"out/soong/.intermediates/core2/android_common_apex10000/aligned/core2.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar")
+		append(
+			[]string{
+				"out/soong/.intermediates/core1/android_common_apex10000/aligned/core1.jar",
+				"out/soong/.intermediates/core2/android_common_apex10000/aligned/core2.jar",
+				"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
+			},
+			java.ApexBootJarDexJarPaths...,
+		)...,
+	)
 }
 
 // checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
@@ -819,6 +839,7 @@
 				compile_dex: true,
 				public: {enabled: true},
 				permitted_packages: ["mysdklibrary"],
+				min_sdk_version: "current",
 			}
 
 			java_sdk_library {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 2ade146..3a2ecc0 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -33,7 +33,8 @@
 
 	// Files needs by most of the tests.
 	android.MockFS{
-		"Test.java": nil,
+		"Test.java":   nil,
+		"art-profile": nil,
 	}.AddToFixture(),
 )
 
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 2a17cdc..66c44c8 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -62,6 +62,9 @@
 				min_sdk_version: "2",
 				compile_dex: true,
 				permitted_packages: ["mylib"],
+				dex_preopt: {
+					profile: "art-profile",
+				},
 			}
 
 			java_sdk_library {
@@ -71,6 +74,9 @@
 				shared_library: false,
 				public: {enabled: true},
 				min_sdk_version: "2",
+				dex_preopt: {
+					profile: "art-profile",
+				},
 			}
 		`),
 	).RunTest(t)
@@ -105,6 +111,9 @@
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     shared_library: false,
+    dex_preopt: {
+        profile_guided: true,
+    },
     public: {
         jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
         stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
@@ -122,6 +131,9 @@
     jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
     min_sdk_version: "2",
     permitted_packages: ["mylib"],
+    dex_preopt: {
+        profile_guided: true,
+    },
 }
 
 prebuilt_systemserverclasspath_fragment {
@@ -199,6 +211,54 @@
 `)
 	})
 
+	t.Run("target-u", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "UpsideDownCake", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    shared_library: false,
+    dex_preopt: {
+        profile_guided: true,
+    },
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_import {
+    name: "mylib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    min_sdk_version: "2",
+    permitted_packages: ["mylib"],
+    dex_preopt: {
+        profile_guided: true,
+    },
+}
+
+prebuilt_systemserverclasspath_fragment {
+    name: "mysystemserverclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    contents: [
+        "mylib",
+        "mysdklibrary",
+    ],
+}
+`)
+	})
+
 	t.Run("added-directly", func(t *testing.T) {
 		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
 	})
diff --git a/sdk/update.go b/sdk/update.go
index f50439c..d98ab68 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -357,6 +357,12 @@
 		// If the minApiLevel of the member is greater than the target API level then exclude it from
 		// this snapshot.
 		exclude := memberVariantDep.minApiLevel.GreaterThan(targetApiLevel)
+		// Always include host variants (e.g. host tools) in the snapshot.
+		// Host variants should not be guarded by a min_sdk_version check. In fact, host variants
+		// do not have a `min_sdk_version`.
+		if memberVariantDep.Host() {
+			exclude = false
+		}
 
 		addMember(name, export, exclude)
 
@@ -565,7 +571,7 @@
 	if m.deps != nil {
 		writeObjectPair("@deps", m.deps)
 	}
-	for _, k := range android.SortedStringKeys(m.memberSpecific) {
+	for _, k := range android.SortedKeys(m.memberSpecific) {
 		v := m.memberSpecific[k]
 		writeObjectPair(k, v)
 	}
@@ -626,7 +632,7 @@
 		getModuleInfo(memberVariantDep.variant)
 	}
 
-	for _, memberName := range android.SortedStringKeys(name2Info) {
+	for _, memberName := range android.SortedKeys(name2Info) {
 		info := name2Info[memberName]
 		modules = append(modules, info)
 	}
@@ -1263,6 +1269,11 @@
 	minApiLevel android.ApiLevel
 }
 
+// Host returns true if the sdk member is a host variant (e.g. host tool)
+func (s *sdkMemberVariantDep) Host() bool {
+	return s.variant.Target().Os.Class == android.Host
+}
+
 var _ android.SdkMember = (*sdkMember)(nil)
 
 // sdkMember groups all the variants of a specific member module together along with the name of the
@@ -1708,7 +1719,7 @@
 		}
 
 		// Create the image variant info in a fixed order.
-		for _, imageVariantName := range android.SortedStringKeys(variantsByImage) {
+		for _, imageVariantName := range android.SortedKeys(variantsByImage) {
 			variants := variantsByImage[imageVariantName]
 			archInfo.imageVariantInfos = append(archInfo.imageVariantInfos, newImageVariantSpecificInfo(ctx, imageVariantName, variantPropertiesFactory, variants))
 		}
diff --git a/soong_ui.bash b/soong_ui.bash
index 7bddb58..8e7cd19 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -14,9 +14,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# To track how long we took to startup. %N isn't supported on Darwin, but
-# that's detected in the Go code, which skips calculating the startup time.
-export TRACE_BEGIN_SOONG=$(date +%s%N)
+# To track how long we took to startup.
+case $(uname -s) in
+  Darwin)
+    export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
+    ;;
+  *)
+    export TRACE_BEGIN_SOONG=$(date +%s%N)
+    ;;
+esac
 
 source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh
 require_top
@@ -27,8 +33,8 @@
 source ${TOP}/build/soong/scripts/microfactory.bash
 
 soong_build_go soong_ui android/soong/cmd/soong_ui
-soong_build_go mk2rbc android/soong/mk2rbc/cmd
-soong_build_go rbcrun rbcrun/cmd
+soong_build_go mk2rbc android/soong/mk2rbc/mk2rbc
+soong_build_go rbcrun rbcrun/rbcrun
 
 cd ${TOP}
 exec "$(getoutdir)/soong_ui" "$@"
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
index 3e51fa1..064fc21 100644
--- a/starlark_fmt/format.go
+++ b/starlark_fmt/format.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 )
 
@@ -84,6 +85,16 @@
 	return PrintDict(formattedValueDict, indentLevel)
 }
 
+// PrintStringIntDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and int values.
+func PrintStringIntDict(dict map[string]int, indentLevel int) string {
+	valDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		valDict[k] = strconv.Itoa(v)
+	}
+	return PrintDict(valDict, indentLevel)
+}
+
 // PrintDict returns a starlark-compatible string containing a dictionary with string keys and
 // values printed with no additional formatting.
 func PrintDict(dict map[string]string, indentLevel int) string {
diff --git a/tests/apex_cc_module_arch_variant_tests.sh b/tests/apex_cc_module_arch_variant_tests.sh
index 97e6576..b0cade1 100755
--- a/tests/apex_cc_module_arch_variant_tests.sh
+++ b/tests/apex_cc_module_arch_variant_tests.sh
@@ -56,7 +56,7 @@
 
 # Number of CppCompile actions with arch variant flag
 actions_with_arch_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
-  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c "\-march=$ARCH_VARIANT_CFLAG")
+  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c \'-march=$ARCH_VARIANT_CFLAG\')
 
 # Number of all CppCompile actions
 all_cppcompile_actions_num=0
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 17b4419..fda5ca0 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -75,7 +75,6 @@
   grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
 }
 
-
 function test_add_android_bp() {
   setup
   run_soong
@@ -313,7 +312,6 @@
 
 }
 
-
 function test_add_file_to_soong_build() {
   setup
   run_soong
@@ -736,7 +734,6 @@
 
 }
 
-
 function test_bp2build_bazel_workspace_structure {
   setup
 
@@ -802,11 +799,10 @@
     || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
 }
 
-function test_bp2build_reports_multiple_errors {
+function test_bp2build_fails_fast {
   setup
 
   mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
-  touch a/a.txt
   cat > a/Android.bp <<EOF
 filegroup {
   name: "a",
@@ -816,7 +812,6 @@
 EOF
 
   mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
-  touch b/b.txt
   cat > b/Android.bp <<EOF
 filegroup {
   name: "b",
@@ -829,8 +824,8 @@
     fail "Build should have failed"
   fi
 
-  grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
-  grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
+  # we should expect at least one error
+  grep -q -E "(a|b)/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for ${GENERATED_BUILD_FILE_NAME} not found"
 }
 
 function test_bp2build_back_and_forth_null_build {
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 6477dac..68d7f8d 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -21,6 +21,68 @@
   fi
 }
 
+# Tests that, if bp2build reruns due to a blueprint file changing, that
+# BUILD files whose contents are unchanged are not regenerated.
+function test_bp2build_unchanged {
+  setup
+
+  mkdir -p pkg
+  touch pkg/x.txt
+  cat > pkg/Android.bp <<'EOF'
+filegroup {
+    name: "x",
+    srcs: ["x.txt"],
+    bazel_module: {bp2build_available: true},
+  }
+EOF
+
+  run_soong bp2build
+  local -r buildfile_mtime1=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
+  local -r marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+
+  # Force bp2build to rerun by updating the timestamp of a blueprint file.
+  touch pkg/Android.bp
+
+  run_soong bp2build
+  local -r buildfile_mtime2=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
+  local -r marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+
+  if [[ "$marker_mtime1" == "$marker_mtime2" ]]; then
+    fail "Expected bp2build marker file to change"
+  fi
+  if [[ "$buildfile_mtime1" != "$buildfile_mtime2" ]]; then
+    fail "BUILD.bazel was updated even though contents are same"
+  fi
+}
+
+# Tests that blueprint files that are deleted are not present when the
+# bp2build tree is regenerated.
+function test_bp2build_deleted_blueprint {
+  setup
+
+  mkdir -p pkg
+  touch pkg/x.txt
+  cat > pkg/Android.bp <<'EOF'
+filegroup {
+    name: "x",
+    srcs: ["x.txt"],
+    bazel_module: {bp2build_available: true},
+  }
+EOF
+
+  run_soong bp2build
+  if [[ ! -e "./out/soong/bp2build/pkg/BUILD.bazel" ]]; then
+    fail "Expected pkg/BUILD.bazel to be generated"
+  fi
+
+  rm pkg/Android.bp
+
+  run_soong bp2build
+  if [[ -e "./out/soong/bp2build/pkg/BUILD.bazel" ]]; then
+    fail "Expected pkg/BUILD.bazel to be deleted"
+  fi
+}
+
 function test_bp2build_null_build_with_globs {
   setup
 
@@ -140,7 +202,7 @@
   # NOTE: We don't actually use the extra BUILD file for anything here
   run_bazel build --config=android --config=bp2build --config=ci //foo/...
 
-  local the_answer_file="$(find -L bazel-out -name the_answer.txt)"
+  local -r the_answer_file="$(find -L bazel-out -name the_answer.txt)"
   if [[ ! -f "${the_answer_file}" ]]; then
     fail "Expected the_answer.txt to be generated, but was missing"
   fi
@@ -156,6 +218,49 @@
   eval "${_save_trap}"
 }
 
+function test_bp2build_symlinks_files {
+  setup
+  mkdir -p foo
+  touch foo/BLANK1
+  touch foo/BLANK2
+  touch foo/F2D
+  touch foo/BUILD
+
+  run_soong bp2build
+
+  if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
+    fail "./out/soong/workspace/foo/BUILD should be omitted"
+  fi
+  for file in BLANK1 BLANK2 F2D
+  do
+    if [[ ! -L "./out/soong/workspace/foo/$file" ]]; then
+      fail "./out/soong/workspace/foo/$file should exist"
+    fi
+  done
+  local -r BLANK1_BEFORE=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
+
+  rm foo/BLANK2
+  rm foo/F2D
+  mkdir foo/F2D
+  touch foo/F2D/BUILD
+
+  run_soong bp2build
+
+  if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
+    fail "./out/soong/workspace/foo/BUILD should be omitted"
+  fi
+  local -r BLANK1_AFTER=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
+  if [[ "$BLANK1_AFTER" != "$BLANK1_BEFORE" ]]; then
+    fail "./out/soong/workspace/foo/BLANK1 should be untouched"
+  fi
+  if [[  -e "./out/soong/workspace/foo/BLANK2" ]]; then
+    fail "./out/soong/workspace/foo/BLANK2 should be removed"
+  fi
+  if [[ -L "./out/soong/workspace/foo/F2D" ]] || [[ ! -d "./out/soong/workspace/foo/F2D" ]]; then
+    fail "./out/soong/workspace/foo/F2D should be a dir"
+  fi
+}
+
 function test_cc_correctness {
   setup
 
@@ -238,4 +343,29 @@
   run_bazel build --config=android --config=api_bp2build //:empty
 }
 
+# Verify that an *_api_contribution target can refer to an api file from
+# another Bazel package.
+function test_api_export_from_another_bazel_package() {
+  setup
+  # Parent dir Android.bp
+  mkdir -p foo
+  cat > foo/Android.bp << 'EOF'
+cc_library {
+  name: "libfoo",
+  stubs: {
+    symbol_file: "api/libfoo.map.txt",
+  },
+}
+EOF
+  # Child dir Android.bp
+  mkdir -p foo/api
+  cat > foo/api/Android.bp << 'EOF'
+package{}
+EOF
+  touch foo/api/libfoo.map.txt
+  # Run test
+  run_soong api_bp2build
+  run_bazel build --config=android --config=api_bp2build //foo:libfoo.contribution
+}
+
 scan_and_run_tests
diff --git a/tests/dcla_apex_comparison_test.sh b/tests/dcla_apex_comparison_test.sh
new file mode 100755
index 0000000..97ae97e
--- /dev/null
+++ b/tests/dcla_apex_comparison_test.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+
+# Copyright (C) 2023 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.
+
+set -euo pipefail
+
+# Soong/Bazel integration test to build the mainline modules in mixed build and
+# compare the DCLA libs extracted from those modules to ensure they are identical.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+  echo "$0 must be run from the top of the Android source tree."
+  exit 1
+fi
+
+TARGET_PRODUCTS=(
+  module_arm64
+  module_x86_64
+)
+
+MODULES=(
+  # These modules depend on the DCLA libs
+  com.android.adbd
+  com.android.art
+  com.android.art.debug
+  com.android.art.testing
+  com.android.btservices
+  com.android.conscrypt
+  com.android.i18n
+  com.android.media
+  com.android.media.swcodec
+  com.android.resolv
+  com.android.runtime
+  com.android.tethering
+)
+
+DCLA_LIBS=(
+  libbase.so
+  libc++.so
+  libcrypto.so
+  libcutils.so
+)
+
+if [[ -z ${OUT_DIR+x} ]]; then
+  OUT_DIR="out"
+fi
+
+if [[ -z ${ANDROID_HOST_OUT+x} ]]; then
+  export ANDROID_HOST_OUT="out/host/linux-x86"
+fi
+
+######################
+# Build deapexer and debugfs
+######################
+DEAPEXER="${ANDROID_HOST_OUT}/bin/deapexer"
+DEBUGFS="${ANDROID_HOST_OUT}/bin/debugfs"
+if [[ ! -f "${DEAPEXER}" ]] || [[ ! -f "${DEBUGFS}" ]]; then
+  build/soong/soong_ui.bash --make-mode --skip-soong-tests deapexer debugfs
+fi
+
+DEAPEXER="${DEAPEXER} --debugfs_path=${DEBUGFS}"
+
+############
+# Test Setup
+############
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+
+function cleanup {
+  rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+#######
+# Tests
+#######
+
+function extract_dcla_libs() {
+  local product=$1; shift
+  for module in "${MODULES[@]}"; do
+    local apex="${OUTPUT_DIR}/${product}/${module}.apex"
+    local extract_dir="${OUTPUT_DIR}/${product}/${module}/extract"
+
+    $DEAPEXER extract "${apex}" "${extract_dir}"
+  done
+}
+
+function compare_dcla_libs() {
+  local product=$1; shift
+
+  for lib in "${DCLA_LIBS[@]}"; do
+    for arch in lib lib64; do
+      local prev_sha=""
+      for module in "${MODULES[@]}"; do
+        local file="${OUTPUT_DIR}/${product}/${module}/extract/${arch}/${lib}"
+        if [[ ! -f "${file}" ]]; then
+          # not all libs are present in a module
+          echo "file doesn't exist: ${file}"
+          continue
+        fi
+        sha=$(sha1sum ${file})
+        sha="${sha% *}"
+        if [ "${prev_sha}" == "" ]; then
+          prev_sha="${sha}"
+        elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [ "${module}" != "com.android.tethering" ]; }; then
+          echo "Test failed, ${lib} has different hash value"
+          exit 1
+        fi
+      done
+    done
+  done
+}
+
+export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true # don't rely on prebuilts
+export TARGET_BUILD_APPS="${MODULES[@]}"
+for product in "${TARGET_PRODUCTS[@]}"; do
+  ###########
+  # Build the mainline modules
+  ###########
+  packages/modules/common/build/build_unbundled_mainline_module.sh \
+    --product "${product}" \
+    --dist_dir "${OUTPUT_DIR}/${product}"
+
+  extract_dcla_libs "${product}"
+  compare_dcla_libs "${product}"
+done
+
+echo "Test passed"
diff --git a/tests/lib.sh b/tests/lib.sh
index 26bdc97..2bcb630 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -94,6 +94,8 @@
   symlink_directory external/go-cmp
   symlink_directory external/golang-protobuf
   symlink_directory external/starlark-go
+  symlink_directory external/python
+  symlink_directory external/sqlite
 
   touch "$MOCK_TOP/Android.bp"
 }
@@ -162,5 +164,6 @@
   fi
   for f in ${test_fns[*]}; do
     $f
+    info "Completed test case \e[96;1m$f\e[0m"
   done
 }
diff --git a/tests/persistent_bazel_test.sh b/tests/persistent_bazel_test.sh
new file mode 100755
index 0000000..4e2982a
--- /dev/null
+++ b/tests/persistent_bazel_test.sh
@@ -0,0 +1,83 @@
+#!/bin/bash -eu
+
+# Copyright (C) 2023 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.
+
+set -o pipefail
+
+source "$(dirname "$0")/lib.sh"
+
+# This test verifies that adding USE_PERSISTENT_BAZEL creates a Bazel process
+# that outlasts the build process.
+# This test should only be run in sandboxed environments (because this test
+# verifies a Bazel process using global process list, and may spawn lingering
+# Bazel processes).
+function test_persistent_bazel {
+  setup
+
+  # Ensure no existing Bazel process.
+  if [[ -e out/bazel/output/server/server.pid.txt ]]; then
+    kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
+    if kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
+      fail "Error killing pre-setup bazel"
+    fi
+  fi
+
+  USE_PERSISTENT_BAZEL=1 run_soong nothing
+
+  if ! kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
+    fail "Persistent bazel process expected, but not found after first build"
+  fi
+  BAZEL_PID=$(cat out/bazel/output/server/server.pid.txt)
+
+  USE_PERSISTENT_BAZEL=1 run_soong nothing
+
+  if ! kill -0 $BAZEL_PID 2>/dev/null ; then
+    fail "Bazel pid $BAZEL_PID was killed after second build"
+  fi
+
+  kill $BAZEL_PID 2>/dev/null
+  if ! kill -0 $BAZEL_PID 2>/dev/null ; then
+    fail "Error killing bazel on shutdown"
+  fi
+}
+
+# Verifies that USE_PERSISTENT_BAZEL mode operates as expected in the event
+# that there are Bazel failures.
+function test_bazel_failure {
+  setup
+
+  # Ensure no existing Bazel process.
+  if [[ -e out/bazel/output/server/server.pid.txt ]]; then
+    kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
+    if kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
+      fail "Error killing pre-setup bazel"
+    fi
+  fi
+
+  # Introduce a syntax error in a BUILD file which is used in every build
+  # (Note this is a BUILD file which is copied as part of test setup, so this
+  # has no effect on sources outside of this test.
+  rm -rf  build/bazel/rules
+
+  USE_PERSISTENT_BAZEL=1 run_soong nothing 1>out/failurelog.txt 2>&1 && fail "Expected build failure" || true
+
+  if ! grep -sq "'build/bazel/rules' is not a package" out/failurelog.txt ; then
+    fail "Expected error to contain 'build/bazel/rules' is not a package, instead got:\n$(cat out/failurelog.txt)"
+  fi
+
+  kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
+}
+
+scan_and_run_tests
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index eb76a46..e1aa70c 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -7,6 +7,7 @@
 "$TOP/build/soong/tests/bootstrap_test.sh"
 "$TOP/build/soong/tests/mixed_mode_test.sh"
 "$TOP/build/soong/tests/bp2build_bazel_test.sh"
+"$TOP/build/soong/tests/persistent_bazel_test.sh"
 "$TOP/build/soong/tests/soong_test.sh"
 "$TOP/build/bazel/ci/rbc_regression_test.sh" aosp_arm64-userdebug
 
@@ -14,7 +15,10 @@
 # mock client.
 "$TOP/build/soong/tests/apex_comparison_tests.sh"
 "$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
-
+extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
-"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
\ No newline at end of file
+"$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"
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
new file mode 100755
index 0000000..6066d70
--- /dev/null
+++ b/tests/sbom_test.sh
@@ -0,0 +1,210 @@
+#!/bin/bash
+
+# Copyright (C) 2023 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.
+
+set -uo pipefail
+
+# Integration test for verifying generated SBOM for cuttlefish device.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+  echo "$0 must be run from the top of the Android source tree."
+  exit 1
+fi
+
+tmp_dir="$(mktemp -d tmp.XXXXXX)"
+function cleanup {
+  rm -rf "${tmp_dir}"
+}
+trap cleanup EXIT
+
+out_dir=$tmp_dir
+droid_target=droid
+
+debug=false
+if [ $debug = "true" ]; then
+  out_dir=out
+  droid_target=
+fi
+# m droid, build sbom later in case additional dependencies might be built and included in partition images.
+TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \
+  build/soong/soong_ui.bash --make-mode $droid_target dump.erofs
+
+product_out=$out_dir/target/product/vsoc_x86_64
+sbom_test=$product_out/sbom_test
+mkdir $sbom_test
+cp $product_out/*.img $sbom_test
+
+# m sbom
+TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \
+  build/soong/soong_ui.bash --make-mode sbom
+
+# Generate installed file list from .img files in PRODUCT_OUT
+dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
+
+declare -A diff_excludes
+diff_excludes[odm]="-I /odm/lib/modules"
+diff_excludes[vendor]=\
+"-I /vendor/lib64/libkeystore2_crypto.so \
+ -I /vendor/lib/modules \
+ -I /vendor/odm"
+diff_excludes[system]=\
+"-I /acct/ \
+ -I /adb_keys \
+ -I /apex/ \
+ -I /bin \
+ -I /bugreports \
+ -I /cache \
+ -I /config/ \
+ -I /d \
+ -I /data/ \
+ -I /data_mirror/ \
+ -I /debug_ramdisk/ \
+ -I /dev/ \
+ -I /etc \
+ -I /init \
+ -I /init.environ.rc \
+ -I /linkerconfig/ \
+ -I /metadata/ \
+ -I /mnt/ \
+ -I /odm/app \
+ -I /odm/bin \
+ -I /odm_dlkm/etc \
+ -I /odm/etc \
+ -I /odm/firmware \
+ -I /odm/framework \
+ -I /odm/lib \
+ -I /odm/lib64 \
+ -I /odm/overlay \
+ -I /odm/priv-app \
+ -I /odm/usr \
+ -I /oem/ \
+ -I /postinstall/ \
+ -I /proc/ \
+ -I /product/ \
+ -I /sdcard \
+ -I /second_stage_resources/ \
+ -I /storage/ \
+ -I /sys/ \
+ -I /system_dlkm/ \
+ -I /system_ext/ \
+ -I /system/lib64/android.hardware.confirmationui@1.0.so \
+ -I /system/lib64/android.hardware.confirmationui-V1-ndk.so \
+ -I /system/lib64/android.hardware.keymaster@4.1.so \
+ -I /system/lib64/android.hardware.security.rkp-V3-ndk.so \
+ -I /system/lib64/android.hardware.security.sharedsecret-V1-ndk.so \
+ -I /system/lib64/android.security.compat-ndk.so \
+ -I /system/lib64/libkeymaster4_1support.so \
+ -I /system/lib64/libkeymint.so \
+ -I /system/lib64/libkeystore2_aaid.so \
+ -I /system/lib64/libkeystore2_apc_compat.so \
+ -I /system/lib64/libkeystore2_crypto.so \
+ -I /system/lib64/libkm_compat_service.so \
+ -I /system/lib64/libkm_compat.so \
+ -I /system/lib64/vndk-29 \
+ -I /system/lib64/vndk-sp-29 \
+ -I /system/lib/modules \
+ -I /system/lib/vndk-29 \
+ -I /system/lib/vndk-sp-29 \
+ -I /system/product \
+ -I /system/system_ext \
+ -I /system/usr/icu \
+ -I /system/vendor \
+ -I /vendor/ \
+ -I /vendor_dlkm/etc"
+
+# Example output of dump.erofs is as below, and the data used in the test start
+# at line 11. Column 1 is inode id, column 2 is inode type and column 3 is name.
+# Each line is captured in variable "entry", sed is used to trim the leading
+# spaces and cut is used to get field 1 every time. Once a field is extracted,
+# "cut --complement" is used to remove the extracted field so next field can be
+# processed in the same way and to be processed field is always field 1.
+# Output of dump.erofs:
+#     File : /
+#     Size: 160  On-disk size: 160  directory
+#     NID: 39   Links: 10   Layout: 2   Compression ratio: 100.00%
+#     Inode size: 64   Extent size: 0   Xattr size: 16
+#     Uid: 0   Gid: 0  Access: 0755/rwxr-xr-x
+#     Timestamp: 2023-02-14 01:15:54.000000000
+#
+#            NID TYPE  FILENAME
+#             39    2  .
+#             39    2  ..
+#             47    2  app
+#        1286748    2  bin
+#        1286754    2  etc
+#        5304814    2  lib
+#        5309056    2  lib64
+#        5309130    2  media
+#        5388910    2  overlay
+#        5479537    2  priv-app
+EROFS_IMAGES="\
+  $sbom_test/product.img \
+  $sbom_test/system.img \
+  $sbom_test/system_ext.img \
+  $sbom_test/system_dlkm.img \
+  $sbom_test/system_other.img \
+  $sbom_test/odm.img \
+  $sbom_test/odm_dlkm.img \
+  $sbom_test/vendor.img \
+  $sbom_test/vendor_dlkm.img"
+for f in $EROFS_IMAGES; do
+  partition_name=$(basename $f | cut -d. -f1)
+  file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
+  files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+  rm "$file_list_file" > /dev/null 2>&1
+  all_dirs="/"
+  while [ ! -z "$all_dirs" ]; do
+    dir=$(echo "$all_dirs" | cut -d ' ' -f1)
+    all_dirs=$(echo "$all_dirs" | cut -d ' ' -f1 --complement -s)
+    entries=$($dump_erofs --ls --path "$dir" $f | tail -n +11)
+    while read -r entry; do
+      nid=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1)
+      entry=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1 --complement)
+      type=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1)
+      entry=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1 --complement)
+      name=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1)
+      case $type in
+        "2")  # directory
+          all_dirs=$(echo "$all_dirs $dir/$name" | sed 's/^\s*//')
+          ;;
+        *)
+          (
+          if [ "$partition_name" != "system" ]; then
+            # system partition is mounted to /, not to prepend partition name.
+            printf %s "/$partition_name"
+          fi
+          echo "$dir/$name" | sed 's#^//#/#'
+          ) >> "$file_list_file"
+          ;;
+      esac
+    done <<< "$entries"
+  done
+  sort -n -o "$file_list_file" "$file_list_file"
+
+  # Diff
+  echo ============ Diffing files in $f and SBOM
+  grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file"
+  exclude=
+  if [ -v 'diff_excludes[$partition_name]' ]; then
+    exclude=${diff_excludes[$partition_name]}
+  fi
+  diff "$file_list_file" "$files_in_spdx_file" $exclude
+  if [ $? != "0" ]; then
+    echo Found diffs in $f and SBOM.
+    exit 1
+  else
+    echo No diffs.
+  fi
+done
\ No newline at end of file
diff --git a/third_party/zip/writer.go b/third_party/zip/writer.go
index f526838..8a957e1 100644
--- a/third_party/zip/writer.go
+++ b/third_party/zip/writer.go
@@ -162,9 +162,17 @@
 		if records > uint16max {
 			records = uint16max
 		}
+		// Only store uint32max for the size and the offset if they don't fit.
+		// Robolectric currently doesn't support zip64 and fails to find the
+		// offset to the central directory when the number of files in the zip
+		// is larger than 2^16.
+		if size > uint32max {
+			size = uint32max
+		}
+		if offset > uint32max {
+			offset = uint32max
+		}
 		// END ANDROID CHANGE
-		size = uint32max
-		offset = uint32max
 	}
 
 	// write end record
diff --git a/tradefed/config.go b/tradefed/config.go
index 999424c..326a006 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -31,6 +31,7 @@
 	pctx.SourcePathVariable("NativeBenchmarkTestConfigTemplate", "build/make/core/native_benchmark_test_config_template.xml")
 	pctx.SourcePathVariable("NativeHostTestConfigTemplate", "build/make/core/native_host_test_config_template.xml")
 	pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
+	pctx.SourcePathVariable("PythonBinaryHostMoblyTestConfigTemplate", "build/make/core/python_binary_host_mobly_test_config_template.xml")
 	pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
 	pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
 	pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 7a8fca9..b79754c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -50,6 +50,7 @@
         "cleanbuild.go",
         "config.go",
         "context.go",
+        "staging_snapshot.go",
         "dumpvars.go",
         "environment.go",
         "exec.go",
@@ -70,10 +71,11 @@
         "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
+        "proc_sync_test.go",
         "rbe_test.go",
+        "staging_snapshot_test.go",
         "upload_test.go",
         "util_test.go",
-        "proc_sync_test.go",
     ],
     darwin: {
         srcs: [
diff --git a/ui/build/build.go b/ui/build/build.go
index d49a754..edc595d 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -102,9 +102,9 @@
 	// Whether to include the kati-generated ninja file in the combined ninja.
 	RunKatiNinja = 1 << iota
 	// Whether to run ninja on the combined ninja.
-	RunNinja      = 1 << iota
-	RunBuildTests = 1 << iota
-	RunAll        = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja
+	RunNinja       = 1 << iota
+	RunDistActions = 1 << iota
+	RunBuildTests  = 1 << iota
 )
 
 // checkBazelMode fails the build if there are conflicting arguments for which bazel
@@ -322,34 +322,42 @@
 
 		runNinjaForBuild(ctx, config)
 	}
+
+	if what&RunDistActions != 0 {
+		runDistActions(ctx, config)
+	}
 }
 
 func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int {
 	//evaluate what to run
-	what := RunAll
+	what := 0
 	if config.Checkbuild() {
 		what |= RunBuildTests
 	}
-	if config.SkipConfig() {
+	if !config.SkipConfig() {
+		what |= RunProductConfig
+	} else {
 		verboseln("Skipping Config as requested")
-		what = what &^ RunProductConfig
 	}
-	if config.SkipKati() {
-		verboseln("Skipping Kati as requested")
-		what = what &^ RunKati
-	}
-	if config.SkipKatiNinja() {
-		verboseln("Skipping use of Kati ninja as requested")
-		what = what &^ RunKatiNinja
-	}
-	if config.SkipSoong() {
+	if !config.SkipSoong() {
+		what |= RunSoong
+	} else {
 		verboseln("Skipping use of Soong as requested")
-		what = what &^ RunSoong
 	}
-
-	if config.SkipNinja() {
+	if !config.SkipKati() {
+		what |= RunKati
+	} else {
+		verboseln("Skipping Kati as requested")
+	}
+	if !config.SkipKatiNinja() {
+		what |= RunKatiNinja
+	} else {
+		verboseln("Skipping use of Kati ninja as requested")
+	}
+	if !config.SkipNinja() {
+		what |= RunNinja
+	} else {
 		verboseln("Skipping Ninja as requested")
-		what = what &^ RunNinja
 	}
 
 	if !config.SoongBuildInvocationNeeded() {
@@ -361,6 +369,11 @@
 		what = what &^ RunNinja
 		what = what &^ RunKati
 	}
+
+	if config.Dist() {
+		what |= RunDistActions
+	}
+
 	return what
 }
 
@@ -419,3 +432,9 @@
 		}
 	}()
 }
+
+// Actions to run on every build where 'dist' is in the actions.
+// Be careful, anything added here slows down EVERY CI build
+func runDistActions(ctx Context, config Config) {
+	runStagingSnapshot(ctx, config)
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index b928faa..20cc9fb 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -45,7 +45,8 @@
 )
 
 var (
-	rbeRandPrefix int
+	rbeRandPrefix             int
+	googleProdCredsExistCache bool
 )
 
 func init() {
@@ -77,6 +78,7 @@
 	queryview         bool
 	reportMkMetrics   bool // Collect and report mk2bp migration progress metrics.
 	soongDocs         bool
+	multitreeBuild    bool // This is a multitree build.
 	skipConfig        bool
 	skipKati          bool
 	skipKatiNinja     bool
@@ -116,8 +118,21 @@
 	bazelForceEnabledModules string
 
 	includeTags []string
+
+	// Data source to write ninja weight list
+	ninjaWeightListSource NinjaWeightListSource
 }
 
+type NinjaWeightListSource uint
+
+const (
+	// ninja doesn't use weight list.
+	NOT_USED NinjaWeightListSource = iota
+	// ninja uses weight list based on previous builds by ninja log
+	NINJA_LOG
+	// ninja thinks every task has the same weight.
+	EVENLY_DISTRIBUTED
+)
 const srcDirFileCheck = "build/soong/root.bp"
 
 var buildFiles = []string{"Android.mk", "Android.bp"}
@@ -147,8 +162,10 @@
 	}
 }
 
-// fetchEnvConfig optionally fetches environment config from an
-// experiments system to control Soong features dynamically.
+// fetchEnvConfig optionally fetches a configuration file that can then subsequently be
+// loaded into Soong environment to control certain aspects of build behavior (e.g., enabling RBE).
+// If a configuration file already exists on disk, the fetch is run in the background
+// so as to NOT block the rest of the build execution.
 func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
 	configName := envConfigName + "." + jsonSuffix
 	expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
@@ -174,8 +191,13 @@
 		return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
 	}
 
+	configExists := false
+	outConfigFilePath := filepath.Join(config.OutDir(), configName)
+	if _, err := os.Stat(outConfigFilePath); err == nil {
+		configExists = true
+	}
+
 	tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
-	defer cancel()
 	fetchStart := time.Now()
 	cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
 		"-output_config_name", configName)
@@ -185,22 +207,39 @@
 		return err
 	}
 
-	if err := cmd.Wait(); err != nil {
-		status := smpb.ExpConfigFetcher_ERROR
-		expConfigFetcher.Status = &status
-		return err
-	}
-	fetchEnd := time.Now()
-	expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
-	outConfigFilePath := filepath.Join(config.OutDir(), configName)
-	expConfigFetcher.Filename = proto.String(outConfigFilePath)
-	if _, err := os.Stat(outConfigFilePath); err == nil {
+	fetchCfg := func() error {
+		if err := cmd.Wait(); err != nil {
+			status := smpb.ExpConfigFetcher_ERROR
+			expConfigFetcher.Status = &status
+			return err
+		}
+		fetchEnd := time.Now()
+		expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
+		expConfigFetcher.Filename = proto.String(outConfigFilePath)
+
+		if _, err := os.Stat(outConfigFilePath); err != nil {
+			status := smpb.ExpConfigFetcher_NO_CONFIG
+			expConfigFetcher.Status = &status
+			return err
+		}
 		status := smpb.ExpConfigFetcher_CONFIG
 		expConfigFetcher.Status = &status
-	} else {
-		status := smpb.ExpConfigFetcher_NO_CONFIG
-		expConfigFetcher.Status = &status
+		return nil
 	}
+
+	// If a config file does not exist, wait for the config file to be fetched. Otherwise
+	// fetch the config file in the background and return immediately.
+	if !configExists {
+		defer cancel()
+		return fetchCfg()
+	}
+
+	go func() {
+		defer cancel()
+		if err := fetchCfg(); err != nil {
+			ctx.Verbosef("Failed to fetch config file %v: %v\n", configName, err)
+		}
+	}()
 	return nil
 }
 
@@ -300,8 +339,8 @@
 	if bc != "" {
 		if err := fetchEnvConfig(ctx, ret, bc); err != nil {
 			ctx.Verbosef("Failed to fetch config file: %v\n", err)
-
-		} else if err := loadEnvConfig(ctx, ret, bc); err != nil {
+		}
+		if err := loadEnvConfig(ctx, ret, bc); err != nil {
 			ctx.Fatalln("Failed to parse env config files: %v", err)
 		}
 	}
@@ -345,6 +384,7 @@
 		"CDPATH",
 		"DISPLAY",
 		"GREP_OPTIONS",
+		"JAVAC",
 		"NDK_ROOT",
 		"POSIXLY_CORRECT",
 
@@ -449,6 +489,10 @@
 	ret.environ.Set("ANDROID_JAVA11_HOME", java11Home)
 	ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
 
+	if ret.MultitreeBuild() {
+		ret.environ.Set("MULTITREE_BUILD", "true")
+	}
+
 	outDir := ret.OutDir()
 	buildDateTimeFile := filepath.Join(outDir, "build_date.txt")
 	if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" {
@@ -497,6 +541,17 @@
 	ctx.Metrics.SystemResourceInfo(s)
 }
 
+func getNinjaWeightListSourceInMetric(s NinjaWeightListSource) *smpb.BuildConfig_NinjaWeightListSource {
+	switch s {
+	case NINJA_LOG:
+		return smpb.BuildConfig_NINJA_LOG.Enum()
+	case EVENLY_DISTRIBUTED:
+		return smpb.BuildConfig_EVENLY_DISTRIBUTED.Enum()
+	default:
+		return smpb.BuildConfig_NOT_USED.Enum()
+	}
+}
+
 func buildConfig(config Config) *smpb.BuildConfig {
 	c := &smpb.BuildConfig{
 		ForceUseGoma:                proto.Bool(config.ForceUseGoma()),
@@ -504,6 +559,7 @@
 		UseRbe:                      proto.Bool(config.UseRBE()),
 		BazelMixedBuild:             proto.Bool(config.BazelBuildEnabled()),
 		ForceDisableBazelMixedBuild: proto.Bool(config.IsBazelMixedBuildForceDisabled()),
+		NinjaWeightListSource:       getNinjaWeightListSourceInMetric(config.NinjaWeightListSource()),
 	}
 	c.Targets = append(c.Targets, config.arguments...)
 
@@ -756,6 +812,8 @@
 			c.skipMetricsUpload = true
 		} else if arg == "--mk-metrics" {
 			c.reportMkMetrics = true
+		} else if arg == "--multitree-build" {
+			c.multitreeBuild = true
 		} else if arg == "--bazel-mode" {
 			c.bazelProdMode = true
 		} else if arg == "--bazel-mode-dev" {
@@ -764,6 +822,17 @@
 			c.bazelStagingMode = true
 		} else if arg == "--search-api-dir" {
 			c.searchApiDir = true
+		} else if strings.HasPrefix(arg, "--ninja_weight_source=") {
+			source := strings.TrimPrefix(arg, "--ninja_weight_source=")
+			if source == "ninja_log" {
+				c.ninjaWeightListSource = NINJA_LOG
+			} else if source == "evenly_distributed" {
+				c.ninjaWeightListSource = EVENLY_DISTRIBUTED
+			} else if source == "not_used" {
+				c.ninjaWeightListSource = NOT_USED
+			} else {
+				ctx.Fatalf("unknown option for ninja_weight_source: %s", source)
+			}
 		} else if strings.HasPrefix(arg, "--build-command=") {
 			buildCmd := strings.TrimPrefix(arg, "--build-command=")
 			// remove quotations
@@ -1054,6 +1123,14 @@
 	return c.verbose
 }
 
+func (c *configImpl) MultitreeBuild() bool {
+	return c.multitreeBuild
+}
+
+func (c *configImpl) NinjaWeightListSource() NinjaWeightListSource {
+	return c.ninjaWeightListSource
+}
+
 func (c *configImpl) SkipKati() bool {
 	return c.skipKati
 }
@@ -1322,9 +1399,13 @@
 // GoogleProdCredsExist determine whether credentials exist on the
 // Googler machine to use remote execution.
 func (c *configImpl) GoogleProdCredsExist() bool {
+	if googleProdCredsExistCache {
+		return googleProdCredsExistCache
+	}
 	if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
 		return false
 	}
+	googleProdCredsExistCache = true
 	return true
 }
 
@@ -1543,6 +1624,10 @@
 	return c.Environment().IsEnvTrue("BUILD_BROKEN_DISABLE_BAZEL")
 }
 
+func (c *configImpl) IsPersistentBazelEnabled() bool {
+	return c.Environment().IsEnvTrue("USE_PERSISTENT_BAZEL")
+}
+
 func (c *configImpl) BazelModulesForceEnabledByFlag() string {
 	return c.bazelForceEnabledModules
 }
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 940d85c..a1eccf0 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -903,6 +903,15 @@
 			tidyOnly:     "",
 			expectedArgs: []string{},
 		}, {
+			description:  "multitree build action executed at root directory",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			rootSymlink:  false,
+			args:         []string{"--multitree-build"},
+			curDir:       ".",
+			tidyOnly:     "",
+			expectedArgs: []string{"--multitree-build"},
+		}, {
 			description:  "build action executed at root directory in symlink",
 			dirsInTrees:  []string{},
 			buildFiles:   []string{},
@@ -1023,6 +1032,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(false),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1034,6 +1044,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(false),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1045,6 +1056,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(false),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1056,6 +1068,7 @@
 				UseRbe:                      proto.Bool(true),
 				BazelMixedBuild:             proto.Bool(false),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1067,6 +1080,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(false),
 				ForceDisableBazelMixedBuild: proto.Bool(true),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1079,6 +1093,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(false),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1091,6 +1106,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(true),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1103,6 +1119,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(true),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1115,6 +1132,7 @@
 				UseRbe:                      proto.Bool(false),
 				BazelMixedBuild:             proto.Bool(true),
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1129,6 +1147,7 @@
 				BazelMixedBuild:             proto.Bool(false),
 				Targets:                     []string{"droid", "dist"},
 				ForceDisableBazelMixedBuild: proto.Bool(false),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 		{
@@ -1147,6 +1166,7 @@
 				UseRbe:                      proto.Bool(true),
 				BazelMixedBuild:             proto.Bool(true),
 				ForceDisableBazelMixedBuild: proto.Bool(true),
+				NinjaWeightListSource:       smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
 	}
diff --git a/ui/build/context.go b/ui/build/context.go
index 2fef0d0..fd20e26 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -40,6 +40,8 @@
 
 	Thread tracer.Thread
 	Tracer tracer.Tracer
+
+	CriticalPath *status.CriticalPath
 }
 
 // BeginTrace starts a new Duration Event.
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 1e3e547..a9c298f 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -84,6 +84,14 @@
 	ctx.BeginTrace(metrics.RunKati, "dumpvars")
 	defer ctx.EndTrace()
 
+	tool := ctx.Status.StartTool()
+	if write_soong_vars {
+		// only print this when write_soong_vars is true so that it's not printed when using
+		// the get_build_var command.
+		tool.Status("Running product configuration...")
+	}
+	defer tool.Finish()
+
 	cmd := Command(ctx, config, "dumpvars",
 		config.PrebuiltBuildTool("ckati"),
 		"-f", "build/make/core/config.mk",
@@ -108,7 +116,7 @@
 	}
 	cmd.StartOrFatal()
 	// TODO: error out when Stderr contains any content
-	status.KatiReader(ctx.Status.StartTool(), pipe)
+	status.KatiReader(tool, pipe)
 	cmd.WaitOrFatal()
 
 	ret := make(map[string]string, len(vars))
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index dab1a9b..4734494 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -23,10 +23,59 @@
 	"strings"
 	"time"
 
+	"android/soong/shared"
 	"android/soong/ui/metrics"
 	"android/soong/ui/status"
 )
 
+const (
+	// File containing the environment state when ninja is executed
+	ninjaEnvFileName = "ninja.environment"
+	ninjaLogFileName = ".ninja_log"
+)
+
+func useNinjaBuildLog(ctx Context, config Config, cmd *Cmd) {
+	ninjaLogFile := filepath.Join(config.OutDir(), ninjaLogFileName)
+	data, err := os.ReadFile(ninjaLogFile)
+	var outputBuilder strings.Builder
+	if err == nil {
+		lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+		// ninja log: <start>	<end>	<restat>	<name>	<cmdhash>
+		// ninja weight list: <name>,<end-start+1>
+		for _, line := range lines {
+			if strings.HasPrefix(line, "#") {
+				continue
+			}
+			fields := strings.Split(line, "\t")
+			path := fields[3]
+			start, err := strconv.Atoi(fields[0])
+			if err != nil {
+				continue
+			}
+			end, err := strconv.Atoi(fields[1])
+			if err != nil {
+				continue
+			}
+			outputBuilder.WriteString(path)
+			outputBuilder.WriteString(",")
+			outputBuilder.WriteString(strconv.Itoa(end-start+1) + "\n")
+		}
+	} else {
+		// If there is no ninja log file, just pass empty ninja weight list.
+		// Because it is still efficient with critical path calculation logic even without weight.
+		ctx.Verbosef("There is an error during reading ninja log, so ninja will use empty weight list: %s", err)
+	}
+
+	weightListFile := filepath.Join(config.OutDir(), ".ninja_weight_list")
+
+	err = os.WriteFile(weightListFile, []byte(outputBuilder.String()), 0644)
+	if err == nil {
+		cmd.Args = append(cmd.Args, "-o", "usesweightlist="+weightListFile)
+	} else {
+		ctx.Panicf("Could not write ninja weight list file %s", err)
+	}
+}
+
 // Constructs and runs the Ninja command line with a restricted set of
 // environment variables. It's important to restrict the environment Ninja runs
 // for hermeticity reasons, and to avoid spurious rebuilds.
@@ -79,6 +128,14 @@
 		cmd.Environment.AppendFromKati(config.KatiEnvFile())
 	}
 
+	switch config.NinjaWeightListSource() {
+	case NINJA_LOG:
+		useNinjaBuildLog(ctx, config, cmd)
+	case EVENLY_DISTRIBUTED:
+		// pass empty weight list means ninja considers every tasks's weight as 1(default value).
+		cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
+	}
+
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
 	// used in the past to specify extra ninja arguments.
 	if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
@@ -186,6 +243,21 @@
 		ctx.Verbosef("  %s", envVar)
 	}
 
+	// Write the env vars available during ninja execution to a file
+	ninjaEnvVars := cmd.Environment.AsMap()
+	data, err := shared.EnvFileContents(ninjaEnvVars)
+	if err != nil {
+		ctx.Panicf("Could not parse environment variables for ninja run %s", err)
+	}
+	// Write the file in every single run. This is fine because
+	// 1. It is not a dep of Soong analysis, so will not retrigger Soong analysis.
+	// 2. Is is fairly lightweight (~1Kb)
+	ninjaEnvVarsFile := shared.JoinPath(config.SoongOutDir(), ninjaEnvFileName)
+	err = os.WriteFile(ninjaEnvVarsFile, data, 0666)
+	if err != nil {
+		ctx.Panicf("Could not write ninja environment file %s", err)
+	}
+
 	// Poll the Ninja log for updates regularly based on the heartbeat
 	// frequency. If it isn't updated enough, then we want to surface the
 	// possibility that Ninja is stuck, to the user.
@@ -194,7 +266,7 @@
 	ticker := time.NewTicker(ninjaHeartbeatDuration)
 	defer ticker.Stop()
 	ninjaChecker := &ninjaStucknessChecker{
-		logPath: filepath.Join(config.OutDir(), ".ninja_log"),
+		logPath: filepath.Join(config.OutDir(), ninjaLogFileName),
 	}
 	go func() {
 		for {
diff --git a/ui/build/path.go b/ui/build/path.go
index 86e61c0..29128d8 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -109,6 +109,15 @@
 	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
 	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
 
+	if value, _ := config.Environment().Get("BUILD_BROKEN_PYTHON_IS_PYTHON2"); value == "true" {
+		py2Path, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86/py2")
+		if info, err := os.Stat(py2Path); err == nil && info.IsDir() {
+			myPath = py2Path + string(os.PathListSeparator) + myPath
+		}
+	} else if value != "" {
+		ctx.Fatalf("BUILD_BROKEN_PYTHON_IS_PYTHON2 can only be set to 'true' or an empty string, but got %s\n", value)
+	}
+
 	// Set $PATH to be the directories containing the host tool symlinks, and
 	// the prebuilts directory for the current host OS.
 	config.Environment().Set("PATH", myPath)
@@ -244,6 +253,15 @@
 	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
 	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
 
+	if value, _ := config.Environment().Get("BUILD_BROKEN_PYTHON_IS_PYTHON2"); value == "true" {
+		py2Path, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86/py2")
+		if info, err := os.Stat(py2Path); err == nil && info.IsDir() {
+			myPath = py2Path + string(os.PathListSeparator) + myPath
+		}
+	} else if value != "" {
+		ctx.Fatalf("BUILD_BROKEN_PYTHON_IS_PYTHON2 can only be set to 'true' or an empty string, but got %s\n", value)
+	}
+
 	// Replace the $PATH variable with the path_interposer symlinks, and
 	// checked-in prebuilts.
 	config.Environment().Set("PATH", myPath)
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 3c844c1..1d17216 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -100,6 +100,8 @@
 	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 	defer ctx.EndTrace()
 
+	ctx.Status.Status("Starting rbe...")
+
 	if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
 		ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
 	}
@@ -180,6 +182,8 @@
 		return
 	}
 
+	ctx.Status.Status("Dumping rbe metrics...")
+
 	outputDir := config.rbeProxyLogsDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index e6543ec..871e637 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -21,6 +21,7 @@
 	"strconv"
 	"strings"
 
+	"android/soong/bazel"
 	"android/soong/ui/metrics"
 	"android/soong/ui/status"
 
@@ -167,6 +168,10 @@
 		commonArgs = append(commonArgs, "-t")
 	}
 
+	if pb.config.multitreeBuild {
+		commonArgs = append(commonArgs, "--multitree-build")
+	}
+
 	commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list"))
 	invocationEnv := make(map[string]string)
 	if pb.debugPort != "" {
@@ -268,9 +273,15 @@
 	if config.bazelStagingMode {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-staging")
 	}
+	if config.IsPersistentBazelEnabled() {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--use-bazel-proxy")
+	}
 	if len(config.bazelForceEnabledModules) > 0 {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-force-enabled-modules="+config.bazelForceEnabledModules)
 	}
+	if config.MultitreeBuild() {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--multitree-build")
+	}
 
 	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
 	// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
@@ -497,6 +508,12 @@
 		ctx.BeginTrace(metrics.RunSoong, name)
 		defer ctx.EndTrace()
 
+		if config.IsPersistentBazelEnabled() {
+			bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace"))
+			bazelProxy.Start()
+			defer bazelProxy.Close()
+		}
+
 		fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
 		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 		defer nr.Close()
diff --git a/ui/build/staging_snapshot.go b/ui/build/staging_snapshot.go
new file mode 100644
index 0000000..377aa64
--- /dev/null
+++ b/ui/build/staging_snapshot.go
@@ -0,0 +1,246 @@
+// 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 build
+
+import (
+	"crypto/sha1"
+	"encoding/hex"
+	"encoding/json"
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"android/soong/shared"
+	"android/soong/ui/metrics"
+)
+
+// Metadata about a staged file
+type fileEntry struct {
+	Name string      `json:"name"`
+	Mode fs.FileMode `json:"mode"`
+	Size int64       `json:"size"`
+	Sha1 string      `json:"sha1"`
+}
+
+func fileEntryEqual(a fileEntry, b fileEntry) bool {
+	return a.Name == b.Name && a.Mode == b.Mode && a.Size == b.Size && a.Sha1 == b.Sha1
+}
+
+func sha1_hash(filename string) (string, error) {
+	f, err := os.Open(filename)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	h := sha1.New()
+	if _, err := io.Copy(h, f); err != nil {
+		return "", err
+	}
+
+	return hex.EncodeToString(h.Sum(nil)), nil
+}
+
+// Subdirs of PRODUCT_OUT to scan
+var stagingSubdirs = []string{
+	"apex",
+	"cache",
+	"coverage",
+	"data",
+	"debug_ramdisk",
+	"fake_packages",
+	"installer",
+	"oem",
+	"product",
+	"ramdisk",
+	"recovery",
+	"root",
+	"sysloader",
+	"system",
+	"system_dlkm",
+	"system_ext",
+	"system_other",
+	"testcases",
+	"test_harness_ramdisk",
+	"vendor",
+	"vendor_debug_ramdisk",
+	"vendor_kernel_ramdisk",
+	"vendor_ramdisk",
+}
+
+// Return an array of stagedFileEntrys, one for each file in the staging directories inside
+// productOut
+func takeStagingSnapshot(ctx Context, productOut string, subdirs []string) ([]fileEntry, error) {
+	var outer_err error
+	if !strings.HasSuffix(productOut, "/") {
+		productOut += "/"
+	}
+	result := []fileEntry{}
+	for _, subdir := range subdirs {
+		filepath.WalkDir(productOut+subdir,
+			func(filename string, dirent fs.DirEntry, err error) error {
+				// Ignore errors. The most common one is that one of the subdirectories
+				// hasn't been built, in which case we just report it as empty.
+				if err != nil {
+					ctx.Verbosef("scanModifiedStagingOutputs error: %s", err)
+					return nil
+				}
+				if dirent.Type().IsRegular() {
+					fileInfo, _ := dirent.Info()
+					relative := strings.TrimPrefix(filename, productOut)
+					sha, err := sha1_hash(filename)
+					if err != nil {
+						outer_err = err
+					}
+					result = append(result, fileEntry{
+						Name: relative,
+						Mode: fileInfo.Mode(),
+						Size: fileInfo.Size(),
+						Sha1: sha,
+					})
+				}
+				return nil
+			})
+	}
+
+	sort.Slice(result, func(l, r int) bool { return result[l].Name < result[r].Name })
+
+	return result, outer_err
+}
+
+// Read json into an array of fileEntry. On error return empty array.
+func readJson(filename string) ([]fileEntry, error) {
+	buf, err := os.ReadFile(filename)
+	if err != nil {
+		// Not an error, just missing, which is empty.
+		return []fileEntry{}, nil
+	}
+
+	var result []fileEntry
+	err = json.Unmarshal(buf, &result)
+	if err != nil {
+		// Bad formatting. This is an error
+		return []fileEntry{}, err
+	}
+
+	return result, nil
+}
+
+// Write obj to filename.
+func writeJson(filename string, obj interface{}) error {
+	buf, err := json.MarshalIndent(obj, "", "  ")
+	if err != nil {
+		return err
+	}
+
+	return os.WriteFile(filename, buf, 0660)
+}
+
+type snapshotDiff struct {
+	Added   []string `json:"added"`
+	Changed []string `json:"changed"`
+	Removed []string `json:"removed"`
+}
+
+// Diff the two snapshots, returning a snapshotDiff.
+func diffSnapshots(previous []fileEntry, current []fileEntry) snapshotDiff {
+	result := snapshotDiff{
+		Added:   []string{},
+		Changed: []string{},
+		Removed: []string{},
+	}
+
+	found := make(map[string]bool)
+
+	prev := make(map[string]fileEntry)
+	for _, pre := range previous {
+		prev[pre.Name] = pre
+	}
+
+	for _, cur := range current {
+		pre, ok := prev[cur.Name]
+		found[cur.Name] = true
+		// Added
+		if !ok {
+			result.Added = append(result.Added, cur.Name)
+			continue
+		}
+		// Changed
+		if !fileEntryEqual(pre, cur) {
+			result.Changed = append(result.Changed, cur.Name)
+		}
+	}
+
+	// Removed
+	for _, pre := range previous {
+		if !found[pre.Name] {
+			result.Removed = append(result.Removed, pre.Name)
+		}
+	}
+
+	// Sort the results
+	sort.Strings(result.Added)
+	sort.Strings(result.Changed)
+	sort.Strings(result.Removed)
+
+	return result
+}
+
+// Write a json files to dist:
+//   - A list of which files have changed in this build.
+//
+// And record in out/soong:
+//   - A list of all files in the staging directories, including their hashes.
+func runStagingSnapshot(ctx Context, config Config) {
+	ctx.BeginTrace(metrics.RunSoong, "runStagingSnapshot")
+	defer ctx.EndTrace()
+
+	snapshotFilename := shared.JoinPath(config.SoongOutDir(), "staged_files.json")
+
+	// Read the existing snapshot file. If it doesn't exist, this is a full
+	// build, so all files will be treated as new.
+	previous, err := readJson(snapshotFilename)
+	if err != nil {
+		ctx.Fatal(err)
+		return
+	}
+
+	// Take a snapshot of the current out directory
+	current, err := takeStagingSnapshot(ctx, config.ProductOut(), stagingSubdirs)
+	if err != nil {
+		ctx.Fatal(err)
+		return
+	}
+
+	// Diff the snapshots
+	diff := diffSnapshots(previous, current)
+
+	// Write the diff (use RealDistDir, not one that might have been faked for bazel)
+	err = writeJson(shared.JoinPath(config.RealDistDir(), "modified_files.json"), diff)
+	if err != nil {
+		ctx.Fatal(err)
+		return
+	}
+
+	// Update the snapshot
+	err = writeJson(snapshotFilename, current)
+	if err != nil {
+		ctx.Fatal(err)
+		return
+	}
+}
diff --git a/ui/build/staging_snapshot_test.go b/ui/build/staging_snapshot_test.go
new file mode 100644
index 0000000..7ac5443
--- /dev/null
+++ b/ui/build/staging_snapshot_test.go
@@ -0,0 +1,188 @@
+// 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 build
+
+import (
+	"os"
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+func assertDeepEqual(t *testing.T, expected interface{}, actual interface{}) {
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("expected:\n  %#v\n actual:\n  %#v", expected, actual)
+	}
+}
+
+// Make a temp directory containing the supplied contents
+func makeTempDir(files []string, directories []string, symlinks []string) string {
+	temp, _ := os.MkdirTemp("", "soon_staging_snapshot_test_")
+
+	for _, file := range files {
+		os.MkdirAll(temp+"/"+filepath.Dir(file), 0700)
+		os.WriteFile(temp+"/"+file, []byte(file), 0600)
+	}
+
+	for _, dir := range directories {
+		os.MkdirAll(temp+"/"+dir, 0770)
+	}
+
+	for _, symlink := range symlinks {
+		os.MkdirAll(temp+"/"+filepath.Dir(symlink), 0770)
+		os.Symlink(temp, temp+"/"+symlink)
+	}
+
+	return temp
+}
+
+// If this is a clean build, we won't have any preexisting files, make sure we get back an empty
+// list and not errors.
+func TestEmptyOut(t *testing.T) {
+	ctx := testContext()
+
+	temp := makeTempDir(nil, nil, nil)
+	defer os.RemoveAll(temp)
+
+	actual, _ := takeStagingSnapshot(ctx, temp, []string{"a", "e", "g"})
+
+	expected := []fileEntry{}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure only the listed directories are picked up, and only regular files
+func TestNoExtraSubdirs(t *testing.T) {
+	ctx := testContext()
+
+	temp := makeTempDir([]string{"a/b", "a/c", "d", "e/f"}, []string{"g/h"}, []string{"e/symlink"})
+	defer os.RemoveAll(temp)
+
+	actual, _ := takeStagingSnapshot(ctx, temp, []string{"a", "e", "g"})
+
+	expected := []fileEntry{
+		{"a/b", 0600, 3, "3ec69c85a4ff96830024afeef2d4e512181c8f7b"},
+		{"a/c", 0600, 3, "592d70e4e03ee6f6780c71b0bf3b9608dbf1e201"},
+		{"e/f", 0600, 3, "9e164bef74aceede0974b857170100409efe67f1"},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles empty lists
+func TestDiffEmpty(t *testing.T) {
+	actual := diffSnapshots(nil, []fileEntry{})
+
+	expected := snapshotDiff{
+		Added:   []string{},
+		Changed: []string{},
+		Removed: []string{},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles adding
+func TestDiffAdd(t *testing.T) {
+	actual := diffSnapshots([]fileEntry{
+		{"a", 0600, 1, "1234"},
+	}, []fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 2, "5678"},
+	})
+
+	expected := snapshotDiff{
+		Added:   []string{"b"},
+		Changed: []string{},
+		Removed: []string{},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles changing mode
+func TestDiffChangeMode(t *testing.T) {
+	actual := diffSnapshots([]fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 2, "5678"},
+	}, []fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0600, 2, "5678"},
+	})
+
+	expected := snapshotDiff{
+		Added:   []string{},
+		Changed: []string{"b"},
+		Removed: []string{},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles changing size
+func TestDiffChangeSize(t *testing.T) {
+	actual := diffSnapshots([]fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 2, "5678"},
+	}, []fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 3, "5678"},
+	})
+
+	expected := snapshotDiff{
+		Added:   []string{},
+		Changed: []string{"b"},
+		Removed: []string{},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles changing contents
+func TestDiffChangeContents(t *testing.T) {
+	actual := diffSnapshots([]fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 2, "5678"},
+	}, []fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 2, "aaaa"},
+	})
+
+	expected := snapshotDiff{
+		Added:   []string{},
+		Changed: []string{"b"},
+		Removed: []string{},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
+
+// Make sure diff handles removing
+func TestDiffRemove(t *testing.T) {
+	actual := diffSnapshots([]fileEntry{
+		{"a", 0600, 1, "1234"},
+		{"b", 0700, 2, "5678"},
+	}, []fileEntry{
+		{"a", 0600, 1, "1234"},
+	})
+
+	expected := snapshotDiff{
+		Added:   []string{},
+		Changed: []string{},
+		Removed: []string{"b"},
+	}
+
+	assertDeepEqual(t, expected, actual)
+}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 9f14bdd..9959e6f 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,16 +18,21 @@
 // another.
 
 import (
+	"bufio"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strconv"
+	"strings"
 	"time"
 
+	"android/soong/shared"
 	"android/soong/ui/metrics"
 
 	"google.golang.org/protobuf/proto"
 
+	bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
 	upload_proto "android/soong/ui/metrics/upload_proto"
 )
 
@@ -73,12 +78,113 @@
 	return metricsFiles
 }
 
+func parseTimingToNanos(str string) int64 {
+	millisString := removeDecimalPoint(str)
+	timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+	return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+	percentageString := removeDecimalPoint(str)
+	//remove the % at the end of the string
+	percentage := strings.ReplaceAll(percentageString, "%", "")
+	percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+	return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+	// The format is always 0.425 or 10.425
+	return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+	words := strings.Fields(line)
+	timing := words[3]
+	return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+	words := strings.Fields(line)
+	getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+		// Sample lines include:
+		// Total launch phase time   0.011 s    2.59%
+		// Total target pattern evaluation phase time  0.011 s    2.59%
+		var beginning int
+		var end int
+		for ind, word := range words {
+			if word == "Total" {
+				beginning = ind + 1
+			} else if beginning > 0 && word == "phase" {
+				end = ind
+				break
+			}
+		}
+		phaseName := strings.Join(words[beginning:end], " ")
+
+		// end is now "phase" - advance by 2 for timing and 4 for percentage
+		percentageString := words[end+4]
+		timingString := words[end+2]
+		timing := parseTimingToNanos(timingString)
+		percentagePortion := parsePercentageToTenThousandths(percentageString)
+		return phaseName, timing, percentagePortion
+	}
+
+	phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+	phaseTiming := bazel_metrics_proto.PhaseTiming{}
+	phaseTiming.DurationNanos = &timing
+	phaseTiming.PortionOfBuildTime = &portion
+
+	phaseTiming.PhaseName = &phaseName
+	return phaseTiming
+}
+
+func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
+	if bazelProfileFile == "" {
+		return
+	}
+
+	readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+		//serialize the proto, write it
+		bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+		file, err := os.ReadFile(filepath)
+		if err != nil {
+			ctx.Fatalln("Error reading metrics file\n", err)
+		}
+
+		scanner := bufio.NewScanner(strings.NewReader(string(file)))
+		scanner.Split(bufio.ScanLines)
+
+		var phaseTimings []*bazel_metrics_proto.PhaseTiming
+		for scanner.Scan() {
+			line := scanner.Text()
+			if strings.HasPrefix(line, "Total run time") {
+				total := parseTotal(line)
+				bazelMetrics.Total = &total
+			} else if strings.HasPrefix(line, "Total") {
+				phaseTiming := parsePhaseTiming(line)
+				phaseTimings = append(phaseTimings, &phaseTiming)
+			}
+		}
+		bazelMetrics.PhaseTimings = phaseTimings
+
+		return bazelMetrics
+	}
+
+	if _, err := os.Stat(bazelProfileFile); err != nil {
+		// We can assume bazel didn't run if the profile doesn't exist
+		return
+	}
+	bazelProto := readBazelProto(bazelProfileFile)
+	shared.Save(&bazelProto, bazelMetricsFile)
+}
+
 // UploadMetrics uploads a set of metrics files to a server for analysis.
 // The metrics files are first copied to a temporary directory
 // and the uploader is then executed in the background to allow the user/system
 // to continue working. Soong communicates to the uploader through the
 // upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
@@ -88,6 +194,7 @@
 		return
 	}
 
+	processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
 	// Several of the files might be directories.
 	metricsFiles := pruneMetricsFiles(paths)
 	if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 764a1e1..58d9237 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -29,6 +29,30 @@
 	"android/soong/ui/logger"
 )
 
+func writeBazelProfileFile(dir string) error {
+	contents := `
+
+=== PHASE SUMMARY INFORMATION ===
+
+Total launch phase time                              1.193 s   15.77%
+Total init phase time                                1.092 s   14.44%
+Total target pattern evaluation phase time           0.580 s    7.67%
+Total interleaved loading-and-analysis phase time    3.646 s   48.21%
+Total preparation phase time                         0.022 s    0.30%
+Total execution phase time                           0.993 s   13.13%
+Total finish phase time                              0.036 s    0.48%
+---------------------------------------------------------------------
+Total run time                                       7.563 s  100.00%
+
+Critical path (178 ms):
+       Time Percentage   Description
+     178 ms  100.00%   action 'BazelWorkspaceStatusAction stable-status.txt'
+
+`
+	file := filepath.Join(dir, "bazel_metrics.txt")
+	return os.WriteFile(file, []byte(contents), 0666)
+}
+
 func TestPruneMetricsFiles(t *testing.T) {
 	rootDir := t.TempDir()
 
@@ -84,12 +108,12 @@
 	}, {
 		description: "non-existent metrics files no upload",
 		uploader:    "echo",
-		files:       []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+		files:       []string{"metrics_file_1", "metrics_file_2", "metrics_file_3, bazel_metrics.pb"},
 	}, {
 		description: "trigger upload",
 		uploader:    "echo",
 		createFiles: true,
-		files:       []string{"metrics_file_1", "metrics_file_2"},
+		files:       []string{"metrics_file_1", "metrics_file_2, bazel_metrics.pb"},
 	}}
 
 	for _, tt := range tests {
@@ -130,6 +154,9 @@
 					}
 				}
 			}
+			if err := writeBazelProfileFile(outDir); err != nil {
+				t.Fatalf("failed to create bazel profile file in dir: %v", outDir)
+			}
 
 			config := Config{&configImpl{
 				environ: &Environment{
@@ -139,7 +166,7 @@
 				metricsUploader: tt.uploader,
 			}}
 
-			UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
+			UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
 		})
 	}
 }
@@ -194,8 +221,79 @@
 				metricsUploader: "echo",
 			}}
 
-			UploadMetrics(ctx, config, true, time.Now(), metricsFile)
+			UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
 			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
 		})
 	}
 }
+
+func TestParsePercentageToTenThousandths(t *testing.T) {
+	// 2.59% should be returned as 259 - representing 259/10000 of the build
+	percentage := parsePercentageToTenThousandths("2.59%")
+	if percentage != 259 {
+		t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 259, have %d\n", percentage)
+	}
+
+	// Test without a leading digit
+	percentage = parsePercentageToTenThousandths(".52%")
+	if percentage != 52 {
+		t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 52, have %d\n", percentage)
+	}
+}
+
+func TestParseTimingToNanos(t *testing.T) {
+	// This parses from seconds (with millis precision) and returns nanos
+	timingNanos := parseTimingToNanos("0.111")
+	if timingNanos != 111000000 {
+		t.Errorf("Error parsing timing. Expected 111000, have %d\n", timingNanos)
+	}
+
+	// Test without a leading digit
+	timingNanos = parseTimingToNanos(".112")
+	if timingNanos != 112000000 {
+		t.Errorf("Error parsing timing. Expected 112000, have %d\n", timingNanos)
+	}
+}
+
+func TestParsePhaseTiming(t *testing.T) {
+	// Sample lines include:
+	// Total launch phase time   0.011 s    2.59%
+	// Total target pattern evaluation phase time  0.012 s    4.59%
+
+	line1 := "Total launch phase time   0.011 s    2.59%"
+	timing := parsePhaseTiming(line1)
+
+	if timing.GetPhaseName() != "launch" {
+		t.Errorf("Failed to parse phase name. Expected launch, have %s\n", timing.GetPhaseName())
+	} else if timing.GetDurationNanos() != 11000000 {
+		t.Errorf("Failed to parse duration nanos. Expected 11000000, have %d\n", timing.GetDurationNanos())
+	} else if timing.GetPortionOfBuildTime() != 259 {
+		t.Errorf("Failed to parse portion of build time. Expected 259, have %d\n", timing.GetPortionOfBuildTime())
+	}
+
+	// Test with a multiword phase name
+	line2 := "Total target pattern evaluation phase  time  0.012 s    4.59%"
+
+	timing = parsePhaseTiming(line2)
+	if timing.GetPhaseName() != "target pattern evaluation" {
+		t.Errorf("Failed to parse phase name. Expected target pattern evaluation, have %s\n", timing.GetPhaseName())
+	} else if timing.GetDurationNanos() != 12000000 {
+		t.Errorf("Failed to parse duration nanos. Expected 12000000, have %d\n", timing.GetDurationNanos())
+	} else if timing.GetPortionOfBuildTime() != 459 {
+		t.Errorf("Failed to parse portion of build time. Expected 459, have %d\n", timing.GetPortionOfBuildTime())
+	}
+}
+
+func TestParseTotal(t *testing.T) {
+	// Total line is in the form of:
+	// Total run time                                       7.563 s  100.00%
+
+	line := "Total run time                                       7.563 s  100.00%"
+
+	total := parseTotal(line)
+
+	// Only the seconds field is parsed, as nanos
+	if total != 7563000000 {
+		t.Errorf("Failed to parse total build time. Expected 7563000000, have %d\n", total)
+	}
+}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 2301c56..bd1517c 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -22,6 +22,7 @@
     deps: [
         "golang-protobuf-proto",
         "soong-ui-bp2build_metrics_proto",
+        "soong-ui-bazel_metrics_proto",
         "soong-ui-metrics_upload_proto",
         "soong-ui-metrics_proto",
         "soong-ui-mk_metrics_proto",
@@ -73,6 +74,18 @@
 }
 
 bootstrap_go_package {
+    name: "soong-ui-bazel_metrics_proto",
+    pkgPath: "android/soong/ui/metrics/bazel_metrics_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+    srcs: [
+        "bazel_metrics_proto/bazel_metrics.pb.go",
+    ],
+}
+
+bootstrap_go_package {
     name: "soong-ui-mk_metrics_proto",
     pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
     deps: [
diff --git a/ui/metrics/BUILD.bazel b/ui/metrics/BUILD.bazel
new file mode 100644
index 0000000..15ebb88
--- /dev/null
+++ b/ui/metrics/BUILD.bazel
@@ -0,0 +1,30 @@
+# Copyright (C) 2023 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.
+
+load("//build/bazel/rules/python:py_proto.bzl", "py_proto_library")
+
+py_proto_library(
+    name = "metrics-py-proto",
+    visibility = ["//build/bazel/scripts/incremental_build:__pkg__"],
+    deps = [":metrics-proto"],
+)
+
+proto_library(
+    name = "metrics-proto",
+    srcs = [
+        "bp2build_metrics_proto/bp2build_metrics.proto",
+        "metrics_proto/metrics.proto",
+    ],
+    strip_import_prefix = "",
+)
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 93f3471..4821043 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.9.1
+// 	protoc-gen-go v1.28.0
+// 	protoc        v3.21.7
 // source: bp2build_metrics.proto
 
 package bp2build_metrics_proto
@@ -45,6 +45,10 @@
 	HandCraftedModuleCount uint64 `protobuf:"varint,2,opt,name=handCraftedModuleCount,proto3" json:"handCraftedModuleCount,omitempty"`
 	// Total number of unconverted Soong modules
 	UnconvertedModuleCount uint64 `protobuf:"varint,3,opt,name=unconvertedModuleCount,proto3" json:"unconvertedModuleCount,omitempty"`
+	// Counts of symlinks in synthetic bazel workspace
+	WorkspaceSymlinkCount uint64 `protobuf:"varint,9,opt,name=workspaceSymlinkCount,proto3" json:"workspaceSymlinkCount,omitempty"`
+	// Counts of mkdir calls during creation of synthetic bazel workspace
+	WorkspaceMkDirCount uint64 `protobuf:"varint,10,opt,name=workspaceMkDirCount,proto3" json:"workspaceMkDirCount,omitempty"`
 	// Counts of generated Bazel targets per Bazel rule class
 	RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
 	// List of converted modules
@@ -111,6 +115,20 @@
 	return 0
 }
 
+func (x *Bp2BuildMetrics) GetWorkspaceSymlinkCount() uint64 {
+	if x != nil {
+		return x.WorkspaceSymlinkCount
+	}
+	return 0
+}
+
+func (x *Bp2BuildMetrics) GetWorkspaceMkDirCount() uint64 {
+	if x != nil {
+		return x.WorkspaceMkDirCount
+	}
+	return 0
+}
+
 func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
 	if x != nil {
 		return x.RuleClassCount
@@ -221,7 +239,7 @@
 	0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
 	0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
 	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xe9, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd1, 0x07, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
 	0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
 	0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
 	0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
@@ -232,60 +250,66 @@
 	0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76,
 	0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
 	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
-	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69,
-	0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43,
-	0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43,
-	0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
-	0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
-	0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
-	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75,
-	0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
-	0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
-	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
-	0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
-	0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32,
-	0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74,
-	0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
-	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06,
-	0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e,
-	0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c,
-	0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d,
+	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34,
+	0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69,
+	0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x77,
+	0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x6b, 0x44, 0x69,
+	0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69, 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c,
+	0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70,
+	0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75,
+	0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72,
+	0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e,
+	0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e,
+	0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01,
+	0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62,
+	0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+	0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
 	0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54,
-	0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74,
-	0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
-	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
-	0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-	0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
-	0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61,
-	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63,
+	0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
+	0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+	0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14,
+	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74,
+	0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f,
+	0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
+	0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+	0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
+	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+	0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76,
+	0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74,
+	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61,
+	0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54,
+	0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+	0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 19a7827..9ff4ae2 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -27,6 +27,12 @@
   // Total number of unconverted Soong modules
   uint64 unconvertedModuleCount = 3;
 
+  // Counts of symlinks in synthetic bazel workspace
+  uint64 workspaceSymlinkCount= 9;
+
+  // Counts of mkdir calls during creation of synthetic bazel workspace
+  uint64 workspaceMkDirCount= 10;
+
   // Counts of generated Bazel targets per Bazel rule class
   map<string, uint64> ruleClassCount = 4;
 
diff --git a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
index 356b188..f6c6df8 100644
--- a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
+++ b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
@@ -22,6 +22,6 @@
 
 py_proto_library(
     name = "bp2build_py_proto",
+    visibility = ["//build/bazel/scripts/bp2build_progress:__pkg__"],
     deps = [":bp2build_proto"],
-    visibility = ["//build/bazel/scripts/bp2build-progress:__pkg__"],
 )
diff --git a/ui/metrics/event.go b/ui/metrics/event.go
index b3a027e..cbdeb27 100644
--- a/ui/metrics/event.go
+++ b/ui/metrics/event.go
@@ -135,8 +135,8 @@
 	e := t.peek()
 	e.procResInfo = append(e.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
 		Name:             proto.String(name),
-		UserTimeMicros:   proto.Uint64(uint64(rusage.Utime.Usec)),
-		SystemTimeMicros: proto.Uint64(uint64(rusage.Stime.Usec)),
+		UserTimeMicros:   proto.Uint64(uint64(state.UserTime().Microseconds())),
+		SystemTimeMicros: proto.Uint64(uint64(state.SystemTime().Microseconds())),
 		MinorPageFaults:  proto.Uint64(uint64(rusage.Minflt)),
 		MajorPageFaults:  proto.Uint64(uint64(rusage.Majflt)),
 		// ru_inblock and ru_oublock are measured in blocks of 512 bytes.
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 717530c..82d11ed 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -39,6 +39,7 @@
 	"time"
 
 	"android/soong/shared"
+
 	"google.golang.org/protobuf/proto"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -125,6 +126,10 @@
 	}
 }
 
+func (m *Metrics) SetCriticalPathInfo(criticalPathInfo soong_metrics_proto.CriticalPathInfo) {
+	m.metrics.CriticalPathInfo = &criticalPathInfo
+}
+
 // SetFatalOrPanicMessage stores a non-zero exit and the relevant message in the latest event if
 // available or the metrics base.
 func (m *Metrics) SetFatalOrPanicMessage(errMsg string) {
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index eb8fdc4..4ad4a7b 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -158,6 +158,65 @@
 	return file_metrics_proto_rawDescGZIP(), []int{0, 1}
 }
 
+type BuildConfig_NinjaWeightListSource int32
+
+const (
+	BuildConfig_NOT_USED           BuildConfig_NinjaWeightListSource = 0
+	BuildConfig_NINJA_LOG          BuildConfig_NinjaWeightListSource = 1
+	BuildConfig_EVENLY_DISTRIBUTED BuildConfig_NinjaWeightListSource = 2
+)
+
+// Enum value maps for BuildConfig_NinjaWeightListSource.
+var (
+	BuildConfig_NinjaWeightListSource_name = map[int32]string{
+		0: "NOT_USED",
+		1: "NINJA_LOG",
+		2: "EVENLY_DISTRIBUTED",
+	}
+	BuildConfig_NinjaWeightListSource_value = map[string]int32{
+		"NOT_USED":           0,
+		"NINJA_LOG":          1,
+		"EVENLY_DISTRIBUTED": 2,
+	}
+)
+
+func (x BuildConfig_NinjaWeightListSource) Enum() *BuildConfig_NinjaWeightListSource {
+	p := new(BuildConfig_NinjaWeightListSource)
+	*p = x
+	return p
+}
+
+func (x BuildConfig_NinjaWeightListSource) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (BuildConfig_NinjaWeightListSource) Descriptor() protoreflect.EnumDescriptor {
+	return file_metrics_proto_enumTypes[2].Descriptor()
+}
+
+func (BuildConfig_NinjaWeightListSource) Type() protoreflect.EnumType {
+	return &file_metrics_proto_enumTypes[2]
+}
+
+func (x BuildConfig_NinjaWeightListSource) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *BuildConfig_NinjaWeightListSource) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = BuildConfig_NinjaWeightListSource(num)
+	return nil
+}
+
+// Deprecated: Use BuildConfig_NinjaWeightListSource.Descriptor instead.
+func (BuildConfig_NinjaWeightListSource) EnumDescriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{1, 0}
+}
+
 type ModuleTypeInfo_BuildSystem int32
 
 const (
@@ -191,11 +250,11 @@
 }
 
 func (ModuleTypeInfo_BuildSystem) Descriptor() protoreflect.EnumDescriptor {
-	return file_metrics_proto_enumTypes[2].Descriptor()
+	return file_metrics_proto_enumTypes[3].Descriptor()
 }
 
 func (ModuleTypeInfo_BuildSystem) Type() protoreflect.EnumType {
-	return &file_metrics_proto_enumTypes[2]
+	return &file_metrics_proto_enumTypes[3]
 }
 
 func (x ModuleTypeInfo_BuildSystem) Number() protoreflect.EnumNumber {
@@ -253,11 +312,11 @@
 }
 
 func (ExpConfigFetcher_ConfigStatus) Descriptor() protoreflect.EnumDescriptor {
-	return file_metrics_proto_enumTypes[3].Descriptor()
+	return file_metrics_proto_enumTypes[4].Descriptor()
 }
 
 func (ExpConfigFetcher_ConfigStatus) Type() protoreflect.EnumType {
-	return &file_metrics_proto_enumTypes[3]
+	return &file_metrics_proto_enumTypes[4]
 }
 
 func (x ExpConfigFetcher_ConfigStatus) Number() protoreflect.EnumNumber {
@@ -348,6 +407,13 @@
 	// The error message due to a non-zero exit _only_ if it did not occur in a
 	// recorded phase of the build.
 	ErrorMessage *string `protobuf:"bytes,30,opt,name=error_message,json=errorMessage" json:"error_message,omitempty"`
+	// The Git Manifest for the user's branch.
+	ManifestUrl *string `protobuf:"bytes,31,opt,name=manifest_url,json=manifestUrl" json:"manifest_url,omitempty"`
+	// The branch on which the build occurred.
+	// Example: refs/heads/master
+	Branch *string `protobuf:"bytes,32,opt,name=branch" json:"branch,omitempty"`
+	// The metric of critical path in build
+	CriticalPathInfo *CriticalPathInfo `protobuf:"bytes,33,opt,name=critical_path_info,json=criticalPathInfo" json:"critical_path_info,omitempty"`
 }
 
 // Default values for MetricsBase fields.
@@ -601,6 +667,27 @@
 	return ""
 }
 
+func (x *MetricsBase) GetManifestUrl() string {
+	if x != nil && x.ManifestUrl != nil {
+		return *x.ManifestUrl
+	}
+	return ""
+}
+
+func (x *MetricsBase) GetBranch() string {
+	if x != nil && x.Branch != nil {
+		return *x.Branch
+	}
+	return ""
+}
+
+func (x *MetricsBase) GetCriticalPathInfo() *CriticalPathInfo {
+	if x != nil {
+		return x.CriticalPathInfo
+	}
+	return nil
+}
+
 type BuildConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -619,8 +706,17 @@
 	Targets []string `protobuf:"bytes,6,rep,name=targets" json:"targets,omitempty"`
 	// Whether the user explicitly disabled bazel mixed builds for this build.
 	ForceDisableBazelMixedBuild *bool `protobuf:"varint,7,opt,name=force_disable_bazel_mixed_build,json=forceDisableBazelMixedBuild" json:"force_disable_bazel_mixed_build,omitempty"`
+	// NOT_USED - ninja doesn't use weight list.
+	// NINJA_LOG - ninja uses weight list based on previous builds by ninja log
+	// EVENLY_DISTRIBUTED - ninja thinks every task has the same weight.
+	NinjaWeightListSource *BuildConfig_NinjaWeightListSource `protobuf:"varint,8,opt,name=ninja_weight_list_source,json=ninjaWeightListSource,enum=soong_build_metrics.BuildConfig_NinjaWeightListSource,def=0" json:"ninja_weight_list_source,omitempty"`
 }
 
+// Default values for BuildConfig fields.
+const (
+	Default_BuildConfig_NinjaWeightListSource = BuildConfig_NOT_USED
+)
+
 func (x *BuildConfig) Reset() {
 	*x = BuildConfig{}
 	if protoimpl.UnsafeEnabled {
@@ -702,6 +798,13 @@
 	return false
 }
 
+func (x *BuildConfig) GetNinjaWeightListSource() BuildConfig_NinjaWeightListSource {
+	if x != nil && x.NinjaWeightListSource != nil {
+		return *x.NinjaWeightListSource
+	}
+	return Default_BuildConfig_NinjaWeightListSource
+}
+
 type SystemResourceInfo struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1409,12 +1512,146 @@
 	return nil
 }
 
+// CriticalPathInfo contains critical path nodes's information.
+// A critical path is a path determining the minimum time needed for the whole build given perfect parallelism.
+type CriticalPathInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Real time which the build system spent in microseconds
+	ElapsedTimeMicros *uint64 `protobuf:"varint,1,opt,name=elapsed_time_micros,json=elapsedTimeMicros" json:"elapsed_time_micros,omitempty"`
+	// The sum of execution time of the longest path from leave to the root in microseconds
+	CriticalPathTimeMicros *uint64 `protobuf:"varint,2,opt,name=critical_path_time_micros,json=criticalPathTimeMicros" json:"critical_path_time_micros,omitempty"`
+	// Detailed job information in a critical path.
+	CriticalPath []*JobInfo `protobuf:"bytes,4,rep,name=critical_path,json=criticalPath" json:"critical_path,omitempty"`
+	// Detailed job information for long running jobs (>30 seconds). These may or may not also be on a critical path.
+	LongRunningJobs []*JobInfo `protobuf:"bytes,5,rep,name=long_running_jobs,json=longRunningJobs" json:"long_running_jobs,omitempty"`
+}
+
+func (x *CriticalPathInfo) Reset() {
+	*x = CriticalPathInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CriticalPathInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriticalPathInfo) ProtoMessage() {}
+
+func (x *CriticalPathInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[11]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriticalPathInfo.ProtoReflect.Descriptor instead.
+func (*CriticalPathInfo) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *CriticalPathInfo) GetElapsedTimeMicros() uint64 {
+	if x != nil && x.ElapsedTimeMicros != nil {
+		return *x.ElapsedTimeMicros
+	}
+	return 0
+}
+
+func (x *CriticalPathInfo) GetCriticalPathTimeMicros() uint64 {
+	if x != nil && x.CriticalPathTimeMicros != nil {
+		return *x.CriticalPathTimeMicros
+	}
+	return 0
+}
+
+func (x *CriticalPathInfo) GetCriticalPath() []*JobInfo {
+	if x != nil {
+		return x.CriticalPath
+	}
+	return nil
+}
+
+func (x *CriticalPathInfo) GetLongRunningJobs() []*JobInfo {
+	if x != nil {
+		return x.LongRunningJobs
+	}
+	return nil
+}
+
+type JobInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Real time which a job spent in microseconds
+	ElapsedTimeMicros *uint64 `protobuf:"varint,1,opt,name=elapsed_time_micros,json=elapsedTimeMicros" json:"elapsed_time_micros,omitempty"`
+	// Description of a job
+	JobDescription *string `protobuf:"bytes,2,opt,name=job_description,json=jobDescription" json:"job_description,omitempty"`
+}
+
+func (x *JobInfo) Reset() {
+	*x = JobInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[12]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *JobInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JobInfo) ProtoMessage() {}
+
+func (x *JobInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[12]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use JobInfo.ProtoReflect.Descriptor instead.
+func (*JobInfo) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *JobInfo) GetElapsedTimeMicros() uint64 {
+	if x != nil && x.ElapsedTimeMicros != nil {
+		return *x.ElapsedTimeMicros
+	}
+	return 0
+}
+
+func (x *JobInfo) GetJobDescription() string {
+	if x != nil && x.JobDescription != nil {
+		return *x.JobDescription
+	}
+	return ""
+}
+
 var File_metrics_proto protoreflect.FileDescriptor
 
 var file_metrics_proto_rawDesc = []byte{
 	0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 	0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x22, 0xfa, 0x0d, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0x8a, 0x0f, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
 	0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
 	0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
@@ -1519,14 +1756,23 @@
 	0x0b, 0x6e, 0x6f, 0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d,
 	0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x1e, 0x20,
 	0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
-	0x65, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e,
+	0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x5f, 0x75, 0x72,
+	0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
+	0x74, 0x55, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x20,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x53, 0x0a, 0x12,
+	0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x69, 0x6e,
+	0x66, 0x6f, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43,
+	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+	0x10, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66,
+	0x6f, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e,
 	0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55,
 	0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e,
 	0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55,
 	0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10,
 	0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03,
 	0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x10,
-	0x04, 0x22, 0x99, 0x02, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x04, 0x22, 0xe2, 0x03, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
 	0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20,
 	0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07,
 	0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75,
@@ -1543,137 +1789,173 @@
 	0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d,
 	0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08,
 	0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61,
-	0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x22, 0x6f, 0x0a,
-	0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79,
-	0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61,
-	0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c,
-	0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
-	0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0xca,
-	0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65,
-	0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a,
-	0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
-	0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65,
-	0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f,
-	0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e,
-	0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x65,
-	0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x6e, 0x5a, 0x65,
-	0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f,
-	0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65,
-	0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xb9, 0x03, 0x0a, 0x13,
-	0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f,
-	0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f,
-	0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65,
-	0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73,
-	0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12,
-	0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a,
-	0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c,
-	0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50,
-	0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a,
-	0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46,
-	0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75,
-	0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e,
-	0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70,
-	0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f,
-	0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e,
-	0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69,
-	0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c,
-	0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69,
-	0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e,
-	0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69,
-	0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76,
-	0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53,
-	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
-	0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70,
-	0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65,
-	0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f,
-	0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f,
-	0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d,
-	0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f,
-	0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a,
-	0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f,
-	0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22,
-	0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a,
-	0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a,
-	0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75,
-	0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04,
-	0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f,
+	0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x79, 0x0a,
+	0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69,
+	0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73,
+	0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45,
+	0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69,
+	0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x4c, 0x0a, 0x15, 0x4e, 0x69, 0x6e, 0x6a,
+	0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10, 0x00, 0x12,
+	0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x16,
+	0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42,
+	0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15,
+	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d,
+	0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74,
+	0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
+	0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70,
+	0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,
+	0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0xca, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66,
+	0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74,
+	0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09,
+	0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61,
+	0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65,
+	0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
+	0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09,
+	0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f,
+	0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
+	0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f,
 	0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75,
-	0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a,
-	0x73, 0x22, 0xcc, 0x02, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a,
-	0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75,
-	0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41,
-	0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74,
-	0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53,
-	0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f,
-	0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48,
-	0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74,
-	0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65,
-	0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x50,
-	0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x69,
-	0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+	0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e,
+	0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12,
+	0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+	0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73,
+	0x73, 0x61, 0x67, 0x65, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69,
+	0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72,
+	0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69,
+	0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f,
+	0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61,
+	0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f,
+	0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c,
+	0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65,
+	0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d,
+	0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e,
+	0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20,
+	0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62,
+	0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43,
+	0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40,
+	0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72,
+	0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73,
+	0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49,
+	0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
 	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
-	0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52,
-	0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f,
-	0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65,
-	0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78, 0x70, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a,
-	0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6d,
-	0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53,
-	0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x46,
-	0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01,
-	0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4d,
-	0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 0x22, 0x91,
-	0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e,
-	0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
-	0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
-	0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f,
-	0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42,
+	0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e,
+	0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70,
+	0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57,
+	0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08,
+	0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74,
+	0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
+	0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
+	0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0xcc, 0x02, 0x0a, 0x11, 0x53,
+	0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61,
+	0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61,
+	0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+	0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75,
+	0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f,
+	0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f,
+	0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d,
+	0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65,
+	0x12, 0x35, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+	0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
+	0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78,
+	0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a,
+	0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65,
+	0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69,
+	0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69,
+	0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47,
+	0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d,
+	0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a,
+	0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52,
+	0x4f, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f,
+	0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 0x22, 0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65,
+	0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d,
+	0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+	0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62,
+	0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69,
+	0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
+	0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61,
+	0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x8a, 0x02, 0x0a, 0x10,
+	0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f,
+	0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65,
+	0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+	0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74,
+	0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74,
+	0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63,
+	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f,
+	0x52, 0x0c, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x48,
+	0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6a,
+	0x6f, 0x62, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+	0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e,
+	0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x62, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x49,
+	0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63,
+	0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x6f,
+	0x62, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x28, 0x5a, 0x26,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69,
+	0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -1688,52 +1970,59 @@
 	return file_metrics_proto_rawDescData
 }
 
-var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
-var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
+var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
 var file_metrics_proto_goTypes = []interface{}{
-	(MetricsBase_BuildVariant)(0),       // 0: soong_build_metrics.MetricsBase.BuildVariant
-	(MetricsBase_Arch)(0),               // 1: soong_build_metrics.MetricsBase.Arch
-	(ModuleTypeInfo_BuildSystem)(0),     // 2: soong_build_metrics.ModuleTypeInfo.BuildSystem
-	(ExpConfigFetcher_ConfigStatus)(0),  // 3: soong_build_metrics.ExpConfigFetcher.ConfigStatus
-	(*MetricsBase)(nil),                 // 4: soong_build_metrics.MetricsBase
-	(*BuildConfig)(nil),                 // 5: soong_build_metrics.BuildConfig
-	(*SystemResourceInfo)(nil),          // 6: soong_build_metrics.SystemResourceInfo
-	(*PerfInfo)(nil),                    // 7: soong_build_metrics.PerfInfo
-	(*ProcessResourceInfo)(nil),         // 8: soong_build_metrics.ProcessResourceInfo
-	(*ModuleTypeInfo)(nil),              // 9: soong_build_metrics.ModuleTypeInfo
-	(*CriticalUserJourneyMetrics)(nil),  // 10: soong_build_metrics.CriticalUserJourneyMetrics
-	(*CriticalUserJourneysMetrics)(nil), // 11: soong_build_metrics.CriticalUserJourneysMetrics
-	(*SoongBuildMetrics)(nil),           // 12: soong_build_metrics.SoongBuildMetrics
-	(*ExpConfigFetcher)(nil),            // 13: soong_build_metrics.ExpConfigFetcher
-	(*MixedBuildsInfo)(nil),             // 14: soong_build_metrics.MixedBuildsInfo
+	(MetricsBase_BuildVariant)(0),          // 0: soong_build_metrics.MetricsBase.BuildVariant
+	(MetricsBase_Arch)(0),                  // 1: soong_build_metrics.MetricsBase.Arch
+	(BuildConfig_NinjaWeightListSource)(0), // 2: soong_build_metrics.BuildConfig.NinjaWeightListSource
+	(ModuleTypeInfo_BuildSystem)(0),        // 3: soong_build_metrics.ModuleTypeInfo.BuildSystem
+	(ExpConfigFetcher_ConfigStatus)(0),     // 4: soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	(*MetricsBase)(nil),                    // 5: soong_build_metrics.MetricsBase
+	(*BuildConfig)(nil),                    // 6: soong_build_metrics.BuildConfig
+	(*SystemResourceInfo)(nil),             // 7: soong_build_metrics.SystemResourceInfo
+	(*PerfInfo)(nil),                       // 8: soong_build_metrics.PerfInfo
+	(*ProcessResourceInfo)(nil),            // 9: soong_build_metrics.ProcessResourceInfo
+	(*ModuleTypeInfo)(nil),                 // 10: soong_build_metrics.ModuleTypeInfo
+	(*CriticalUserJourneyMetrics)(nil),     // 11: soong_build_metrics.CriticalUserJourneyMetrics
+	(*CriticalUserJourneysMetrics)(nil),    // 12: soong_build_metrics.CriticalUserJourneysMetrics
+	(*SoongBuildMetrics)(nil),              // 13: soong_build_metrics.SoongBuildMetrics
+	(*ExpConfigFetcher)(nil),               // 14: soong_build_metrics.ExpConfigFetcher
+	(*MixedBuildsInfo)(nil),                // 15: soong_build_metrics.MixedBuildsInfo
+	(*CriticalPathInfo)(nil),               // 16: soong_build_metrics.CriticalPathInfo
+	(*JobInfo)(nil),                        // 17: soong_build_metrics.JobInfo
 }
 var file_metrics_proto_depIdxs = []int32{
 	0,  // 0: soong_build_metrics.MetricsBase.target_build_variant:type_name -> soong_build_metrics.MetricsBase.BuildVariant
 	1,  // 1: soong_build_metrics.MetricsBase.target_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 2: soong_build_metrics.MetricsBase.host_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 3: soong_build_metrics.MetricsBase.host_2nd_arch:type_name -> soong_build_metrics.MetricsBase.Arch
-	7,  // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
-	7,  // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
-	7,  // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
-	7,  // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
-	7,  // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
-	12, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
-	5,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
-	6,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
-	7,  // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
-	13, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
-	8,  // 14: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
-	2,  // 15: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
-	4,  // 16: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
-	10, // 17: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
-	7,  // 18: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
-	14, // 19: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
-	3,  // 20: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
-	21, // [21:21] is the sub-list for method output_type
-	21, // [21:21] is the sub-list for method input_type
-	21, // [21:21] is the sub-list for extension type_name
-	21, // [21:21] is the sub-list for extension extendee
-	0,  // [0:21] is the sub-list for field type_name
+	8,  // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+	8,  // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+	8,  // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+	8,  // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+	8,  // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+	13, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+	6,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
+	7,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
+	8,  // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+	14, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
+	16, // 14: soong_build_metrics.MetricsBase.critical_path_info:type_name -> soong_build_metrics.CriticalPathInfo
+	2,  // 15: soong_build_metrics.BuildConfig.ninja_weight_list_source:type_name -> soong_build_metrics.BuildConfig.NinjaWeightListSource
+	9,  // 16: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+	3,  // 17: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+	5,  // 18: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+	11, // 19: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+	8,  // 20: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+	15, // 21: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
+	4,  // 22: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	17, // 23: soong_build_metrics.CriticalPathInfo.critical_path:type_name -> soong_build_metrics.JobInfo
+	17, // 24: soong_build_metrics.CriticalPathInfo.long_running_jobs:type_name -> soong_build_metrics.JobInfo
+	25, // [25:25] is the sub-list for method output_type
+	25, // [25:25] is the sub-list for method input_type
+	25, // [25:25] is the sub-list for extension type_name
+	25, // [25:25] is the sub-list for extension extendee
+	0,  // [0:25] is the sub-list for field type_name
 }
 
 func init() { file_metrics_proto_init() }
@@ -1874,14 +2163,38 @@
 				return nil
 			}
 		}
+		file_metrics_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CriticalPathInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*JobInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_metrics_proto_rawDesc,
-			NumEnums:      4,
-			NumMessages:   11,
+			NumEnums:      5,
+			NumMessages:   13,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 5ad13fc..5e9a055 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -121,9 +121,25 @@
   // The error message due to a non-zero exit _only_ if it did not occur in a
   // recorded phase of the build.
   optional string error_message = 30;
+
+  // The Git Manifest for the user's branch.
+  optional string manifest_url = 31;
+
+  // The branch on which the build occurred.
+  // Example: refs/heads/master
+  optional string branch = 32;
+
+  // The metric of critical path in build
+  optional CriticalPathInfo critical_path_info = 33;
 }
 
 message BuildConfig {
+  enum NinjaWeightListSource {
+    NOT_USED = 0;
+    NINJA_LOG = 1;
+    EVENLY_DISTRIBUTED = 2;
+  }
+
   optional bool use_goma = 1;
 
   optional bool use_rbe = 2;
@@ -143,6 +159,11 @@
 
   // Whether the user explicitly disabled bazel mixed builds for this build.
   optional bool force_disable_bazel_mixed_build = 7;
+
+  // NOT_USED - ninja doesn't use weight list.
+  // NINJA_LOG - ninja uses weight list based on previous builds by ninja log
+  // EVENLY_DISTRIBUTED - ninja thinks every task has the same weight.
+  optional NinjaWeightListSource ninja_weight_list_source = 8 [default = NOT_USED];
 }
 
 message SystemResourceInfo {
@@ -307,3 +328,23 @@
   // Modules that are not enabled for MixedBuilds
   repeated string mixed_build_disabled_modules = 2;
 }
+
+// CriticalPathInfo contains critical path nodes's information.
+// A critical path is a path determining the minimum time needed for the whole build given perfect parallelism.
+message CriticalPathInfo {
+  // Real time which the build system spent in microseconds
+  optional uint64 elapsed_time_micros = 1;
+  // The sum of execution time of the longest path from leave to the root in microseconds
+  optional uint64 critical_path_time_micros = 2;
+  // Detailed job information in a critical path.
+  repeated JobInfo critical_path = 4;
+  // Detailed job information for long running jobs (>30 seconds). These may or may not also be on a critical path.
+  repeated JobInfo long_running_jobs = 5;
+}
+
+message JobInfo {
+  // Real time which a job spent in microseconds
+  optional uint64 elapsed_time_micros = 1;
+  // Description of a job
+  optional string job_description = 2;
+}
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index a46a007..b724bc9 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -28,6 +28,7 @@
     ],
     srcs: [
         "critical_path.go",
+        "critical_path_logger.go",
         "kati.go",
         "log.go",
         "ninja.go",
diff --git a/ui/status/critical_path.go b/ui/status/critical_path.go
index 8065c60..bf32c58 100644
--- a/ui/status/critical_path.go
+++ b/ui/status/critical_path.go
@@ -15,23 +15,23 @@
 package status
 
 import (
+	"android/soong/ui/metrics"
+
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 	"time"
 
-	"android/soong/ui/logger"
+	"google.golang.org/protobuf/proto"
 )
 
-func NewCriticalPath(log logger.Logger) StatusOutput {
-	return &criticalPath{
-		log:     log,
+func NewCriticalPath() *CriticalPath {
+	return &CriticalPath{
 		running: make(map[*Action]time.Time),
 		nodes:   make(map[string]*node),
 		clock:   osClock{},
 	}
 }
 
-type criticalPath struct {
-	log logger.Logger
-
+type CriticalPath struct {
 	nodes   map[string]*node
 	running map[*Action]time.Time
 
@@ -57,7 +57,7 @@
 	input              *node
 }
 
-func (cp *criticalPath) StartAction(action *Action, counts Counts) {
+func (cp *CriticalPath) StartAction(action *Action) {
 	start := cp.clock.Now()
 	if cp.start.IsZero() {
 		cp.start = start
@@ -65,13 +65,13 @@
 	cp.running[action] = start
 }
 
-func (cp *criticalPath) FinishAction(result ActionResult, counts Counts) {
-	if start, ok := cp.running[result.Action]; ok {
-		delete(cp.running, result.Action)
+func (cp *CriticalPath) FinishAction(action *Action) {
+	if start, ok := cp.running[action]; ok {
+		delete(cp.running, action)
 
 		// Determine the input to this edge with the longest cumulative duration
 		var criticalPathInput *node
-		for _, input := range result.Action.Inputs {
+		for _, input := range action.Inputs {
 			if x := cp.nodes[input]; x != nil {
 				if criticalPathInput == nil || x.cumulativeDuration > criticalPathInput.cumulativeDuration {
 					criticalPathInput = x
@@ -88,13 +88,13 @@
 		}
 
 		node := &node{
-			action:             result.Action,
+			action:             action,
 			cumulativeDuration: cumulativeDuration,
 			duration:           duration,
 			input:              criticalPathInput,
 		}
 
-		for _, output := range result.Action.Outputs {
+		for _, output := range action.Outputs {
 			cp.nodes[output] = node
 		}
 
@@ -102,37 +102,7 @@
 	}
 }
 
-func (cp *criticalPath) Flush() {
-	criticalPath := cp.criticalPath()
-
-	if len(criticalPath) > 0 {
-		// Log the critical path to the verbose log
-		criticalTime := criticalPath[0].cumulativeDuration.Round(time.Second)
-		cp.log.Verbosef("critical path took %s", criticalTime.String())
-		if !cp.start.IsZero() {
-			elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
-			cp.log.Verbosef("elapsed time %s", elapsedTime.String())
-			if elapsedTime > 0 {
-				cp.log.Verbosef("perfect parallelism ratio %d%%",
-					int(float64(criticalTime)/float64(elapsedTime)*100))
-			}
-		}
-		cp.log.Verbose("critical path:")
-		for i := len(criticalPath) - 1; i >= 0; i-- {
-			duration := criticalPath[i].duration
-			duration = duration.Round(time.Second)
-			seconds := int(duration.Seconds())
-			cp.log.Verbosef("   %2d:%02d %s",
-				seconds/60, seconds%60, criticalPath[i].action.Description)
-		}
-	}
-}
-
-func (cp *criticalPath) Message(level MsgLevel, msg string) {}
-
-func (cp *criticalPath) Write(p []byte) (n int, err error) { return len(p), nil }
-
-func (cp *criticalPath) criticalPath() []*node {
+func (cp *CriticalPath) criticalPath() (path []*node, elapsedTime time.Duration, criticalTime time.Duration) {
 	var max *node
 
 	// Find the node with the longest critical path
@@ -142,13 +112,46 @@
 		}
 	}
 
-	// Follow the critical path back to the leaf node
-	var criticalPath []*node
 	node := max
 	for node != nil {
-		criticalPath = append(criticalPath, node)
+		path = append(path, node)
 		node = node.input
 	}
+	if len(path) > 0 {
+		// Log the critical path to the verbose log
+		criticalTime = path[0].cumulativeDuration
+		if !cp.start.IsZero() {
+			elapsedTime = cp.end.Sub(cp.start)
+		}
+	}
+	return
+}
 
-	return criticalPath
+func (cp *CriticalPath) longRunningJobs() (nodes []*node) {
+	threshold := time.Second * 30
+	for _, node := range cp.nodes {
+		if node != nil && node.duration > threshold {
+			nodes = append(nodes, node)
+		}
+	}
+	return
+}
+
+func addJobInfos(jobInfos *[]*soong_metrics_proto.JobInfo, sources []*node) {
+	for _, job := range sources {
+		jobInfo := soong_metrics_proto.JobInfo{}
+		jobInfo.ElapsedTimeMicros = proto.Uint64(uint64(job.duration.Microseconds()))
+		jobInfo.JobDescription = &job.action.Description
+		*jobInfos = append(*jobInfos, &jobInfo)
+	}
+}
+
+func (cp *CriticalPath) WriteToMetrics(met *metrics.Metrics) {
+	criticalPathInfo := soong_metrics_proto.CriticalPathInfo{}
+	path, elapsedTime, criticalTime := cp.criticalPath()
+	criticalPathInfo.ElapsedTimeMicros = proto.Uint64(uint64(elapsedTime.Microseconds()))
+	criticalPathInfo.CriticalPathTimeMicros = proto.Uint64(uint64(criticalTime.Microseconds()))
+	addJobInfos(&criticalPathInfo.LongRunningJobs, cp.longRunningJobs())
+	addJobInfos(&criticalPathInfo.CriticalPath, path)
+	met.SetCriticalPathInfo(criticalPathInfo)
 }
diff --git a/ui/status/critical_path_logger.go b/ui/status/critical_path_logger.go
new file mode 100644
index 0000000..f8b49d1
--- /dev/null
+++ b/ui/status/critical_path_logger.go
@@ -0,0 +1,73 @@
+// 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 status
+
+import (
+	"time"
+
+	"android/soong/ui/logger"
+)
+
+// Create a new CriticalPathLogger. if criticalPath is nil, it creates a new criticalPath,
+// if not, it uses that.(its purpose is using a critical path outside logger)
+func NewCriticalPathLogger(log logger.Logger, criticalPath *CriticalPath) StatusOutput {
+	if criticalPath == nil {
+		criticalPath = NewCriticalPath()
+	}
+	return &criticalPathLogger{
+		log:          log,
+		criticalPath: criticalPath,
+	}
+}
+
+type criticalPathLogger struct {
+	log          logger.Logger
+	criticalPath *CriticalPath
+}
+
+func (cp *criticalPathLogger) StartAction(action *Action, counts Counts) {
+	cp.criticalPath.StartAction(action)
+}
+
+func (cp *criticalPathLogger) FinishAction(result ActionResult, counts Counts) {
+	cp.criticalPath.FinishAction(result.Action)
+}
+
+func (cp *criticalPathLogger) Flush() {
+	criticalPath, elapsedTime, criticalTime := cp.criticalPath.criticalPath()
+
+	if len(criticalPath) > 0 {
+		cp.log.Verbosef("critical path took %s", criticalTime.String())
+		if !cp.criticalPath.start.IsZero() {
+			cp.log.Verbosef("elapsed time %s", elapsedTime.String())
+			if elapsedTime > 0 {
+				cp.log.Verbosef("perfect parallelism ratio %d%%",
+					int(float64(criticalTime)/float64(elapsedTime)*100))
+			}
+		}
+		cp.log.Verbose("critical path:")
+		for i := len(criticalPath) - 1; i >= 0; i-- {
+			duration := criticalPath[i].duration
+			duration = duration.Round(time.Second)
+			seconds := int(duration.Seconds())
+			cp.log.Verbosef("   %2d:%02d %s",
+				seconds/60, seconds%60, criticalPath[i].action.Description)
+		}
+	}
+}
+
+func (cp *criticalPathLogger) Message(level MsgLevel, msg string) {}
+
+func (cp *criticalPathLogger) Write(p []byte) (n int, err error) { return len(p), nil }
diff --git a/ui/status/critical_path_test.go b/ui/status/critical_path_test.go
index 965e0ad..369e805 100644
--- a/ui/status/critical_path_test.go
+++ b/ui/status/critical_path_test.go
@@ -21,7 +21,7 @@
 )
 
 type testCriticalPath struct {
-	*criticalPath
+	*CriticalPath
 	Counts
 
 	actions map[int]*Action
@@ -40,14 +40,12 @@
 	}
 
 	t.actions[id] = action
-	t.StartAction(action, t.Counts)
+	t.StartAction(action)
 }
 
 func (t *testCriticalPath) finish(id int, endTime time.Duration) {
 	t.clock = testClock(time.Unix(0, 0).Add(endTime))
-	t.FinishAction(ActionResult{
-		Action: t.actions[id],
-	}, t.Counts)
+	t.FinishAction(t.actions[id])
 }
 
 func TestCriticalPath(t *testing.T) {
@@ -137,13 +135,13 @@
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			cp := &testCriticalPath{
-				criticalPath: NewCriticalPath(nil).(*criticalPath),
+				CriticalPath: NewCriticalPath(),
 				actions:      make(map[int]*Action),
 			}
 
 			tt.msgs(cp)
 
-			criticalPath := cp.criticalPath.criticalPath()
+			criticalPath, _, _ := cp.CriticalPath.criticalPath()
 
 			var descs []string
 			for _, x := range criticalPath {
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index cbc73ed..def76aa 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -163,6 +163,7 @@
 	parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
 	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")
 
 	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")
@@ -224,6 +225,7 @@
 		WriteIfChanged:           *writeIfChanged,
 		StoreSymlinks:            *symlinks,
 		IgnoreMissingFiles:       *ignoreMissingFiles,
+		Sha256Checksum:           *sha256Checksum,
 	})
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index 955fe68..6f1a8ad 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -17,8 +17,11 @@
 import (
 	"bytes"
 	"compress/flate"
+	"crypto/sha256"
+	"encoding/binary"
 	"errors"
 	"fmt"
+	"hash"
 	"hash/crc32"
 	"io"
 	"io/ioutil"
@@ -38,6 +41,14 @@
 	"android/soong/third_party/zip"
 )
 
+// Sha256HeaderID is a custom Header ID for the `extra` field in
+// the file header to store the SHA checksum.
+const Sha256HeaderID = 0x4967
+
+// Sha256HeaderSignature is the signature to verify that the extra
+// data block is used to store the SHA checksum.
+const Sha256HeaderSignature = 0x9514
+
 // Block size used during parallel compression of a single file.
 const parallelBlockSize = 1 * 1024 * 1024 // 1MB
 
@@ -231,6 +242,8 @@
 
 	stderr io.Writer
 	fs     pathtools.FileSystem
+
+	sha256Checksum bool
 }
 
 type zipEntry struct {
@@ -257,6 +270,7 @@
 	WriteIfChanged           bool
 	StoreSymlinks            bool
 	IgnoreMissingFiles       bool
+	Sha256Checksum           bool
 
 	Stderr     io.Writer
 	Filesystem pathtools.FileSystem
@@ -280,6 +294,7 @@
 		ignoreMissingFiles: args.IgnoreMissingFiles,
 		stderr:             args.Stderr,
 		fs:                 args.Filesystem,
+		sha256Checksum:     args.Sha256Checksum,
 	}
 
 	if z.fs == nil {
@@ -782,15 +797,17 @@
 		// this based on actual buffer sizes in RateLimit.
 		ze.futureReaders = make(chan chan io.Reader, (fileSize/parallelBlockSize)+1)
 
-		// Calculate the CRC in the background, since reading the entire
-		// file could take a while.
+		// Calculate the CRC and SHA256 in the background, since reading
+		// the entire file could take a while.
 		//
 		// We could split this up into chunks as well, but it's faster
 		// than the compression. Due to the Go Zip API, we also need to
 		// know the result before we can begin writing the compressed
 		// data out to the zipfile.
+		//
+		// We calculate SHA256 only if `-sha256` is set.
 		wg.Add(1)
-		go z.crcFile(r, ze, compressChan, wg)
+		go z.checksumFileAsync(r, ze, compressChan, wg)
 
 		for start := int64(0); start < fileSize; start += parallelBlockSize {
 			sr := io.NewSectionReader(r, start, parallelBlockSize)
@@ -829,20 +846,53 @@
 	return nil
 }
 
-func (z *ZipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *ZipWriter) checksumFileAsync(r io.ReadSeeker, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
 	defer wg.Done()
 	defer z.cpuRateLimiter.Finish()
 
+	z.checksumFile(r, ze)
+
+	resultChan <- ze
+	close(resultChan)
+}
+
+func (z *ZipWriter) checksumFile(r io.ReadSeeker, ze *zipEntry) {
 	crc := crc32.NewIEEE()
-	_, err := io.Copy(crc, r)
+	writers := []io.Writer{crc}
+
+	var shaHasher hash.Hash
+	if z.sha256Checksum && !ze.fh.Mode().IsDir() {
+		shaHasher = sha256.New()
+		writers = append(writers, shaHasher)
+	}
+
+	w := io.MultiWriter(writers...)
+
+	_, err := io.Copy(w, r)
 	if err != nil {
 		z.errors <- err
 		return
 	}
 
 	ze.fh.CRC32 = crc.Sum32()
-	resultChan <- ze
-	close(resultChan)
+	if shaHasher != nil {
+		z.appendSHAToExtra(ze, shaHasher.Sum(nil))
+	}
+}
+
+func (z *ZipWriter) appendSHAToExtra(ze *zipEntry, checksum []byte) {
+	// The block of SHA256 checksum consist of:
+	// - Header ID, equals to Sha256HeaderID (2 bytes)
+	// - Data size (2 bytes)
+	// - Data block:
+	//   - Signature, equals to Sha256HeaderSignature (2 bytes)
+	//   - Data, SHA checksum value
+	var buf []byte
+	buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderID)
+	buf = binary.LittleEndian.AppendUint16(buf, uint16(len(checksum)+2))
+	buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderSignature)
+	buf = append(buf, checksum...)
+	ze.fh.Extra = append(ze.fh.Extra, buf...)
 }
 
 func (z *ZipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
@@ -894,17 +944,9 @@
 }
 
 func (z *ZipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
+	z.checksumFile(r, ze)
 
-	crc := crc32.NewIEEE()
-	_, err := io.Copy(crc, r)
-	if err != nil {
-		z.errors <- err
-		return
-	}
-
-	ze.fh.CRC32 = crc.Sum32()
-
-	_, err = r.Seek(0, 0)
+	_, err := r.Seek(0, 0)
 	if err != nil {
 		z.errors <- err
 		return
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c4832dc..e7fdea8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"bytes"
+	"encoding/hex"
 	"hash/crc32"
 	"io"
 	"os"
@@ -35,6 +36,10 @@
 	fileEmpty    = []byte("")
 	fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
 
+	sha256FileA = "d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6"
+	sha256FileB = "430c56c5818e62bcb6d478901ef86284e97714c138f3c86aa14fd6a84b7ce5d3"
+	sha256FileC = "31c5ab6111f1d6aa13c2c4e92bb3c0f7c76b61b42d141af1e846eb7f6586a51c"
+
 	fileCustomManifest  = []byte("Custom manifest: true\n")
 	customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
 )
@@ -67,6 +72,20 @@
 	}
 }
 
+func fhWithSHA256(name string, contents []byte, method uint16, sha256 string) zip.FileHeader {
+	h := fh(name, contents, method)
+	// The extra field contains 38 bytes, including 2 bytes of header ID, 2 bytes
+	// of size, 2 bytes of signature, and 32 bytes of checksum data block.
+	var extra [38]byte
+	// The first 6 bytes contains Sha256HeaderID (0x4967), size (unit(34)) and
+	// Sha256HeaderSignature (0x9514)
+	copy(extra[0:], []byte{103, 73, 34, 0, 20, 149})
+	sha256Bytes, _ := hex.DecodeString(sha256)
+	copy(extra[6:], sha256Bytes)
+	h.Extra = append(h.Extra, extra[:]...)
+	return h
+}
+
 func fhManifest(contents []byte) zip.FileHeader {
 	return zip.FileHeader{
 		Name:               "META-INF/MANIFEST.MF",
@@ -87,13 +106,18 @@
 	}
 }
 
-func fhDir(name string) zip.FileHeader {
+type fhDirOptions struct {
+	extra []byte
+}
+
+func fhDir(name string, opts fhDirOptions) zip.FileHeader {
 	return zip.FileHeader{
 		Name:               name,
 		Method:             zip.Store,
 		CRC32:              crc32.ChecksumIEEE(nil),
 		UncompressedSize64: 0,
 		ExternalAttrs:      (syscall.S_IFDIR|0755)<<16 | 0x10,
+		Extra:              opts.extra,
 	}
 }
 
@@ -114,6 +138,7 @@
 		manifest           string
 		storeSymlinks      bool
 		ignoreMissingFiles bool
+		sha256Checksum     bool
 
 		files []zip.FileHeader
 		err   error
@@ -320,10 +345,10 @@
 			emulateJar:       true,
 
 			files: []zip.FileHeader{
-				fhDir("META-INF/"),
+				fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
 				fhManifest(fileManifest),
-				fhDir("a/"),
-				fhDir("a/a/"),
+				fhDir("a/", fhDirOptions{}),
+				fhDir("a/a/", fhDirOptions{}),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 			},
@@ -338,10 +363,10 @@
 			manifest:         "manifest.txt",
 
 			files: []zip.FileHeader{
-				fhDir("META-INF/"),
+				fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
 				fhManifest(customManifestAfter),
-				fhDir("a/"),
-				fhDir("a/a/"),
+				fhDir("a/", fhDirOptions{}),
+				fhDir("a/a/", fhDirOptions{}),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 			},
@@ -355,8 +380,8 @@
 			dirEntries:       true,
 
 			files: []zip.FileHeader{
-				fhDir("a/"),
-				fhDir("a/a/"),
+				fhDir("a/", fhDirOptions{}),
+				fhDir("a/a/", fhDirOptions{}),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 			},
@@ -412,6 +437,23 @@
 				fh("a/a/a", fileA, zip.Deflate),
 			},
 		},
+		{
+			name: "generate SHA256 checksum",
+			args: fileArgsBuilder().
+				File("a/a/a").
+				File("a/a/b").
+				File("a/a/c").
+				File("c"),
+			compressionLevel: 9,
+			sha256Checksum:   true,
+
+			files: []zip.FileHeader{
+				fhWithSHA256("a/a/a", fileA, zip.Deflate, sha256FileA),
+				fhWithSHA256("a/a/b", fileB, zip.Deflate, sha256FileB),
+				fhWithSHA256("a/a/c", fileC, zip.Deflate, sha256FileC),
+				fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
+			},
+		},
 
 		// errors
 		{
@@ -465,6 +507,7 @@
 			args.ManifestSourcePath = test.manifest
 			args.StoreSymlinks = test.storeSymlinks
 			args.IgnoreMissingFiles = test.ignoreMissingFiles
+			args.Sha256Checksum = test.sha256Checksum
 			args.Filesystem = mockFs
 			args.Stderr = &bytes.Buffer{}
 
@@ -555,6 +598,11 @@
 					t.Errorf("incorrect file %s method want %v got %v", want.Name,
 						want.Method, got.Method)
 				}
+
+				if !bytes.Equal(want.Extra, got.Extra) {
+					t.Errorf("incorrect file %s extra want %v got %v", want.Name,
+						want.Extra, got.Extra)
+				}
 			}
 		})
 	}