Merge "Update documenation for default value of sdk_version"
diff --git a/OWNERS b/OWNERS
index 730db7a..e18eea1 100644
--- a/OWNERS
+++ b/OWNERS
@@ -14,6 +14,7 @@
 eakammer@google.com
 jobredeaux@google.com
 joeo@google.com
+juu@google.com
 lamontjones@google.com
 spandandas@google.com
 tradical@google.com
diff --git a/README.md b/README.md
index 87d6948..18cf7b2 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,33 @@
 # Soong
 
-Soong is the replacement for the old Android make-based build system.  It
-replaces Android.mk files with Android.bp files, which are JSON-like simple
-declarative descriptions of modules to build.
+Soong is one of the build systems used in Android. There are altogether three:
+* The legacy Make-based build system that is controlled by files called
+  `Android.mk`.
+* Soong, which is controlled by files called `Android.bp`.
+* The upcoming Bazel-based build system that is controlled by files called
+  `BUILD.bazel`.
+
+`Android.bp` file are JSON-like declarative descriptions of "modules" to build;
+a "module" is the basic unit of building that Soong understands, similarly to
+how "target" is the basic unit of building for Bazel (and Make, although the
+two kinds of "targets" are very different)
 
 See [Simple Build
 Configuration](https://source.android.com/compatibility/tests/development/blueprints)
 on source.android.com to read how Soong is configured for testing.
 
+### Contributing
+
+Code reviews are handled through the usual code review system of Android,
+available [here](https://android-review.googlesource.com/dashboard/self).
+
+For simple changes (fixing typos, obvious optimizations, etc.), sending a code
+review request is enough. For more substantial changes, file a bug in our
+[bug tracker](https://issuetracker.google.com/issues/new?component=381517) or
+or write us at android-building@googlegroups.com .
+
+For Googlers, see our [internal documentation](http://go/soong).
+
 ## Android.bp file format
 
 By design, Android.bp files are very simple.  There are no conditionals or
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index c2af38e..31368b2 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -33,6 +33,10 @@
 	// all modules in this package (not recursively) default to bp2build_available: false.
 	// allows modules to opt-in.
 	Bp2BuildDefaultFalse
+
+	// all modules in this package and subpackages default to bp2build_available: false.
+	// allows modules to opt-in.
+	Bp2BuildDefaultFalseRecursively
 )
 
 var (
@@ -45,10 +49,15 @@
 		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
 		"art/tools":                             Bp2BuildDefaultTrue,
 		"bionic":                                Bp2BuildDefaultTrueRecursively,
+		"bootable/recovery/minadbd":             Bp2BuildDefaultTrue,
+		"bootable/recovery/minui":               Bp2BuildDefaultTrue,
+		"bootable/recovery/applypatch":          Bp2BuildDefaultTrue,
+		"bootable/recovery/recovery_utils":      Bp2BuildDefaultTrue,
 		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
 
 		"build/bazel":                        Bp2BuildDefaultTrueRecursively,
 		"build/make/target/product/security": Bp2BuildDefaultTrue,
+		"build/make/tools/releasetools":      Bp2BuildDefaultTrue,
 		"build/make/tools/signapk":           Bp2BuildDefaultTrue,
 		"build/make/tools/zipalign":          Bp2BuildDefaultTrueRecursively,
 		"build/soong":                        Bp2BuildDefaultTrue,
@@ -106,6 +115,8 @@
 		"external/boringssl":                     Bp2BuildDefaultTrueRecursively,
 		"external/bouncycastle":                  Bp2BuildDefaultTrue,
 		"external/brotli":                        Bp2BuildDefaultTrue,
+		"external/bsdiff":                        Bp2BuildDefaultTrueRecursively,
+		"external/bzip2":                         Bp2BuildDefaultTrueRecursively,
 		"external/conscrypt":                     Bp2BuildDefaultTrue,
 		"external/e2fsprogs":                     Bp2BuildDefaultTrueRecursively,
 		"external/eigen":                         Bp2BuildDefaultTrueRecursively,
@@ -124,16 +135,19 @@
 		"external/icu/icu4j":                     Bp2BuildDefaultFalse, // java rules incomplete
 		"external/jacoco":                        Bp2BuildDefaultTrueRecursively,
 		"external/jarjar":                        Bp2BuildDefaultTrueRecursively,
-		"external/javassist":                     Bp2BuildDefaultTrueRecursively,
 		"external/javaparser":                    Bp2BuildDefaultTrueRecursively,
 		"external/javapoet":                      Bp2BuildDefaultTrueRecursively,
+		"external/javassist":                     Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                  Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                       Bp2BuildDefaultTrueRecursively,
 		"external/junit":                         Bp2BuildDefaultTrueRecursively,
+		"external/libaom":                        Bp2BuildDefaultTrueRecursively,
 		"external/libavc":                        Bp2BuildDefaultTrueRecursively,
 		"external/libcap":                        Bp2BuildDefaultTrueRecursively,
 		"external/libcxx":                        Bp2BuildDefaultTrueRecursively,
 		"external/libcxxabi":                     Bp2BuildDefaultTrueRecursively,
+		"external/libdivsufsort":                 Bp2BuildDefaultTrueRecursively,
+		"external/libdrm":                        Bp2BuildDefaultTrue,
 		"external/libevent":                      Bp2BuildDefaultTrueRecursively,
 		"external/libgav1":                       Bp2BuildDefaultTrueRecursively,
 		"external/libhevc":                       Bp2BuildDefaultTrueRecursively,
@@ -143,9 +157,11 @@
 		"external/libvpx":                        Bp2BuildDefaultTrueRecursively,
 		"external/libyuv":                        Bp2BuildDefaultTrueRecursively,
 		"external/lz4/lib":                       Bp2BuildDefaultTrue,
+		"external/lz4/programs":                  Bp2BuildDefaultTrue,
 		"external/lzma/C":                        Bp2BuildDefaultTrueRecursively,
 		"external/mdnsresponder":                 Bp2BuildDefaultTrueRecursively,
 		"external/minijail":                      Bp2BuildDefaultTrueRecursively,
+		"external/objenesis":                     Bp2BuildDefaultTrueRecursively,
 		"external/openscreen":                    Bp2BuildDefaultTrueRecursively,
 		"external/pcre":                          Bp2BuildDefaultTrueRecursively,
 		"external/protobuf":                      Bp2BuildDefaultTrueRecursively,
@@ -162,6 +178,7 @@
 
 		"frameworks/av":                                      Bp2BuildDefaultTrue,
 		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/codec2/components/aom":          Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/liberror":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
@@ -173,14 +190,17 @@
 		"frameworks/base/tools/aapt2":                        Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
 		"frameworks/native/libs/math":                        Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/nativebase":                  Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/vr":                          Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/opengl/tests/gl2_cameraeye":       Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/gl2_java":            Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testLatency":         Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testPauseResume":     Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testViewport":        Bp2BuildDefaultTrue,
-		"frameworks/proto_logging/stats/stats_log_api_gen":   Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/services/batteryservice":          Bp2BuildDefaultTrue,
+		"frameworks/proto_logging/stats":                     Bp2BuildDefaultTrueRecursively,
 
 		"hardware/interfaces":                          Bp2BuildDefaultTrue,
 		"hardware/interfaces/common/aidl":              Bp2BuildDefaultTrue,
@@ -201,6 +221,14 @@
 		"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,
@@ -250,6 +278,8 @@
 		"system/apex/tools":                                      Bp2BuildDefaultTrueRecursively,
 		"system/core/debuggerd":                                  Bp2BuildDefaultTrueRecursively,
 		"system/core/diagnose_usb":                               Bp2BuildDefaultTrueRecursively,
+		"system/core/healthd":                                    Bp2BuildDefaultTrue,
+		"system/core/healthd/testdata":                           Bp2BuildDefaultTrue,
 		"system/core/libasyncio":                                 Bp2BuildDefaultTrue,
 		"system/core/libcrypto_utils":                            Bp2BuildDefaultTrueRecursively,
 		"system/core/libcutils":                                  Bp2BuildDefaultTrueRecursively,
@@ -257,12 +287,15 @@
 		"system/core/libprocessgroup":                            Bp2BuildDefaultTrue,
 		"system/core/libprocessgroup/cgrouprc":                   Bp2BuildDefaultTrue,
 		"system/core/libprocessgroup/cgrouprc_format":            Bp2BuildDefaultTrue,
+		"system/core/libsuspend":                                 Bp2BuildDefaultTrue,
 		"system/core/libsystem":                                  Bp2BuildDefaultTrueRecursively,
 		"system/core/libsysutils":                                Bp2BuildDefaultTrueRecursively,
 		"system/core/libutils":                                   Bp2BuildDefaultTrueRecursively,
 		"system/core/libvndksupport":                             Bp2BuildDefaultTrueRecursively,
+		"system/core/mkbootfs":                                   Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser":     Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively,
+		"system/extras/toolchain-extras":                         Bp2BuildDefaultTrue,
 		"system/incremental_delivery/incfs":                      Bp2BuildDefaultTrue,
 		"system/libartpalette":                                   Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                                         Bp2BuildDefaultTrueRecursively,
@@ -293,11 +326,10 @@
 		"system/timezone/apex":                                   Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":                            Bp2BuildDefaultTrueRecursively,
 		"system/tools/aidl/build/tests_bp2build":                 Bp2BuildDefaultTrue,
+		"system/tools/mkbootimg":                                 Bp2BuildDefaultTrueRecursively,
 		"system/tools/sysprop":                                   Bp2BuildDefaultTrue,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
-		"frameworks/proto_logging/stats": Bp2BuildDefaultTrueRecursively,
-
 		"tools/apksig": Bp2BuildDefaultTrue,
 		"tools/platform-compat/java/android/compat":  Bp2BuildDefaultTrueRecursively,
 		"tools/tradefederation/prebuilts/filegroups": Bp2BuildDefaultTrueRecursively,
@@ -312,7 +344,7 @@
 		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
 		"build/make/tools":/* recursive = */ false,
 		"build/pesto":/* recursive = */ true,
-		"build/soong/ui/metrics/bp2build_progress_metrics_proto":/* recursive = */ true,
+		"build/soong":/* recursive = */ true,
 
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
@@ -323,6 +355,10 @@
 		"external/guava":/* recursive = */ true,
 		"external/jsr305":/* recursive = */ true,
 		"external/protobuf":/* recursive = */ false,
+
+		// 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,
 
@@ -349,8 +385,10 @@
 		"code_coverage.policy.other",
 		"codec2_soft_exports",
 		"codecs_g711dec",
+		"com.android.media.swcodec",
 		"com.android.media.swcodec-androidManifest",
 		"com.android.media.swcodec-ld.config.txt",
+		"com.android.media.swcodec-mediaswcodec.32rc",
 		"com.android.media.swcodec-mediaswcodec.rc",
 		"com.android.media.swcodec.certificate",
 		"com.android.media.swcodec.key",
@@ -372,14 +410,11 @@
 		"libbinder_headers_platform_shared",
 		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
-		"libbufferhub_headers",
 		"libcodec2",
 		"libcodec2_headers",
 		"libcodec2_internal",
 		"libdmabufheap",
-		"libdvr_headers",
 		"libgsm",
-		"libgui_bufferqueue_sources",
 		"libgrallocusage",
 		"libgralloctypes",
 		"libnativewindow",
@@ -392,7 +427,6 @@
 		"libneuralnetworks_headers",
 		"libneuralnetworks_packageinfo",
 		"libopus",
-		"libpdx_headers",
 		"libprocpartition",
 		"libruy_static",
 		"libandroidio",
@@ -418,7 +452,6 @@
 		"libtextclassifier_hash_static",
 		"libtflite_kernel_utils",
 		"libtinyxml2",
-		"libgui_aidl",
 		"libui",
 		"libui-types",
 		"libui_headers",
@@ -445,13 +478,14 @@
 		"statslog_neuralnetworks.h",
 		"tensorflow_headers",
 
-		"libgui_headers",
 		"libstagefright_bufferpool@2.0",
 		"libstagefright_bufferpool@2.0.1",
 		"libSurfaceFlingerProp",
 
+		// prebuilts
+		"prebuilt_stats-log-api-gen",
+
 		// fastboot
-		"bootimg_headers",
 		"fastboot",
 		"libfastboot",
 		"liblp",
@@ -462,6 +496,10 @@
 		"libavb",
 		"avb_headers",
 
+		//external/libxml2
+		"xmllint",
+		"libxml2",
+
 		//external/fec
 		"libfec_rs",
 
@@ -480,6 +518,7 @@
 
 		//system/extras/verity/fec
 		"fec",
+		"boot_signer",
 
 		//packages/apps/Car/libs/car-ui-lib/car-ui-androidx
 		// genrule dependencies for java_imports
@@ -528,9 +567,6 @@
 		//system/core/libkeyutils
 		"libkeyutils",
 
-		//bootable/recovery/minadbd
-		"libminadbd_headers",
-
 		//bootable/recovery/otautil
 		"libotautil",
 
@@ -551,21 +587,95 @@
 
 		//system/core/fs_mgr
 		"libfs_mgr",
+
+		"libcodec2_hidl@1.0",
+		"libcodec2_hidl@1.1",
+		"libcodec2_hidl@1.2",
+		"libcodec2_hidl_plugin_stub",
+		"libcodec2_hidl_plugin",
+		"libstagefright_bufferqueue_helper_novndk",
+		"libGLESv2",
+		"libEGL",
+		"libcodec2_vndk",
+		"libnativeloader_lazy",
+		"libnativeloader",
+		"libEGL_getProcAddress",
+		"libEGL_blobCache",
+
+		"protoc-gen-cppstream",
+
+		"mediaswcodec",
+		"libmedia_headers",
+		"libmedia_codecserviceregistrant",
+		"libsfplugin_ccodec_utils",
+		"libcodec2_soft_aacenc",
+		"libcodec2_soft_amrnbdec",
+		"libcodec2_soft_amrnbenc",
+		"libcodec2_soft_amrwbdec",
+		"libcodec2_soft_amrwbenc",
+		"libcodec2_soft_hevcdec",
+		"libcodec2_soft_hevcenc",
+		"libcodec2_soft_g711alawdec",
+		"libcodec2_soft_g711mlawdec",
+		"libcodec2_soft_mpeg2dec",
+		"libcodec2_soft_h263dec",
+		"libcodec2_soft_h263enc",
+		"libcodec2_soft_mpeg4dec",
+		"libcodec2_soft_mpeg4enc",
+		"libcodec2_soft_mp3dec",
+		"libcodec2_soft_vorbisdec",
+		"libcodec2_soft_opusdec",
+		"libcodec2_soft_opusenc",
+		"libcodec2_soft_vp8dec",
+		"libcodec2_soft_vp9dec",
+		"libcodec2_soft_av1dec_gav1",
+		"libcodec2_soft_vp8enc",
+		"libcodec2_soft_vp9enc",
+		"libcodec2_soft_rawdec",
+		"libcodec2_soft_flacdec",
+		"libcodec2_soft_flacenc",
+		"libcodec2_soft_gsmdec",
+		"libcodec2_soft_avcdec",
+		"libcodec2_soft_avcenc",
+		"libcodec2_soft_aacdec",
+		"libcodec2_soft_common",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
 		"aidl_interface_headers",
-		"api_domain",
 		"license",
 		"linker_config",
 		"java_import",
 		"java_import_host",
-		"ndk_headers",
-		"ndk_library",
 		"sysprop_library",
+		"bpf",
 	}
 
+	// Add the names of modules that bp2build should never convert, if it is
+	// in the package allowlist.  An error will be thrown if a module must
+	// not be here and in the alwaysConvert lists.
+	//
+	// For prebuilt modules (e.g. android_library_import), remember to add
+	// 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",
+		// Depends on unconverted libchrome
+		"pdx_benchmarks",
+		"buffer_hub_queue-test",
+		"buffer_hub_queue_producer-test",
+
 		// cc bugs
 		"libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
 
@@ -602,30 +712,26 @@
 		"prebuilt_car-ui-androidx-core-common",         // TODO(b/224773339), genrule dependency creates an .aar, not a .jar
 		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
 		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
+		// ERROR: The dependencies for the following 1 jar(s) are not complete.
+		// 1.bazel-out/android_target-fastbuild/bin/prebuilts/tools/common/m2/_aar/robolectric-monitor-1.0.2-alpha1/classes_and_libs_merged.jar
+		"prebuilt_robolectric-monitor-1.0.2-alpha1",
 
 		// path property for filegroups
 		"conscrypt",                        // TODO(b/210751803), we don't handle path property for filegroups
 		"conscrypt-for-host",               // TODO(b/210751803), we don't handle path property for filegroups
 		"host-libprotobuf-java-full",       // TODO(b/210751803), we don't handle path property for filegroups
-		"libprotobuf-internal-protos",      // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-full",            // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-util-full",       // TODO(b/210751803), we don't handle path property for filegroups
 		"auto_value_plugin_resources",      // TODO(b/210751803), we don't handle path property for filegroups
 
 		// go deps:
-		"aapt2-protos",                                                                               // depends on soong_zip, a go binary
-		"analyze_bcpf",                                                                               // depends on bpmodify a blueprint_go_binary.
-		"apex-protos",                                                                                // depends on soong_zip, a go binary
-		"generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // depends on unconverted modules: soong_zip
-		"host_bionic_linker_asm",                                                  // depends on extract_linker, a go binary.
-		"host_bionic_linker_script",                                               // depends on extract_linker, a go binary.
-		"libc_musl_sysroot_bionic_arch_headers",                                   // depends on soong_zip
-		"libc_musl_sysroot_bionic_headers",                                        // 218405924, depends on soong_zip and generates duplicate srcs
-		"libc_musl_sysroot_libc++_headers", "libc_musl_sysroot_libc++abi_headers", // depends on soong_zip, zip2zip
-		"libc_musl_sysroot_zlib_headers", // depends on soong_zip and zip2zip
-		"robolectric-sqlite4java-native", // depends on soong_zip, a go binary
-		"robolectric_tzdata",             // depends on soong_zip, a go binary
+		"analyze_bcpf",              // depends on bpmodify a blueprint_go_binary.
+		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
+		"host_bionic_linker_script", // depends on extract_linker, a go binary.
+
+		// in cmd attribute of genrule rule //system/timezone/output_data:robolectric_tzdata: label '//system/timezone/output_data:iana/tzdata' in $(location) expression is not a declared prerequisite of this rule
+		"robolectric_tzdata",
 
 		// rust support
 		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
@@ -641,8 +747,8 @@
 		"com.android.runtime",                                        // depends on unconverted modules: bionic-linker-config, linkerconfig
 		"currysrc",                                                   // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
 		"dex2oat-script",                                             // depends on unconverted modules: dex2oat
-		"generated_android_icu4j_resources",                          // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
-		"generated_android_icu4j_test_resources",                     // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+		"generated_android_icu4j_resources",                          // depends on unconverted modules: android_icu4j_srcgen_binary
+		"generated_android_icu4j_test_resources",                     // depends on unconverted modules: android_icu4j_srcgen_binary
 		"host-libprotobuf-java-nano",                                 // b/220869005, depends on libprotobuf-java-nano
 		"jacoco-stubs",                                               // b/245767077, depends on droidstubs
 		"libapexutil",                                                // depends on unconverted modules: apex-info-list-tinyxml
@@ -680,6 +786,16 @@
 		// aidl files not created
 		"overlayable_policy_aidl_interface",
 
+		//prebuilts/tools/common/m2
+		// depends on //external/okio:okio-lib, which uses kotlin
+		"wire-runtime",
+
+		// depends on adbd_system_api_recovery, which is a unconverted `phony` module type
+		"minadbd",
+
+		// depends on android.hardware.health-V2.0-java
+		"android.hardware.health-translate-java",
+
 		// cc_test related.
 		// Failing host cc_tests
 		"memunreachable_unit_test",
@@ -842,6 +958,7 @@
 		"libdlext_test_norelro",
 		"libdlext_test_recursive",
 		"libdlext_test_zip",
+		"libdvrcommon_test",
 		"libfortify1-new-tests-clang",
 		"libfortify1-new-tests-clang",
 		"libfortify1-tests-clang",
@@ -1140,6 +1257,29 @@
 		"launcherprotosnano",
 		"datastallprotosnano",
 		"devicepolicyprotosnano",
+		"ota_metadata_proto_java",
+		"merge_ota",
+
+		// releasetools
+		"releasetools_fsverity_metadata_generator",
+		"verity_utils",
+		"check_ota_package_signature",
+		"check_target_files_vintf",
+		"releasetools_check_target_files_vintf",
+		"releasetools_verity_utils",
+		"build_image",
+		"ota_from_target_files",
+		"releasetools_ota_from_target_files",
+		"releasetools_build_image",
+		"add_img_to_target_files",
+		"releasetools_add_img_to_target_files",
+		"fsverity_metadata_generator",
+		"sign_target_files_apks",
+
+		// depends on the support of yacc file
+		"libapplypatch",
+		"libapplypatch_modes",
+		"applypatch",
 	}
 
 	Bp2buildCcLibraryStaticOnlyList = []string{}
diff --git a/android/android_test.go b/android/android_test.go
index fb82e37..64ceedc 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -15,10 +15,32 @@
 package android
 
 import (
+	"io/ioutil"
 	"os"
 	"testing"
 )
 
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "android_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
 func TestMain(m *testing.M) {
-	os.Exit(m.Run())
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
 }
diff --git a/android/api_domain.go b/android/api_domain.go
index 8ff4752..0993e3d 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -67,16 +67,15 @@
 	m := &apiDomain{}
 	m.AddProperties(&m.properties)
 	InitAndroidArchModule(m, DeviceSupported, MultilibBoth)
-	InitBazelModule(m)
 	return m
 }
 
 func (a *apiDomain) DepsMutator(ctx BottomUpMutatorContext) {
 	for _, cc := range a.properties.Cc_api_contributions {
 		// Use FarVariationDependencies since the variants of api_domain is a subset of the variants of the dependency cc module
-		// Creating a dependency on the first variant is ok since this is a no-op in Soong
+		// Creating a dependency on the first variant that matches (os,arch) is ok since this is a no-op in Soong
 		// The primary function of this dependency is to create a connected graph in the corresponding bp2build workspace
-		ctx.AddFarVariationDependencies([]blueprint.Variation{}, nil, cc)
+		ctx.AddFarVariationDependencies(ctx.Target().Variations(), nil, cc)
 	}
 }
 
@@ -108,7 +107,9 @@
 	Cc_api_contributions bazel.LabelListAttribute
 }
 
-func (a *apiDomain) ConvertWithBp2build(ctx TopDownMutatorContext) {
+var _ ApiProvider = (*apiDomain)(nil)
+
+func (a *apiDomain) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "api_domain",
 		Bzl_load_location: "//build/bazel/rules/apis:api_domain.bzl",
diff --git a/android/arch.go b/android/arch.go
index 9bc9d89..75ee922 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -147,10 +147,11 @@
 var (
 	archTypeList []ArchType
 
-	Arm    = newArch("arm", "lib32")
-	Arm64  = newArch("arm64", "lib64")
-	X86    = newArch("x86", "lib32")
-	X86_64 = newArch("x86_64", "lib64")
+	Arm     = newArch("arm", "lib32")
+	Arm64   = newArch("arm64", "lib64")
+	Riscv64 = newArch("riscv64", "lib64")
+	X86     = newArch("x86", "lib32")
+	X86_64  = newArch("x86_64", "lib64")
 
 	Common = ArchType{
 		Name: COMMON_VARIANT,
@@ -318,7 +319,7 @@
 	Windows = newOsType("windows", Host, true, X86, X86_64)
 	// Android is the OS for target devices that run all of Android, including the Linux kernel
 	// and the Bionic libc runtime.
-	Android = newOsType("android", Device, false, Arm, Arm64, X86, X86_64)
+	Android = newOsType("android", Device, false, Arm, Arm64, Riscv64, X86, X86_64)
 
 	// CommonOS is a pseudo OSType for a common OS variant, which is OsType agnostic and which
 	// has dependencies on all the OS variants.
diff --git a/android/arch_test.go b/android/arch_test.go
index 6814f8a..46c018a 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -596,6 +596,7 @@
 				arm64: {
 					a:  ["arm64"],
 				},
+				riscv64: { a: ["riscv64"] },
 				x86: { a:  ["x86"] },
 				x86_64: { a:  ["x86_64"] },
 			},
diff --git a/android/bazel.go b/android/bazel.go
index dd1de7b..d30cb80 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -134,6 +134,11 @@
 	SetBaseModuleType(baseModuleType string)
 }
 
+// ApiProvider is implemented by modules that contribute to an API surface
+type ApiProvider interface {
+	ConvertWithApiBp2build(ctx TopDownMutatorContext)
+}
+
 // MixedBuildBuildable is an interface that module types should implement in order
 // to be "handled by Bazel" in a mixed build.
 type MixedBuildBuildable interface {
@@ -415,6 +420,13 @@
 		return false
 	}
 
+	// In api_bp2build mode, all soong modules that can provide API contributions should be converted
+	// This is irrespective of its presence/absence in bp2build allowlists
+	if ctx.Config().BuildMode == ApiBp2build {
+		_, providesApis := module.(ApiProvider)
+		return providesApis
+	}
+
 	propValue := b.bazelProperties.Bazel_module.Bp2build_available
 	packagePath := ctx.OtherModuleDir(module)
 
@@ -477,21 +489,27 @@
 	// Check if the package path has an exact match in the config.
 	if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively {
 		return true, packagePath
-	} else if config[packagePath] == allowlists.Bp2BuildDefaultFalse {
+	} else if config[packagePath] == allowlists.Bp2BuildDefaultFalse || config[packagePath] == allowlists.Bp2BuildDefaultFalseRecursively {
 		return false, packagePath
 	}
 
 	// If not, check for the config recursively.
-	packagePrefix := ""
-	// e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
-	for _, part := range strings.Split(packagePath, "/") {
-		packagePrefix += part
-		if config[packagePrefix] == allowlists.Bp2BuildDefaultTrueRecursively {
+	packagePrefix := packagePath
+
+	// e.g. for x/y/z, iterate over x/y, then x, taking the most-specific value from the allowlist.
+	for strings.Contains(packagePrefix, "/") {
+		dirIndex := strings.LastIndex(packagePrefix, "/")
+		packagePrefix = packagePrefix[:dirIndex]
+		switch value := config[packagePrefix]; value {
+		case allowlists.Bp2BuildDefaultTrueRecursively:
 			// package contains this prefix and this prefix should convert all modules
 			return true, packagePrefix
+		case allowlists.Bp2BuildDefaultFalseRecursively:
+			//package contains this prefix and this prefix should NOT convert any modules
+			return false, packagePrefix
 		}
 		// Continue to the next part of the package dir.
-		packagePrefix += "/"
+
 	}
 
 	return false, packagePath
@@ -510,6 +528,17 @@
 	bModule.ConvertWithBp2build(ctx)
 }
 
+func registerApiBp2buildConversionMutator(ctx RegisterMutatorsContext) {
+	ctx.TopDown("apiBp2build_conversion", convertWithApiBp2build).Parallel()
+}
+
+// Generate API contribution targets if the Soong module provides APIs
+func convertWithApiBp2build(ctx TopDownMutatorContext) {
+	if m, ok := ctx.Module().(ApiProvider); ok {
+		m.ConvertWithApiBp2build(ctx)
+	}
+}
+
 // GetMainClassInManifest scans the manifest file specified in filepath and returns
 // the value of attribute Main-Class in the manifest file if it exists, or returns error.
 // WARNING: this is for bp2build converters of java_* modules only.
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 3e490dd..29695d6 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -104,9 +104,16 @@
 	configKey   configKey
 }
 
+func makeCqueryKey(label string, cqueryRequest cqueryRequest, cfgKey configKey) cqueryKey {
+	if strings.HasPrefix(label, "//") {
+		// Normalize Bazel labels to specify main repository explicitly.
+		label = "@" + label
+	}
+	return cqueryKey{label, cqueryRequest, cfgKey}
+}
+
 func (c cqueryKey) String() string {
 	return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
-
 }
 
 // BazelContext is a context object useful for interacting with Bazel during
@@ -137,6 +144,9 @@
 	// Returns the results of the GetApexInfo query (including output files)
 	GetApexInfo(label string, cfgkey configKey) (cquery.ApexCqueryInfo, error)
 
+	// Returns the results of the GetCcUnstrippedInfo query
+	GetCcUnstrippedInfo(label string, cfgkey configKey) (cquery.CcUnstrippedInfo, error)
+
 	// ** end Cquery Results Retrieval Functions
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -160,16 +170,18 @@
 }
 
 type bazelRunner interface {
-	issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
+	createBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
+	issueBazelCommand(bazelCmd *exec.Cmd) (output string, errorMessage string, error error)
 }
 
 type bazelPaths struct {
-	homeDir      string
-	bazelPath    string
-	outputBase   string
-	workspaceDir string
-	soongOutDir  string
-	metricsDir   string
+	homeDir       string
+	bazelPath     string
+	outputBase    string
+	workspaceDir  string
+	soongOutDir   string
+	metricsDir    string
+	bazelDepsFile string
 }
 
 // A context object which tracks queued requests that need to be made to Bazel,
@@ -215,6 +227,7 @@
 	LabelToCcInfo       map[string]cquery.CcInfo
 	LabelToPythonBinary map[string]string
 	LabelToApexInfo     map[string]cquery.ApexCqueryInfo
+	LabelToCcBinary     map[string]cquery.CcUnstrippedInfo
 }
 
 func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
@@ -240,6 +253,11 @@
 	panic("unimplemented")
 }
 
+func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) {
+	result, _ := m.LabelToCcBinary[label]
+	return result, nil
+}
+
 func (m MockBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
@@ -261,23 +279,24 @@
 var _ BazelContext = MockBazelContext{}
 
 func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
-	key := cqueryKey{label, requestType, cfgKey}
+	key := makeCqueryKey(label, requestType, cfgKey)
 	bazelCtx.requestMutex.Lock()
 	defer bazelCtx.requestMutex.Unlock()
 	bazelCtx.requests[key] = true
 }
 
 func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
-	key := cqueryKey{label, cquery.GetOutputFiles, cfgKey}
+	key := makeCqueryKey(label, cquery.GetOutputFiles, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		bazelOutput := strings.TrimSpace(rawString)
+
 		return cquery.GetOutputFiles.ParseResult(bazelOutput), nil
 	}
 	return nil, fmt.Errorf("no bazel response found for %v", key)
 }
 
 func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
-	key := cqueryKey{label, cquery.GetCcInfo, cfgKey}
+	key := makeCqueryKey(label, cquery.GetCcInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		bazelOutput := strings.TrimSpace(rawString)
 		return cquery.GetCcInfo.ParseResult(bazelOutput)
@@ -286,7 +305,7 @@
 }
 
 func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
-	key := cqueryKey{label, cquery.GetPythonBinary, cfgKey}
+	key := makeCqueryKey(label, cquery.GetPythonBinary, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		bazelOutput := strings.TrimSpace(rawString)
 		return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
@@ -295,13 +314,21 @@
 }
 
 func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexCqueryInfo, error) {
-	key := cqueryKey{label, cquery.GetApexInfo, cfgKey}
+	key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)), nil
 	}
 	return cquery.ApexCqueryInfo{}, fmt.Errorf("no bazel response found for %v", key)
 }
 
+func (bazelCtx *bazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
+	key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey)
+	if rawString, ok := bazelCtx.results[key]; ok {
+		return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString)), nil
+	}
+	return cquery.CcUnstrippedInfo{}, fmt.Errorf("no bazel response for %s", key)
+}
+
 func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
@@ -322,6 +349,11 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) GetCcUnstrippedInfo(_ string, _ configKey) (cquery.CcUnstrippedInfo, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
 func (n noopBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
@@ -415,6 +447,11 @@
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
 	}
+	if len(c.Getenv("BAZEL_DEPS_FILE")) > 1 {
+		p.bazelDepsFile = c.Getenv("BAZEL_DEPS_FILE")
+	} else {
+		missingEnvVars = append(missingEnvVars, "BAZEL_DEPS_FILE")
+	}
 	if len(missingEnvVars) > 0 {
 		return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
 	} else {
@@ -452,16 +489,30 @@
 
 type mockBazelRunner struct {
 	bazelCommandResults map[bazelCommand]string
-	commands            []bazelCommand
-	extraFlags          []string
+	// use *exec.Cmd as a key to get the bazelCommand, the map will be used in issueBazelCommand()
+	// Register createBazelCommand() invocations. Later, an
+	// issueBazelCommand() invocation can be mapped to the *exec.Cmd instance
+	// and then to the expected result via bazelCommandResults
+	tokens     map[*exec.Cmd]bazelCommand
+	commands   []bazelCommand
+	extraFlags []string
 }
 
-func (r *mockBazelRunner) issueBazelCommand(_ *bazelPaths, _ bazel.RunName,
-	command bazelCommand, extraFlags ...string) (string, string, error) {
+func (r *mockBazelRunner) createBazelCommand(paths *bazelPaths, runName bazel.RunName,
+	command bazelCommand, extraFlags ...string) *exec.Cmd {
 	r.commands = append(r.commands, command)
 	r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
-	if ret, ok := r.bazelCommandResults[command]; ok {
-		return ret, "", nil
+	cmd := &exec.Cmd{}
+	if r.tokens == nil {
+		r.tokens = make(map[*exec.Cmd]bazelCommand)
+	}
+	r.tokens[cmd] = command
+	return cmd
+}
+
+func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
+	if command, ok := r.tokens[bazelCmd]; ok {
+		return r.bazelCommandResults[command], "", nil
 	}
 	return "", "", nil
 }
@@ -472,8 +523,20 @@
 // 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(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
-	extraFlags ...string) (string, string, error) {
+
+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. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
+	} else {
+		return string(output), string(stderr.Bytes()), nil
+	}
+}
+
+func (r *builtinBazelRunner) createBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
+	extraFlags ...string) *exec.Cmd {
 	cmdFlags := []string{
 		"--output_base=" + absolutePath(paths.outputBase),
 		command.command,
@@ -517,15 +580,14 @@
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
 	}
 	bazelCmd.Env = append(os.Environ(), extraEnv...)
-	stderr := &bytes.Buffer{}
-	bazelCmd.Stderr = stderr
 
-	if output, err := bazelCmd.Output(); err != nil {
-		return "", string(stderr.Bytes()),
-			fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
-	} else {
-		return string(output), string(stderr.Bytes()), nil
-	}
+	return bazelCmd
+}
+
+func printableCqueryCommand(bazelCmd *exec.Cmd) string {
+	outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(bazelCmd.Args, "\" \"") + "\""
+	return outputString
+
 }
 
 func (context *bazelContext) mainBzlFileContents() []byte {
@@ -755,6 +817,10 @@
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
+  # TODO(b/248106697): Remove once Bazel is updated to always normalize labels.
+  if id_string.startswith("//"):
+    id_string = "@" + id_string
+
   # Main switch section
   %s
   # This target was not requested via cquery, and thus must be a dependency
@@ -826,15 +892,16 @@
 
 	const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
 	cqueryCmd := bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
-	cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
+	cqueryCommandWithFlag := context.createBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
 		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
+	cqueryOutput, cqueryErr, err := context.issueBazelCommand(cqueryCommandWithFlag)
 	if err != nil {
 		return err
 	}
-	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666); err != nil {
+	cqueryCommandPrint := fmt.Sprintf("cquery command line:\n  %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag))
+	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
 		return err
 	}
-
 	cqueryResults := map[string]string{}
 	for _, outputLine := range strings.Split(cqueryOutput, "\n") {
 		if strings.Contains(outputLine, ">>") {
@@ -870,8 +937,8 @@
 		}
 	}
 	aqueryCmd := bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
-	if aqueryOutput, _, err := context.issueBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
-		extraFlags...); err == nil {
+	if aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
+		extraFlags...)); err == nil {
 		context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
 	}
 	if err != nil {
@@ -882,7 +949,7 @@
 	// 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.
 	buildCmd := bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
-	if _, _, err = context.issueBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd); err != nil {
+	if _, _, err = context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd)); err != nil {
 		return err
 	}
 
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index ec2541b..dc2261c 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -13,10 +13,10 @@
 var testConfig = TestConfig("out", nil, "", nil)
 
 func TestRequestResultsAfterInvokeBazel(t *testing.T) {
-	label := "//foo:bar"
+	label := "@//foo:bar"
 	cfg := configKey{"arm64_armv8-a", Android}
 	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)"}: `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
 	})
 	bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
 	err := bazelContext.InvokeBazel(testConfig)
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index bbdae96..9c50098 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -202,20 +202,26 @@
 	return labels
 }
 
-// Returns true if a prefix + components[:i] + /Android.bp exists
-// TODO(b/185358476) Could check for BUILD file instead of checking for Android.bp file, or ensure BUILD is always generated?
-func directoryHasBlueprint(fs pathtools.FileSystem, prefix string, components []string, componentIndex int) bool {
-	blueprintPath := prefix
-	if blueprintPath != "" {
-		blueprintPath = blueprintPath + "/"
-	}
-	blueprintPath = blueprintPath + strings.Join(components[:componentIndex+1], "/")
-	blueprintPath = blueprintPath + "/Android.bp"
-	if exists, _, _ := fs.Exists(blueprintPath); exists {
+// Returns true if a prefix + components[:i] is a package boundary.
+//
+// A package boundary is determined by a BUILD file in the directory. This can happen in 2 cases:
+//
+//  1. An Android.bp exists, which bp2build will always convert to a sibling BUILD file.
+//  2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
+//     is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
+func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
+	prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
+	if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
 		return true
-	} else {
-		return false
+	} else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) {
+		if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
+			return true
+		} else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
+			return true
+		}
 	}
+
+	return false
 }
 
 // Transform a path (if necessary) to acknowledge package boundaries
@@ -242,17 +248,37 @@
 		newPath.Label = path.Label
 		return newPath
 	}
-
-	newLabel := ""
+	if strings.HasPrefix(path.Label, "./") {
+		// Drop "./" for consistent handling of paths.
+		// Specifically, to not let "." be considered a package boundary.
+		// Say `inputPath` is `x/Android.bp` and that file has some module
+		// with `srcs=["y/a.c", "z/b.c"]`.
+		// And say the directory tree is:
+		//     x
+		//     ├── Android.bp
+		//     ├── y
+		//     │   ├── a.c
+		//     │   └── Android.bp
+		//     └── z
+		//         └── b.c
+		// Then bazel equivalent labels in srcs should be:
+		//   //x/y:a.c, x/z/b.c
+		// The above should still be the case if `x/Android.bp` had
+		//   srcs=["./y/a.c", "./z/b.c"]
+		// However, if we didn't strip "./", we'd get
+		//   //x/./y:a.c, //x/.:z/b.c
+		path.Label = strings.TrimPrefix(path.Label, "./")
+	}
 	pathComponents := strings.Split(path.Label, "/")
-	foundBlueprint := false
+	newLabel := ""
+	foundPackageBoundary := false
 	// Check the deepest subdirectory first and work upwards
 	for i := len(pathComponents) - 1; i >= 0; i-- {
 		pathComponent := pathComponents[i]
 		var sep string
-		if !foundBlueprint && directoryHasBlueprint(ctx.Config().fs, ctx.ModuleDir(), pathComponents, i) {
+		if !foundPackageBoundary && isPackageBoundary(ctx.Config(), ctx.ModuleDir(), pathComponents, i) {
 			sep = ":"
-			foundBlueprint = true
+			foundPackageBoundary = true
 		} else {
 			sep = "/"
 		}
@@ -262,7 +288,7 @@
 			newLabel = pathComponent + sep + newLabel
 		}
 	}
-	if foundBlueprint {
+	if foundPackageBoundary {
 		// Ensure paths end up looking like //bionic/... instead of //./bionic/...
 		moduleDir := ctx.ModuleDir()
 		if strings.HasPrefix(moduleDir, ".") {
diff --git a/android/bazel_paths_test.go b/android/bazel_paths_test.go
index b047511..450bf76 100644
--- a/android/bazel_paths_test.go
+++ b/android/bazel_paths_test.go
@@ -17,6 +17,10 @@
 import (
 	"path/filepath"
 	"testing"
+
+	"android/soong/bazel"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
 type TestBazelPathContext struct{}
@@ -29,7 +33,7 @@
 	return cfg
 }
 
-func (*TestBazelPathContext) AddNinjaFileDeps(deps ...string) {
+func (*TestBazelPathContext) AddNinjaFileDeps(...string) {
 	panic("Unimplemented")
 }
 
@@ -106,3 +110,74 @@
 		t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
 	}
 }
+
+type TestBazelConversionPathContext struct {
+	TestBazelConversionContext
+	moduleDir string
+	cfg       Config
+}
+
+func (ctx *TestBazelConversionPathContext) AddNinjaFileDeps(...string) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) GlobWithDeps(string, []string) ([]string, error) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) PropertyErrorf(string, string, ...interface{}) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) GetDirectDep(string) (blueprint.Module, blueprint.DependencyTag) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) ModuleFromName(string) (blueprint.Module, bool) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) AddUnconvertedBp2buildDep(string) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) AddMissingBp2buildDep(string) {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) Module() Module {
+	panic("Unimplemented")
+}
+
+func (ctx *TestBazelConversionPathContext) Config() Config {
+	return ctx.cfg
+}
+
+func (ctx *TestBazelConversionPathContext) ModuleDir() string {
+	return ctx.moduleDir
+}
+
+func TestTransformSubpackagePath(t *testing.T) {
+	cfg := NullConfig("out", "out/soong")
+	cfg.fs = pathtools.MockFs(map[string][]byte{
+		"x/Android.bp":   nil,
+		"x/y/Android.bp": nil,
+	})
+
+	var ctx BazelConversionPathContext = &TestBazelConversionPathContext{
+		moduleDir: "x",
+		cfg:       cfg,
+	}
+	pairs := map[string]string{
+		"y/a.c":   "//x/y:a.c",
+		"./y/a.c": "//x/y:a.c",
+		"z/b.c":   "z/b.c",
+		"./z/b.c": "z/b.c",
+	}
+	for in, out := range pairs {
+		actual := transformSubpackagePath(ctx, bazel.Label{Label: in}).Label
+		if actual != out {
+			t.Errorf("expected:\n%v\nactual:\n%v", out, actual)
+		}
+	}
+}
diff --git a/android/bazel_test.go b/android/bazel_test.go
index dbe6067..3164b23 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -71,6 +71,20 @@
 			},
 			packageDir: "a",
 		},
+		{
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":   allowlists.Bp2BuildDefaultFalseRecursively,
+				"a/b": allowlists.Bp2BuildDefaultTrue,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":   allowlists.Bp2BuildDefaultFalseRecursively,
+				"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a/b/c",
+		},
 	}
 
 	for _, test := range testCases {
@@ -133,6 +147,20 @@
 			},
 			packageDir: "a",
 		},
+		{
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":   allowlists.Bp2BuildDefaultFalseRecursively,
+				"a/b": allowlists.Bp2BuildDefaultTrue,
+			},
+			packageDir: "a/b/c",
+		},
+		{
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":   allowlists.Bp2BuildDefaultTrueRecursively,
+				"a/b": allowlists.Bp2BuildDefaultFalseRecursively,
+			},
+			packageDir: "a/b/c",
+		},
 	}
 
 	for _, test := range testCases {
diff --git a/android/config.go b/android/config.go
index ee432a2..4992882 100644
--- a/android/config.go
+++ b/android/config.go
@@ -83,6 +83,9 @@
 	// express build semantics.
 	GenerateQueryView
 
+	// Generate BUILD files for API contributions to API surfaces
+	ApiBp2build
+
 	// Create a JSON representation of the module graph and exit.
 	GenerateModuleGraph
 
@@ -124,6 +127,11 @@
 	return []bootstrap.PrimaryBuilderInvocation{}
 }
 
+// RunningInsideUnitTest returns true if this code is being run as part of a Soong unit test.
+func (c Config) RunningInsideUnitTest() bool {
+	return c.config.TestProductVariables != nil
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 4ee5bf7..18b63d3 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -98,6 +98,11 @@
 	var orderOnlyDeps Paths
 	var args []string
 
+	if n := ctx.ModuleName(); n != "" {
+		args = append(args,
+			"-mn "+proptools.NinjaAndShellEscape(n))
+	}
+
 	if t := ctx.ModuleType(); t != "" {
 		args = append(args,
 			"-mt "+proptools.NinjaAndShellEscape(t))
diff --git a/android/makevars.go b/android/makevars.go
index a74185a..5165a55 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -35,7 +35,7 @@
 	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String())
 }
 
-///////////////////////////////////////////////////////////////////////////////
+// /////////////////////////////////////////////////////////////////////////////
 
 // BaseMakeVarsContext contains the common functions for other packages to use
 // to declare make variables
@@ -173,13 +173,14 @@
 	MakeVars(ctx MakeVarsModuleContext)
 }
 
-///////////////////////////////////////////////////////////////////////////////
+// /////////////////////////////////////////////////////////////////////////////
 
 func makeVarsSingletonFunc() Singleton {
 	return &makeVarsSingleton{}
 }
 
 type makeVarsSingleton struct {
+	varsForTesting     []makeVarsVariable
 	installsForTesting []byte
 }
 
@@ -320,7 +321,11 @@
 		ctx.Errorf(err.Error())
 	}
 
-	s.installsForTesting = installsBytes
+	// Only save state for tests when testing.
+	if ctx.Config().RunningInsideUnitTest() {
+		s.varsForTesting = vars
+		s.installsForTesting = installsBytes
+	}
 }
 
 func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
diff --git a/android/module.go b/android/module.go
index 5d520f4..5aecb05 100644
--- a/android/module.go
+++ b/android/module.go
@@ -915,10 +915,19 @@
 type CommonAttributes struct {
 	// Soong nameProperties -> Bazel name
 	Name string
+
 	// Data mapped from: Required
 	Data bazel.LabelListAttribute
 
+	// SkipData is neither a Soong nor Bazel target attribute
+	// If true, this will not fill the data attribute automatically
+	// This is useful for Soong modules that have 1:many Bazel targets
+	// Some of the generated Bazel targets might not have a data attribute
+	SkipData *bool
+
 	Tags bazel.StringListAttribute
+
+	Applicable_licenses bazel.LabelListAttribute
 }
 
 // constraintAttributes represents Bazel attributes pertaining to build constraints,
@@ -1231,22 +1240,21 @@
 		}
 	}
 
-	required := depsToLabelList(mod.commonProperties.Required)
+	attrs.Applicable_licenses = bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, mod.commonProperties.Licenses))
+
+	// The required property can contain the module itself. This causes a cycle
+	// when generated as the 'data' label list attribute in Bazel. Remove it if
+	// it exists. See b/247985196.
+	_, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), mod.commonProperties.Required)
+	requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles)
+	required := depsToLabelList(requiredWithoutCycles)
 	archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
 			if archProps, ok := _props.(*commonProperties); ok {
-				// TODO(b/234748998) Remove this requiredFiltered workaround when aapt2 converts successfully
-				requiredFiltered := archProps.Required
-				if attrs.Name == "apexer" {
-					requiredFiltered = make([]string, 0, len(archProps.Required))
-					for _, req := range archProps.Required {
-						if req != "aapt2" && req != "apexer" {
-							requiredFiltered = append(requiredFiltered, req)
-						}
-					}
-				}
-				required.SetSelectValue(axis, config, depsToLabelList(requiredFiltered).Value)
+				_, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), archProps.Required)
+				requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles)
+				required.SetSelectValue(axis, config, depsToLabelList(requiredWithoutCycles).Value)
 				if !neitherHostNorDevice {
 					if archProps.Enabled != nil {
 						if axis != bazel.OsConfigurationAxis || osSupport[config] {
@@ -1306,7 +1314,12 @@
 		platformEnabledAttribute.Add(&l)
 	}
 
-	attrs.Data.Append(required)
+	if !proptools.Bool(attrs.SkipData) {
+		attrs.Data.Append(required)
+	}
+	// SkipData is not an attribute of any Bazel target
+	// Set this to nil so that it does not appear in the generated build file
+	attrs.SkipData = nil
 
 	moduleEnableConstraints := bazel.LabelListAttribute{}
 	moduleEnableConstraints.Append(platformEnabledAttribute)
@@ -1524,7 +1537,7 @@
 	return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.UnconvertedDeps)
 }
 
-// GetMissingBp2buildDeps eturns the list of module names that were not found in Android.bp files.
+// GetMissingBp2buildDeps returns the list of module names that were not found in Android.bp files.
 func (m *ModuleBase) GetMissingBp2buildDeps() []string {
 	return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.MissingDeps)
 }
@@ -3547,10 +3560,29 @@
 		reportPathError(ctx, err)
 		return nil
 	}
+	if len(paths) == 0 {
+		type addMissingDependenciesIntf interface {
+			AddMissingDependencies([]string)
+			OtherModuleName(blueprint.Module) string
+		}
+		if mctx, ok := ctx.(addMissingDependenciesIntf); ok && ctx.Config().AllowMissingDependencies() {
+			mctx.AddMissingDependencies([]string{mctx.OtherModuleName(module)})
+		} else {
+			ReportPathErrorf(ctx, "failed to get output files from module %q", pathContextName(ctx, module))
+		}
+		// Return a fake output file to avoid nil dereferences of Path objects later.
+		// This should never get used for an actual build as the error or missing
+		// dependency has already been reported.
+		p, err := pathForSource(ctx, filepath.Join("missing_output_file", pathContextName(ctx, module)))
+		if err != nil {
+			reportPathError(ctx, err)
+			return nil
+		}
+		return p
+	}
 	if len(paths) > 1 {
 		ReportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one",
 			pathContextName(ctx, module))
-		return nil
 	}
 	return paths[0]
 }
@@ -3562,18 +3594,12 @@
 			return nil, fmt.Errorf("failed to get output file from module %q: %s",
 				pathContextName(ctx, module), err.Error())
 		}
-		if len(paths) == 0 {
-			return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
-		}
 		return paths, nil
 	} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
 		if tag != "" {
 			return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
 		}
 		paths := sourceFileProducer.Srcs()
-		if len(paths) == 0 {
-			return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
-		}
 		return paths, nil
 	} else {
 		return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
diff --git a/android/module_test.go b/android/module_test.go
index 0580bef..1ca7422 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"github.com/google/blueprint"
 	"path/filepath"
 	"runtime"
 	"testing"
@@ -978,3 +979,88 @@
 		})
 	}
 }
+
+type fakeBlueprintModule struct{}
+
+func (fakeBlueprintModule) Name() string { return "foo" }
+
+func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
+
+type sourceProducerTestModule struct {
+	fakeBlueprintModule
+	source Path
+}
+
+func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
+
+type outputFileProducerTestModule struct {
+	fakeBlueprintModule
+	output map[string]Path
+	error  map[string]error
+}
+
+func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
+	return PathsIfNonNil(o.output[tag]), o.error[tag]
+}
+
+type pathContextAddMissingDependenciesWrapper struct {
+	PathContext
+	missingDeps []string
+}
+
+func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) {
+	p.missingDeps = append(p.missingDeps, deps...)
+}
+func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string {
+	return module.Name()
+}
+
+func TestOutputFileForModule(t *testing.T) {
+	testcases := []struct {
+		name        string
+		module      blueprint.Module
+		tag         string
+		env         map[string]string
+		config      func(*config)
+		expected    string
+		missingDeps []string
+	}{
+		{
+			name:     "SourceFileProducer",
+			module:   &sourceProducerTestModule{source: PathForTesting("foo.txt")},
+			expected: "foo.txt",
+		},
+		{
+			name:     "OutputFileProducer",
+			module:   &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
+			expected: "foo.txt",
+		},
+		{
+			name:     "OutputFileProducer_tag",
+			module:   &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
+			tag:      "foo",
+			expected: "foo.txt",
+		},
+		{
+			name: "OutputFileProducer_AllowMissingDependencies",
+			config: func(config *config) {
+				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
+			},
+			module:      &outputFileProducerTestModule{},
+			missingDeps: []string{"foo"},
+			expected:    "missing_output_file/foo",
+		},
+	}
+	for _, tt := range testcases {
+		config := TestConfig(buildDir, tt.env, "", nil)
+		if tt.config != nil {
+			tt.config(config.config)
+		}
+		ctx := &pathContextAddMissingDependenciesWrapper{
+			PathContext: PathContextForTesting(config),
+		}
+		got := OutputFileForModule(ctx, tt.module, tt.tag)
+		AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
+		AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+	}
+}
diff --git a/android/mutator.go b/android/mutator.go
index 9e4aa59..83d4e66 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -31,22 +31,33 @@
 
 // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
 func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
+	bp2buildMutators := append(preArchMutators, registerBp2buildConversionMutator)
+	registerMutatorsForBazelConversion(ctx, bp2buildMutators)
+}
+
+// RegisterMutatorsForApiBazelConversion is an alternate registration pipeline for api_bp2build
+// This pipeline restricts generation of Bazel targets to Soong modules that contribute APIs
+func RegisterMutatorsForApiBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
+	bp2buildMutators := append(preArchMutators, registerApiBp2buildConversionMutator)
+	registerMutatorsForBazelConversion(ctx, bp2buildMutators)
+}
+
+func registerMutatorsForBazelConversion(ctx *Context, bp2buildMutators []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{
 		bazelConversionMode: true,
 	}
 
-	bp2buildMutators := append([]RegisterMutatorFunc{
+	allMutators := append([]RegisterMutatorFunc{
 		RegisterNamespaceMutator,
 		RegisterDefaultsPreArchMutators,
 		// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
 		// evaluate the impact on conversion.
 		RegisterPrebuiltsPreArchMutators,
 	},
-		preArchMutators...)
-	bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator)
+		bp2buildMutators...)
 
 	// Register bp2build mutators
-	for _, f := range bp2buildMutators {
+	for _, f := range allMutators {
 		f(mctx)
 	}
 
diff --git a/android/neverallow.go b/android/neverallow.go
index 00078a0..1cdccc3 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -58,6 +58,7 @@
 	AddNeverAllowRules(createMakefileGoalRules()...)
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
+	AddNeverAllowRules(createBp2BuildRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -65,6 +66,13 @@
 	neverallows = append(neverallows, rules...)
 }
 
+func createBp2BuildRule() Rule {
+	return NeverAllow().
+		With("bazel_module.bp2build_available", "true").
+		Because("setting bp2build_available in Android.bp is not " +
+			"supported for custom conversion, use allowlists.go instead.")
+}
+
 func createIncludeDirsRules() []Rule {
 	notInIncludeDir := []string{
 		"art",
diff --git a/android/paths.go b/android/paths.go
index 27f4bf5..dbcdb23 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1638,6 +1638,10 @@
 	}
 }
 
+func (p InstallPath) Partition() string {
+	return p.partition
+}
+
 // Join creates a new InstallPath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 4e4fa42..9b5c0e9 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -56,7 +56,9 @@
 var _ ExcludeFromVisibilityEnforcementTag = PrebuiltDepTag
 var _ ExcludeFromApexContentsTag = PrebuiltDepTag
 
-type PrebuiltProperties struct {
+// UserSuppliedPrebuiltProperties contains the prebuilt properties that can be specified in an
+// Android.bp file.
+type UserSuppliedPrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
 	// a matching name.
 	Prefer *bool `android:"arch_variant"`
@@ -70,6 +72,16 @@
 	// If specified then the prefer property is ignored in favor of the value of the Soong config
 	// variable.
 	Use_source_config_var *ConfigVarProperties
+}
+
+// CopyUserSuppliedPropertiesFromPrebuilt copies the user supplied prebuilt properties from the
+// prebuilt properties.
+func (u *UserSuppliedPrebuiltProperties) CopyUserSuppliedPropertiesFromPrebuilt(p *Prebuilt) {
+	*u = p.properties.UserSuppliedPrebuiltProperties
+}
+
+type PrebuiltProperties struct {
+	UserSuppliedPrebuiltProperties
 
 	SourceExists bool `blueprint:"mutated"`
 	UsePrebuilt  bool `blueprint:"mutated"`
diff --git a/android/register.go b/android/register.go
index d4ce5f1..6c69cc5 100644
--- a/android/register.go
+++ b/android/register.go
@@ -180,6 +180,16 @@
 	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
 }
 
+// RegisterForApiBazelConversion is similar to RegisterForBazelConversion except that
+// it only generates API targets in the generated  workspace
+func (ctx *Context) RegisterForApiBazelConversion() {
+	for _, t := range moduleTypes {
+		t.register(ctx)
+	}
+
+	RegisterMutatorsForApiBazelConversion(ctx, bp2buildPreArchMutators)
+}
+
 // Register the pipeline of singletons, module types, and mutators for
 // generating build.ninja and other files for Kati, from Android.bp files.
 func (ctx *Context) Register() {
diff --git a/android/sdk.go b/android/sdk.go
index a9cc547..bd2f5d1 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -735,8 +735,13 @@
 	//   have common values. Those fields are cleared and the common value added to the common
 	//   properties.
 	//
-	//   A field annotated with a tag of `sdk:"keep"` will be treated as if it
-	//   was not capitalized, i.e. not optimized for common values.
+	//   A field annotated with a tag of `sdk:"ignore"` will be treated as if it
+	//   was not capitalized, i.e. ignored and not optimized for common values.
+	//
+	//   A field annotated with a tag of `sdk:"keep"` will not be cleared even if the value is common
+	//   across multiple structs. Common values will still be copied into the common property struct.
+	//   So, if the same value is placed in all structs populated from variants that value would be
+	//   copied into all common property structs and so be available in every instance.
 	//
 	//   A field annotated with a tag of `android:"arch_variant"` will be allowed to have
 	//   values that differ by arch, fields not tagged as such must have common values across
@@ -923,18 +928,18 @@
 	// the locations of any of their prebuilt files in the snapshot by os type to prevent them
 	// from colliding. See OsPrefix().
 	//
-	// This property is the same for all variants of a member and so would be optimized away
-	// if it was not explicitly kept.
-	Os_count int `sdk:"keep"`
+	// Ignore this property during optimization. This is needed because this property is the same for
+	// all variants of a member and so would be optimized away if it was not ignored.
+	Os_count int `sdk:"ignore"`
 
 	// The os type for which these properties refer.
 	//
 	// Provided to allow a member to differentiate between os types in the locations of their
 	// prebuilt files when it supports more than one os type.
 	//
-	// This property is the same for all os type specific variants of a member and so would be
-	// optimized away if it was not explicitly kept.
-	Os OsType `sdk:"keep"`
+	// Ignore this property during optimization. This is needed because this property is the same for
+	// all variants of a member and so would be optimized away if it was not ignored.
+	Os OsType `sdk:"ignore"`
 
 	// The setting to use for the compile_multilib property.
 	Compile_multilib string `android:"arch_variant"`
diff --git a/android/test_config.go b/android/test_config.go
index 0f88d51..de546c4 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -91,6 +91,9 @@
 		},
 	}
 
+	// Make the CommonOS OsType available for all products.
+	config.Targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
+
 	if runtime.GOOS == "darwin" {
 		config.Targets[config.BuildOS] = config.Targets[config.BuildOS][:1]
 	}
diff --git a/android/testing.go b/android/testing.go
index 7b74c89..f9f9670 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -461,6 +461,12 @@
 	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
 }
 
+// RegisterForApiBazelConversion prepares a test context for API bp2build conversion.
+func (ctx *TestContext) RegisterForApiBazelConversion() {
+	ctx.config.BuildMode = ApiBp2build
+	RegisterMutatorsForApiBazelConversion(ctx.Context, ctx.bp2buildPreArch)
+}
+
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
 	// This function adapts the old style ParseFileList calls that are spread throughout the tests
 	// to the new style that takes a config.
@@ -667,6 +673,46 @@
 	return parseMkRules(t, ctx.config, nodes)
 }
 
+// MakeVarVariable provides access to make vars that will be written by the makeVarsSingleton
+type MakeVarVariable interface {
+	// Name is the name of the variable.
+	Name() string
+
+	// Value is the value of the variable.
+	Value() string
+}
+
+func (v makeVarsVariable) Name() string {
+	return v.name
+}
+
+func (v makeVarsVariable) Value() string {
+	return v.value
+}
+
+// PrepareForTestAccessingMakeVars sets up the test so that MakeVarsForTesting will work.
+var PrepareForTestAccessingMakeVars = GroupFixturePreparers(
+	PrepareForTestWithAndroidMk,
+	PrepareForTestWithMakevars,
+)
+
+// MakeVarsForTesting returns a filtered list of MakeVarVariable objects that represent the
+// variables that will be written out.
+//
+// It is necessary to use PrepareForTestAccessingMakeVars in tests that want to call this function.
+// Along with any other preparers needed to add the make vars.
+func (ctx *TestContext) MakeVarsForTesting(filter func(variable MakeVarVariable) bool) []MakeVarVariable {
+	vars := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).varsForTesting
+	result := make([]MakeVarVariable, 0, len(vars))
+	for _, v := range vars {
+		if filter(v) {
+			result = append(result, v)
+		}
+	}
+
+	return result
+}
+
 func (ctx *TestContext) Config() Config {
 	return ctx.config
 }
diff --git a/apex/apex.go b/apex/apex.go
index 2e54e7e..6afbd4a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -286,6 +286,9 @@
 		Arm64 struct {
 			ApexNativeDependencies
 		}
+		Riscv64 struct {
+			ApexNativeDependencies
+		}
 		X86 struct {
 			ApexNativeDependencies
 		}
@@ -787,6 +790,8 @@
 			depsList = append(depsList, a.archProperties.Arch.Arm.ApexNativeDependencies)
 		case android.Arm64:
 			depsList = append(depsList, a.archProperties.Arch.Arm64.ApexNativeDependencies)
+		case android.Riscv64:
+			depsList = append(depsList, a.archProperties.Arch.Riscv64.ApexNativeDependencies)
 		case android.X86:
 			depsList = append(depsList, a.archProperties.Arch.X86.ApexNativeDependencies)
 		case android.X86_64:
@@ -1559,7 +1564,7 @@
 		dirInApex = filepath.Join(dirInApex, "bionic")
 	}
 
-	fileToCopy := ccMod.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, ccMod, "")
 	androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
 	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
 }
@@ -1570,7 +1575,7 @@
 		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
 	}
 	dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
-	fileToCopy := cc.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, cc, "")
 	androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
 	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
 	af.symlinks = cc.Symlinks()
@@ -1583,7 +1588,7 @@
 	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
 	}
-	fileToCopy := rustm.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
 	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
 	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
 	return af
@@ -1602,7 +1607,7 @@
 	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
 	}
-	fileToCopy := rustm.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
 	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
 	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
 }
@@ -2659,9 +2664,13 @@
 		}
 
 		// Certificate
-		if overridableProperties.Certificate != nil {
-			attrs.Certificate = bazel.LabelAttribute{}
-			attrs.Certificate.SetValue(android.BazelLabelForModuleDepSingle(ctx, *overridableProperties.Certificate))
+		if overridableProperties.Certificate == nil {
+			// delegated to the rule attr default
+			attrs.Certificate = nil
+		} else {
+			certificateName, certificate := java.ParseCertificateToAttribute(ctx, overridableProperties.Certificate)
+			attrs.Certificate_name = certificateName
+			attrs.Certificate = certificate
 		}
 
 		// Prebuilts
@@ -2726,16 +2735,18 @@
 	// 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.
-	overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
-	overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
-	originalMinApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
-	isMinSdkSet := a.properties.Min_sdk_version != nil
-	isOverrideValueHigher := overrideApiLevel.CompareTo(originalMinApiLevel) > 0
-	if overrideMinSdkValue != "" && isMinSdkSet && isOverrideValueHigher {
-		return overrideMinSdkValue
+	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	if minApiLevel.IsNone() {
+		return ""
 	}
 
-	return proptools.String(a.properties.Min_sdk_version)
+	overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
+	overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
+	if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(minApiLevel) > 0 {
+		minApiLevel = overrideApiLevel
+	}
+
+	return minApiLevel.String()
 }
 
 // Returns apex's min_sdk_version SdkSpec, honoring overrides
@@ -3335,7 +3346,8 @@
 	Android_manifest      bazel.LabelAttribute
 	File_contexts         bazel.LabelAttribute
 	Key                   bazel.LabelAttribute
-	Certificate           bazel.LabelAttribute
+	Certificate           *bazel.Label // used when the certificate prop is a module
+	Certificate_name      *string      // used when the certificate prop is a string
 	Min_sdk_version       *string
 	Updatable             bazel.BoolAttribute
 	Installable           bazel.BoolAttribute
@@ -3397,10 +3409,7 @@
 		keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key))
 	}
 
-	var certificateLabelAttribute bazel.LabelAttribute
-	if a.overridableProperties.Certificate != nil {
-		certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
-	}
+	certificateName, certificate := java.ParseCertificateToAttribute(ctx, a.overridableProperties.Certificate)
 
 	nativeSharedLibs := &convertedNativeSharedLibs{
 		Native_shared_libs_32: bazel.LabelListAttribute{},
@@ -3456,7 +3465,8 @@
 		File_contexts:         fileContextsLabelAttribute,
 		Min_sdk_version:       minSdkVersion,
 		Key:                   keyLabelAttribute,
-		Certificate:           certificateLabelAttribute,
+		Certificate:           certificate,
+		Certificate_name:      certificateName,
 		Updatable:             updatableAttribute,
 		Installable:           installableAttribute,
 		Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32,
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 1805291..647a575 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2191,6 +2191,38 @@
 	`)
 }
 
+func TestApexMinSdkVersion_MinApiForArch(t *testing.T) {
+	// Tests that an apex dependency with min_sdk_version higher than the
+	// min_sdk_version of the apex is allowed as long as the dependency's
+	// min_sdk_version is less than or equal to the api level that the
+	// architecture was introduced in.  In this case, arm64 didn't exist
+	// until api level 21, so the arm64 code will never need to run on
+	// an api level 20 device, even if other architectures of the apex
+	// will.
+	testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			min_sdk_version: "20",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			srcs: ["mylib.cpp"],
+			apex_available: ["myapex"],
+			min_sdk_version: "21",
+			stl: "none",
+		}
+	`)
+}
+
 func TestJavaStableSdkVersion(t *testing.T) {
 	testCases := []struct {
 		name          string
@@ -7235,12 +7267,18 @@
 	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 3 {
-		t.Fatalf("Expected 3 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 4 {
+		t.Fatalf("Expected 4 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 	ensureMatches(t, copyCmds[0], "^rm -rf .*/app/AppSet@TEST.BUILD_ID$")
 	ensureMatches(t, copyCmds[1], "^mkdir -p .*/app/AppSet@TEST.BUILD_ID$")
-	ensureMatches(t, copyCmds[2], "^unzip .*-d .*/app/AppSet@TEST.BUILD_ID .*/AppSet.zip$")
+	ensureMatches(t, copyCmds[2], "^cp -f .*/app/AppSet@TEST.BUILD_ID/AppSet.apk$")
+	ensureMatches(t, copyCmds[3], "^unzip .*-d .*/app/AppSet@TEST.BUILD_ID .*/AppSet.zip$")
+
+	// Ensure that canned_fs_config has an entry for the app set zip file
+	generateFsRule := mod.Rule("generateFsConfig")
+	cmd := generateFsRule.RuleParams.Command
+	ensureContains(t, cmd, "AppSet.zip")
 }
 
 func TestAppSetBundlePrebuilt(t *testing.T) {
@@ -7271,6 +7309,28 @@
 	android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
 }
 
+func TestApexSetApksModuleAssignment(t *testing.T) {
+	ctx := testApex(t, `
+		apex_set {
+			name: "myapex",
+			set: ":myapex_apks_file",
+		}
+
+		filegroup {
+			name: "myapex_apks_file",
+			srcs: ["myapex.apks"],
+		}
+	`)
+
+	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+
+	// Check that the extractor produces the correct apks file from the input module
+	extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.apks"
+	extractedApex := m.Output(extractorOutput)
+
+	android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
+}
+
 func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) {
 	t.Helper()
 
diff --git a/apex/builder.go b/apex/builder.go
index b95b3bd..ad8075b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -459,8 +459,13 @@
 			pathOnDevice := filepath.Join("/system", fi.path())
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
+			// Copy the file into APEX
+			copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
+
 			var installedPath android.InstallPath
 			if fi.class == appSet {
+				// In case of AppSet, we need to copy additional APKs as well. They
+				// are zipped. So we need to unzip them.
 				copyCommands = append(copyCommands,
 					fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
 						fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
@@ -469,7 +474,6 @@
 						fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs())
 				}
 			} else {
-				copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
 				if installSymbolFiles {
 					installedPath = ctx.InstallFile(pathWhenActivated.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
 				}
@@ -1086,8 +1090,11 @@
 				executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
 			}
 		} else if f.class == appSet {
+			// base APK
+			readOnlyPaths = append(readOnlyPaths, pathInApex)
+			// Additional APKs
 			appSetDirs = append(appSetDirs, f.installDir)
-			appSetFiles[f.installDir] = f.builtFile
+			appSetFiles[f.installDir] = f.module.(*java.AndroidAppSet).PackedAdditionalOutputs()
 		} else {
 			readOnlyPaths = append(readOnlyPaths, pathInApex)
 		}
diff --git a/apex/key.go b/apex/key.go
index d449589..2b09f1d 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -44,8 +44,6 @@
 
 	publicKeyFile  android.Path
 	privateKeyFile android.Path
-
-	keyName string
 }
 
 type apexKeyProperties struct {
@@ -102,7 +100,6 @@
 			m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
 		return
 	}
-	m.keyName = pubKeyName
 }
 
 // //////////////////////////////////////////////////////////////////////
@@ -203,8 +200,11 @@
 // For Bazel / bp2build
 
 type bazelApexKeyAttributes struct {
-	Public_key  bazel.LabelAttribute
-	Private_key bazel.LabelAttribute
+	Public_key      bazel.LabelAttribute
+	Public_key_name bazel.LabelAttribute
+
+	Private_key      bazel.LabelAttribute
+	Private_key_name bazel.LabelAttribute
 }
 
 // ConvertWithBp2build performs conversion apexKey for bp2build
@@ -214,18 +214,33 @@
 
 func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
 	var privateKeyLabelAttribute bazel.LabelAttribute
+	var privateKeyNameAttribute bazel.LabelAttribute
 	if module.properties.Private_key != nil {
-		privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
+		m := String(module.properties.Private_key)
+		if android.SrcIsModule(m) == "" {
+			privateKeyNameAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
+		} else {
+			privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.Private_key))
+		}
 	}
 
 	var publicKeyLabelAttribute bazel.LabelAttribute
+	var publicKeyNameAttribute bazel.LabelAttribute
 	if module.properties.Public_key != nil {
-		publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
+		m := String(module.properties.Public_key)
+		if android.SrcIsModule(m) == "" {
+			publicKeyNameAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
+		} else {
+			publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.Public_key))
+		}
 	}
 
 	attrs := &bazelApexKeyAttributes{
-		Private_key: privateKeyLabelAttribute,
-		Public_key:  publicKeyLabelAttribute,
+		Private_key:      privateKeyLabelAttribute,
+		Private_key_name: privateKeyNameAttribute,
+
+		Public_key:      publicKeyLabelAttribute,
+		Public_key_name: publicKeyNameAttribute,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 172a201..70308c8 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -500,6 +500,9 @@
 		Arm64 struct {
 			Src *string `android:"path"`
 		}
+		Riscv64 struct {
+			Src *string `android:"path"`
+		}
 		X86 struct {
 			Src *string `android:"path"`
 		}
@@ -527,6 +530,8 @@
 		src = String(p.Arch.Arm.Src)
 	case android.Arm64:
 		src = String(p.Arch.Arm64.Src)
+	case android.Riscv64:
+		src = String(p.Arch.Riscv64.Src)
 	case android.X86:
 		src = String(p.Arch.X86.Src)
 	case android.X86_64:
@@ -537,7 +542,11 @@
 	}
 
 	if src == "" {
-		ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+		if ctx.Config().AllowMissingDependencies() {
+			ctx.AddMissingDependencies([]string{ctx.OtherModuleName(prebuilt)})
+		} else {
+			ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+		}
 		// Drop through to return an empty string as the src (instead of nil) to avoid the prebuilt
 		// logic from reporting a more general, less useful message.
 	}
@@ -840,17 +849,17 @@
 
 type ApexExtractorProperties struct {
 	// the .apks file path that contains prebuilt apex files to be extracted.
-	Set *string
+	Set *string `android:"path"`
 
 	Sanitized struct {
 		None struct {
-			Set *string
+			Set *string `android:"path"`
 		}
 		Address struct {
-			Set *string
+			Set *string `android:"path"`
 		}
 		Hwaddress struct {
-			Set *string
+			Set *string `android:"path"`
 		}
 	}
 
diff --git a/bazel/configurability.go b/bazel/configurability.go
index b6bbd67..a93aa00 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -23,10 +23,11 @@
 
 const (
 	// ArchType names in arch.go
-	archArm    = "arm"
-	archArm64  = "arm64"
-	archX86    = "x86"
-	archX86_64 = "x86_64"
+	archArm     = "arm"
+	archArm64   = "arm64"
+	archRiscv64 = "riscv64"
+	archX86     = "x86"
+	archX86_64  = "x86_64"
 
 	// OsType names in arch.go
 	OsAndroid     = "android"
@@ -39,6 +40,7 @@
 	// Targets in arch.go
 	osArchAndroidArm        = "android_arm"
 	osArchAndroidArm64      = "android_arm64"
+	osArchAndroidRiscv64    = "android_riscv64"
 	osArchAndroidX86        = "android_x86"
 	osArchAndroidX86_64     = "android_x86_64"
 	osArchDarwinArm64       = "darwin_arm64"
@@ -99,6 +101,7 @@
 		"arm64": {
 			"dotprod",
 		},
+		"riscv64": {},
 		"x86": {
 			"ssse3",
 			"sse4",
@@ -164,6 +167,7 @@
 	platformOsArchMap = map[string]string{
 		osArchAndroidArm:           "//build/bazel/platforms/os_arch:android_arm",
 		osArchAndroidArm64:         "//build/bazel/platforms/os_arch:android_arm64",
+		osArchAndroidRiscv64:       "//build/bazel/platforms/os_arch:android_riscv64",
 		osArchAndroidX86:           "//build/bazel/platforms/os_arch:android_x86",
 		osArchAndroidX86_64:        "//build/bazel/platforms/os_arch:android_x86_64",
 		osArchDarwinArm64:          "//build/bazel/platforms/os_arch:darwin_arm64",
@@ -187,7 +191,7 @@
 	// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
 	// in a cyclic dependency.
 	osToArchMap = map[string][]string{
-		OsAndroid:     {archArm, archArm64, archX86, archX86_64},
+		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
 		osLinux:       {archX86, archX86_64},
 		osLinuxMusl:   {archX86, archX86_64},
 		osDarwin:      {archArm64, archX86_64},
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 3b06f85..fa73fb2 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -7,10 +7,11 @@
 )
 
 var (
-	GetOutputFiles  = &getOutputFilesRequestType{}
-	GetPythonBinary = &getPythonBinaryRequestType{}
-	GetCcInfo       = &getCcInfoType{}
-	GetApexInfo     = &getApexInfoType{}
+	GetOutputFiles      = &getOutputFilesRequestType{}
+	GetPythonBinary     = &getPythonBinaryRequestType{}
+	GetCcInfo           = &getCcInfoType{}
+	GetApexInfo         = &getApexInfoType{}
+	GetCcUnstrippedInfo = &getCcUnstippedInfoType{}
 )
 
 type CcInfo struct {
@@ -30,6 +31,7 @@
 	// but general cc_library will also have dynamic libraries in output files).
 	RootDynamicLibraries []string
 	TocFile              string
+	UnstrippedOutput     string
 }
 
 type getOutputFilesRequestType struct{}
@@ -135,12 +137,17 @@
 rootSharedLibraries = []
 
 shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo"
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+unstripped = ""
 
 if shared_info_tag in providers(target):
   shared_info = providers(target)[shared_info_tag]
   path = shared_info.output_file.path
   sharedLibraries.append(path)
   rootSharedLibraries += [path]
+  unstripped = path
+  if unstripped_tag in providers(target):
+    unstripped = providers(target)[unstripped_tag].unstripped.path
 else:
   for linker_input in linker_inputs:
     for library in linker_input.libraries:
@@ -168,7 +175,8 @@
 	"Headers": headers,
 	"RootStaticArchives": rootStaticArchives,
 	"RootDynamicLibraries": rootSharedLibraries,
-	"TocFile": toc_file
+	"TocFile": toc_file,
+	"UnstrippedOutput": unstripped,
 })`
 
 }
@@ -178,10 +186,8 @@
 // Starlark given in StarlarkFunctionBody.
 func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
 	var ccInfo CcInfo
-	decoder := json.NewDecoder(strings.NewReader(rawString))
-	decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
-	err := decoder.Decode(&ccInfo)
-	return ccInfo, err
+	parseJson(rawString, &ccInfo)
+	return ccInfo, nil
 }
 
 // Query Bazel for the artifacts generated by the apex modules.
@@ -226,14 +232,47 @@
 // Starlark given in StarlarkFunctionBody.
 func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo {
 	var info ApexCqueryInfo
-	decoder := json.NewDecoder(strings.NewReader(rawString))
-	decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
-	if err := decoder.Decode(&info); err != nil {
-		panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err))
-	}
+	parseJson(rawString, &info)
 	return info
 }
 
+// getCcUnstrippedInfoType implements cqueryRequest interface. It handles the
+// interaction with `bazel cquery` to retrieve CcUnstrippedInfo provided
+// by the` cc_binary` and `cc_shared_library` rules.
+type getCcUnstippedInfoType struct{}
+
+func (g getCcUnstippedInfoType) Name() string {
+	return "getCcUnstrippedInfo"
+}
+
+func (g getCcUnstippedInfoType) StarlarkFunctionBody() string {
+	return `unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+p = providers(target)
+output_path = target.files.to_list()[0].path
+unstripped = output_path
+if unstripped_tag in p:
+    unstripped = p[unstripped_tag].unstripped.files.to_list()[0].path
+return json_encode({
+    "OutputFile":  output_path,
+    "UnstrippedOutput": unstripped,
+})
+`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getCcUnstippedInfoType) ParseResult(rawString string) CcUnstrippedInfo {
+	var info CcUnstrippedInfo
+	parseJson(rawString, &info)
+	return info
+}
+
+type CcUnstrippedInfo struct {
+	OutputFile       string
+	UnstrippedOutput string
+}
+
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
 // if the given string is empty.
 func splitOrEmpty(s string, sep string) []string {
@@ -243,3 +282,13 @@
 		return strings.Split(s, sep)
 	}
 }
+
+// parseJson decodes json string into the fields of the receiver.
+// Unknown attribute name causes panic.
+func parseJson(jsonString string, info interface{}) {
+	decoder := json.NewDecoder(strings.NewReader(jsonString))
+	decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
+	if err := decoder.Decode(info); err != nil {
+		panic(fmt.Errorf("cannot parse cquery result '%s': %s", jsonString, err))
+	}
+}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index afe478b..0f51cc0 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -165,3 +165,31 @@
 		}
 	}
 }
+
+func TestGetCcUnstrippedParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput CcUnstrippedInfo
+	}{
+		{
+			description:    "no result",
+			input:          "{}",
+			expectedOutput: CcUnstrippedInfo{},
+		},
+		{
+			description: "one result",
+			input:       `{"OutputFile":"myapp", "UnstrippedOutput":"myapp_unstripped"}`,
+			expectedOutput: CcUnstrippedInfo{
+				OutputFile:       "myapp",
+				UnstrippedOutput: "myapp_unstripped",
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetCcUnstrippedInfo.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
diff --git a/bazel/properties.go b/bazel/properties.go
index c329e41..823cda8 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -73,6 +73,17 @@
 	}
 }
 
+// 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 {
+	labels := []Label{}
+	for _, name := range targetNames {
+		label := Label{Label: ":" + name}
+		labels = append(labels, label)
+	}
+	return MakeLabelList(labels)
+}
+
 func (ll *LabelList) Equals(other LabelList) bool {
 	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
 		return false
@@ -1178,6 +1189,11 @@
 	ConfigurableValues configurableStringLists
 }
 
+// IsEmpty returns true if the attribute has no values under any configuration.
+func (sla StringListAttribute) IsEmpty() bool {
+	return len(sla.Value) == 0 && !sla.HasConfigurableValues()
+}
+
 type configurableStringLists map[ConfigurationAxis]stringListSelectValues
 
 func (csl configurableStringLists) Append(other configurableStringLists) {
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index b0a2966..4fd6e43 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -120,7 +120,7 @@
 	file_contexts: ":com.android.apogee-file_contexts",
 	min_sdk_version: "29",
 	key: "com.android.apogee.key",
-	certificate: "com.android.apogee.certificate",
+	certificate: ":com.android.apogee.certificate",
 	updatable: false,
 	installable: false,
 	compressible: false,
@@ -277,23 +277,23 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_32": `[
-        ":native_shared_lib_1",
-        ":native_shared_lib_3",
+        ":native_shared_lib_for_both",
+        ":native_shared_lib_for_lib32",
     ] + select({
-        "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
-        "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+        "//build/bazel/platforms/arch:arm": [":native_shared_lib_for_first"],
+        "//build/bazel/platforms/arch:x86": [":native_shared_lib_for_first"],
         "//conditions:default": [],
     })`,
 				"native_shared_libs_64": `select({
         "//build/bazel/platforms/arch:arm64": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_4",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib64",
+            ":native_shared_lib_for_first",
         ],
         "//build/bazel/platforms/arch:x86_64": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_4",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib64",
+            ":native_shared_lib_for_first",
         ],
         "//conditions:default": [],
     })`,
@@ -322,27 +322,27 @@
 			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_32": `select({
         "//build/bazel/platforms/arch:arm": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_3",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib32",
+            ":native_shared_lib_for_first",
         ],
         "//build/bazel/platforms/arch:x86": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_3",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib32",
+            ":native_shared_lib_for_first",
         ],
         "//conditions:default": [],
     })`,
 				"native_shared_libs_64": `select({
         "//build/bazel/platforms/arch:arm64": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_4",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib64",
+            ":native_shared_lib_for_first",
         ],
         "//build/bazel/platforms/arch:x86_64": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_4",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib64",
+            ":native_shared_lib_for_first",
         ],
         "//conditions:default": [],
     })`,
@@ -370,11 +370,11 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_32": `[
-        ":native_shared_lib_1",
-        ":native_shared_lib_3",
+        ":native_shared_lib_for_both",
+        ":native_shared_lib_for_lib32",
     ] + select({
-        "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
-        "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+        "//build/bazel/platforms/arch:arm": [":native_shared_lib_for_first"],
+        "//build/bazel/platforms/arch:x86": [":native_shared_lib_for_first"],
         "//conditions:default": [],
     })`,
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
@@ -402,14 +402,14 @@
 			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_64": `select({
         "//build/bazel/platforms/arch:arm64": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_4",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib64",
+            ":native_shared_lib_for_first",
         ],
         "//build/bazel/platforms/arch:x86_64": [
-            ":native_shared_lib_1",
-            ":native_shared_lib_4",
-            ":native_shared_lib_2",
+            ":native_shared_lib_for_both",
+            ":native_shared_lib_for_lib64",
+            ":native_shared_lib_for_first",
         ],
         "//conditions:default": [],
     })`,
@@ -419,6 +419,56 @@
 		}})
 }
 
+func createMultilibBlueprint(compile_multilib string) string {
+	return `
+cc_library {
+	name: "native_shared_lib_for_both",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_for_first",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_for_lib32",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_for_lib64",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	compile_multilib: "` + compile_multilib + `",
+	multilib: {
+		both: {
+			native_shared_libs: [
+				"native_shared_lib_for_both",
+			],
+		},
+		first: {
+			native_shared_libs: [
+				"native_shared_lib_for_first",
+			],
+		},
+		lib32: {
+			native_shared_libs: [
+				"native_shared_lib_for_lib32",
+			],
+		},
+		lib64: {
+			native_shared_libs: [
+				"native_shared_lib_for_lib64",
+			],
+		},
+	},
+}`
+}
+
 func TestApexBundleDefaultPropertyValues(t *testing.T) {
 	runApexTestCase(t, Bp2buildTestCase{
 		Description:                "apex - default property values",
@@ -474,56 +524,6 @@
 		}})
 }
 
-func createMultilibBlueprint(compile_multilib string) string {
-	return `
-cc_library {
-	name: "native_shared_lib_1",
-	bazel_module: { bp2build_available: false },
-}
-
-cc_library {
-	name: "native_shared_lib_2",
-	bazel_module: { bp2build_available: false },
-}
-
-cc_library {
-	name: "native_shared_lib_3",
-	bazel_module: { bp2build_available: false },
-}
-
-cc_library {
-	name: "native_shared_lib_4",
-	bazel_module: { bp2build_available: false },
-}
-
-apex {
-	name: "com.android.apogee",
-	compile_multilib: "` + compile_multilib + `",
-	multilib: {
-		both: {
-			native_shared_libs: [
-				"native_shared_lib_1",
-			],
-		},
-		first: {
-			native_shared_libs: [
-				"native_shared_lib_2",
-			],
-		},
-		lib32: {
-			native_shared_libs: [
-				"native_shared_lib_3",
-			],
-		},
-		lib64: {
-			native_shared_libs: [
-				"native_shared_lib_4",
-			],
-		},
-	},
-}`
-}
-
 func TestBp2BuildOverrideApex(t *testing.T) {
 	runOverrideApexTestCase(t, Bp2buildTestCase{
 		Description:                "override_apex",
@@ -582,7 +582,7 @@
 	file_contexts: ":com.android.apogee-file_contexts",
 	min_sdk_version: "29",
 	key: "com.android.apogee.key",
-	certificate: "com.android.apogee.certificate",
+	certificate: ":com.android.apogee.certificate",
 	updatable: false,
 	installable: false,
 	compressible: false,
@@ -618,7 +618,7 @@
 	name: "com.google.android.apogee",
 	base: ":com.android.apogee",
 	key: "com.google.android.apogee.key",
-	certificate: "com.google.android.apogee.certificate",
+	certificate: ":com.google.android.apogee.certificate",
 	prebuilts: [],
 	compressible: true,
 }
@@ -1016,3 +1016,193 @@
 			}),
 		}})
 }
+
+func TestBp2BuildOverrideApex_CertificateNil(t *testing.T) {
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "override_apex - don't set default certificate",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	// certificate is deliberately omitted, and not converted to bazel,
+	// because the overridden apex shouldn't be using the base apex's cert.
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"file_contexts": `":com.android.apogee-file_contexts"`,
+				"manifest":      `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestApexCertificateIsModule(t *testing.T) {
+	runApexTestCase(t, Bp2buildTestCase{
+		Description:                "apex - certificate is module",
+		ModuleTypeUnderTest:        "apex",
+		ModuleTypeUnderTestFactory: apex.BundleFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+				"certificate":   `":com.android.apogee.certificate"`,
+				"file_contexts": `":com.android.apogee-file_contexts"`,
+				"manifest":      `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestApexCertificateIsSrc(t *testing.T) {
+	runApexTestCase(t, Bp2buildTestCase{
+		Description:                "apex - certificate is src",
+		ModuleTypeUnderTest:        "apex",
+		ModuleTypeUnderTestFactory: apex.BundleFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: "com.android.apogee.certificate",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+				"certificate_name": `"com.android.apogee.certificate"`,
+				"file_contexts":    `":com.android.apogee-file_contexts"`,
+				"manifest":         `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestBp2BuildOverrideApex_CertificateIsModule(t *testing.T) {
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "override_apex - certificate is module",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+	bazel_module: { bp2build_available: false },
+}
+
+android_app_certificate {
+	name: "com.google.android.apogee.certificate",
+	certificate: "com.google.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	certificate: ":com.google.android.apogee.certificate",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"file_contexts": `":com.android.apogee-file_contexts"`,
+				"certificate":   `":com.google.android.apogee.certificate"`,
+				"manifest":      `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestBp2BuildOverrideApex_CertificateIsSrc(t *testing.T) {
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "override_apex - certificate is src",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	certificate: "com.google.android.apogee.certificate",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"file_contexts":    `":com.android.apogee-file_contexts"`,
+				"certificate_name": `"com.google.android.apogee.certificate"`,
+				"manifest":         `"apogee_manifest.json"`,
+			}),
+		}})
+}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 15bccf7..79b1d89 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -27,11 +27,12 @@
 }
 
 func registerApexKeyModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 }
 
-func TestApexKeySimple(t *testing.T) {
+func TestApexKeySimple_KeysAreSrcFiles(t *testing.T) {
 	runApexKeyTestCase(t, Bp2buildTestCase{
-		Description:                "apex key - simple example",
+		Description:                "apex key - keys are src files, use key_name attributes",
 		ModuleTypeUnderTest:        "apex_key",
 		ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
 		Filesystem:                 map[string]string{},
@@ -43,8 +44,29 @@
 }
 `,
 		ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{
-			"private_key": `"com.android.apogee.pem"`,
-			"public_key":  `"com.android.apogee.avbpubkey"`,
+			"private_key_name": `"com.android.apogee.pem"`,
+			"public_key_name":  `"com.android.apogee.avbpubkey"`,
+		}),
+		}})
+}
+
+func TestApexKey_KeysAreModules(t *testing.T) {
+	runApexKeyTestCase(t, Bp2buildTestCase{
+		Description:                "apex key - keys are modules, use key attributes",
+		ModuleTypeUnderTest:        "apex_key",
+		ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+apex_key {
+        name: "com.android.apogee.key",
+        public_key: ":com.android.apogee.avbpubkey",
+        private_key: ":com.android.apogee.pem",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.avbpubkey") +
+			simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.pem"),
+		ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{
+			"private_key": `":com.android.apogee.pem"`,
+			"public_key":  `":com.android.apogee.avbpubkey"`,
 		}),
 		}})
 }
diff --git a/bp2build/api_domain_conversion_test.go b/bp2build/api_domain_conversion_test.go
index fc9d1d5..224008f 100644
--- a/bp2build/api_domain_conversion_test.go
+++ b/bp2build/api_domain_conversion_test.go
@@ -60,7 +60,7 @@
 			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 		},
 	)
-	RunBp2BuildTestCase(t, registerApiDomainModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, registerApiDomainModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
 		Filesystem:           fs,
diff --git a/bp2build/bpf_conversion_test.go b/bp2build/bpf_conversion_test.go
new file mode 100644
index 0000000..1259f9e
--- /dev/null
+++ b/bp2build/bpf_conversion_test.go
@@ -0,0 +1,65 @@
+// Copyright 2022 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 (
+	"android/soong/android"
+	"android/soong/bpf"
+
+	"testing"
+)
+
+func runBpfTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "bpf"
+	(&tc).ModuleTypeUnderTestFactory = bpf.BpfFactory
+	RunBp2BuildTestCase(t, registerBpfModuleTypes, tc)
+}
+
+func registerBpfModuleTypes(ctx android.RegistrationContext) {}
+
+func TestBpfSupportedAttrs(t *testing.T) {
+	runBpfTestCase(t, Bp2buildTestCase{
+		Description: "Bpf module only converts supported attributes",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+bpf {
+    name: "bpfTestOut.o",
+    srcs: ["bpfTestSrcOne.c",
+           "bpfTestSrcTwo.c"],
+    btf: true,
+    cflags: ["-bpfCflagOne",
+             "-bpfCflagTwo"],
+    include_dirs: ["ia/ib/ic"],
+    sub_dir: "sa/ab",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("bpf", "bpfTestOut.o", AttrNameToString{
+				"absolute_includes": `["ia/ib/ic"]`,
+				"btf":               `True`,
+				"copts": `[
+        "-bpfCflagOne",
+        "-bpfCflagTwo",
+    ]`,
+				"srcs": `[
+        "bpfTestSrcOne.c",
+        "bpfTestSrcTwo.c",
+    ]`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index ee162b2..82ce115 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -163,6 +163,9 @@
 	// This mode is used for discovering and introspecting the existing Soong
 	// module graph.
 	QueryView
+
+	// ApiBp2build - generate BUILD files for API contribution targets
+	ApiBp2build
 )
 
 type unconvertedDepsMode int
@@ -181,6 +184,8 @@
 		return "Bp2Build"
 	case QueryView:
 		return "QueryView"
+	case ApiBp2build:
+		return "ApiBp2build"
 	default:
 		return fmt.Sprintf("%d", mode)
 	}
@@ -242,6 +247,7 @@
 	// Simple metrics tracking for bp2build
 	metrics := CodegenMetrics{
 		ruleClassCount:           make(map[string]uint64),
+		convertedModulePathMap:   make(map[string]string),
 		convertedModuleTypeCount: make(map[string]uint64),
 		totalModuleTypeCount:     make(map[string]uint64),
 	}
@@ -272,20 +278,21 @@
 				// target in a BUILD file, we don't autoconvert them.
 
 				// Log the module.
-				metrics.AddConvertedModule(m, moduleType, Handcrafted)
+				metrics.AddConvertedModule(m, moduleType, dir, Handcrafted)
 			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
 				// Handle modules converted to generated targets.
 
 				// Log the module.
-				metrics.AddConvertedModule(aModule, moduleType, Generated)
+				metrics.AddConvertedModule(aModule, moduleType, dir, Generated)
 
 				// Handle modules with unconverted deps. By default, emit a warning.
 				if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
 					msg := fmt.Sprintf("%s %s:%s depends on unconverted modules: %s",
 						moduleType, bpCtx.ModuleDir(m), m.Name(), strings.Join(unconvertedDeps, ", "))
-					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+					switch ctx.unconvertedDepMode {
+					case warnUnconvertedDeps:
 						metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg)
-					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+					case errorModulesUnconvertedDeps:
 						errs = append(errs, fmt.Errorf(msg))
 						return
 					}
@@ -293,9 +300,10 @@
 				if unconvertedDeps := aModule.GetMissingBp2buildDeps(); len(unconvertedDeps) > 0 {
 					msg := fmt.Sprintf("%s %s:%s depends on missing modules: %s",
 						moduleType, bpCtx.ModuleDir(m), m.Name(), strings.Join(unconvertedDeps, ", "))
-					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+					switch ctx.unconvertedDepMode {
+					case warnUnconvertedDeps:
 						metrics.moduleWithMissingDepsMsgs = append(metrics.moduleWithMissingDepsMsgs, msg)
-					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+					case errorModulesUnconvertedDeps:
 						errs = append(errs, fmt.Errorf(msg))
 						return
 					}
@@ -324,6 +332,10 @@
 				errs = append(errs, err)
 			}
 			targets = append(targets, t)
+		case ApiBp2build:
+			if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				targets, errs = generateBazelTargets(bpCtx, aModule)
+			}
 		default:
 			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
 			return
@@ -348,7 +360,7 @@
 		// TODO(b/198619163): We should change this to export_files(glob(["**/*"])) instead, but doing that causes these errors:
 		// "Error in exports_files: generated label '//external/avb:avbtool' conflicts with existing py_binary rule"
 		// So we need to solve all the "target ... is both a rule and a file" warnings first.
-		for dir, _ := range dirs {
+		for dir := range dirs {
 			buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
 				name:      "bp2build_all_srcs",
 				content:   `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]))`,
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index dd28c3c..7c24a94 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -369,6 +369,7 @@
       x86_64:  { arch_paths: ["x86_64.txt"] },
       arm:  { arch_paths: ["arm.txt"] },
       arm64:  { arch_paths: ["arm64.txt"] },
+      riscv64: { arch_paths: ["riscv64.txt"] },
     },
     target: {
       linux: { arch_paths: ["linux.txt"] },
@@ -401,6 +402,10 @@
             "arm64.txt",
             "lib64.txt",
         ],
+        "//build/bazel/platforms/arch:riscv64": [
+            "riscv64.txt",
+            "lib64.txt",
+        ],
         "//build/bazel/platforms/arch:x86": [
             "x86.txt",
             "lib32.txt",
@@ -971,6 +976,24 @@
 			},
 		},
 		{
+			Description:                "filegroup with dot-slash-prefixed srcs",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Blueprint: `filegroup {
+    name: "fg_foo",
+    srcs: ["./a", "./b"],
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a",
+        "b",
+    ]`,
+				}),
+			},
+		},
+		{
 			Description:                "filegroup with excludes srcs",
 			ModuleTypeUnderTest:        "filegroup",
 			ModuleTypeUnderTestFactory: android.FileGroupFactory,
@@ -987,57 +1010,6 @@
 			},
 		},
 		{
-			Description:                "filegroup with glob",
-			ModuleTypeUnderTest:        "filegroup",
-			ModuleTypeUnderTestFactory: android.FileGroupFactory,
-			Blueprint: `filegroup {
-    name: "fg_foo",
-    srcs: ["**/*.txt"],
-    bazel_module: { bp2build_available: true },
-}`,
-			ExpectedBazelTargets: []string{
-				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
-					"srcs": `[
-        "other/a.txt",
-        "other/b.txt",
-        "other/subdir/a.txt",
-    ]`,
-				}),
-			},
-			Filesystem: map[string]string{
-				"other/a.txt":        "",
-				"other/b.txt":        "",
-				"other/subdir/a.txt": "",
-				"other/file":         "",
-			},
-		},
-		{
-			Description:                "filegroup with glob in subdir",
-			ModuleTypeUnderTest:        "filegroup",
-			ModuleTypeUnderTestFactory: android.FileGroupFactory,
-			Dir:                        "other",
-			Filesystem: map[string]string{
-				"other/Android.bp": `filegroup {
-    name: "fg_foo",
-    srcs: ["**/*.txt"],
-    bazel_module: { bp2build_available: true },
-}`,
-				"other/a.txt":        "",
-				"other/b.txt":        "",
-				"other/subdir/a.txt": "",
-				"other/file":         "",
-			},
-			ExpectedBazelTargets: []string{
-				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
-					"srcs": `[
-        "a.txt",
-        "b.txt",
-        "subdir/a.txt",
-    ]`,
-				}),
-			},
-		},
-		{
 			Description:                "depends_on_other_dir_module",
 			ModuleTypeUnderTest:        "filegroup",
 			ModuleTypeUnderTestFactory: android.FileGroupFactory,
@@ -1086,6 +1058,29 @@
 }`,
 			},
 		},
+		{
+			Description:                "depends_on_other_missing_module_error",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			UnconvertedDepsMode:        errorModulesUnconvertedDeps,
+			Blueprint: `filegroup {
+    name: "foobar",
+    srcs: [
+        "c",
+        "//other:foo",
+        "//other:goo",
+    ],
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedErr: fmt.Errorf(`filegroup .:foobar depends on missing modules: //other:goo`),
+			Filesystem: map[string]string{"other/Android.bp": `filegroup {
+    name: "foo",
+    srcs: ["a"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+			},
+		},
 	}
 
 	for _, testCase := range testCases {
@@ -1095,8 +1090,6 @@
 	}
 }
 
-type bp2buildMutator = func(android.TopDownMutatorContext)
-
 func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
 	testCases := []struct {
 		moduleTypeUnderTest        string
@@ -1429,6 +1422,226 @@
 	}
 }
 
+func TestGlob(t *testing.T) {
+	testCases := []Bp2buildTestCase{
+		{
+			Description:                "filegroup with glob",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Blueprint: `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "other/a.txt",
+        "other/b.txt",
+        "other/subdir/a.txt",
+    ]`,
+				}),
+			},
+			Filesystem: map[string]string{
+				"other/a.txt":        "",
+				"other/b.txt":        "",
+				"other/subdir/a.txt": "",
+				"other/file":         "",
+			},
+		},
+		{
+			Description:                "filegroup with glob in subdir",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Dir:                        "other",
+			Filesystem: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+				"other/a.txt":        "",
+				"other/b.txt":        "",
+				"other/subdir/a.txt": "",
+				"other/file":         "",
+			},
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a.txt",
+        "b.txt",
+        "subdir/a.txt",
+    ]`,
+				}),
+			},
+		},
+		{
+			Description:                "filegroup with glob with no kept BUILD files",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			KeepBuildFileForDirs:       []string{
+				// empty
+			},
+			Blueprint: `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			Filesystem: map[string]string{
+				"a.txt":         "",
+				"b.txt":         "",
+				"foo/BUILD":     "",
+				"foo/a.txt":     "",
+				"foo/bar/BUILD": "",
+				"foo/bar/b.txt": "",
+			},
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a.txt",
+        "b.txt",
+        "foo/a.txt",
+        "foo/bar/b.txt",
+    ]`,
+				}),
+			},
+		},
+		{
+			Description:                "filegroup with glob with kept BUILD file",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			KeepBuildFileForDirs: []string{
+				"foo",
+			},
+			Blueprint: `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			Filesystem: map[string]string{
+				"a.txt":         "",
+				"b.txt":         "",
+				"foo/BUILD":     "",
+				"foo/a.txt":     "",
+				"foo/bar/BUILD": "",
+				"foo/bar/b.txt": "",
+			},
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a.txt",
+        "b.txt",
+        "//foo:a.txt",
+        "//foo:bar/b.txt",
+    ]`,
+				}),
+			},
+		},
+		{
+			Description:                "filegroup with glob with kept BUILD.bazel file",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			KeepBuildFileForDirs: []string{
+				"foo",
+			},
+			Blueprint: `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			Filesystem: map[string]string{
+				"a.txt":               "",
+				"b.txt":               "",
+				"foo/BUILD.bazel":     "",
+				"foo/a.txt":           "",
+				"foo/bar/BUILD.bazel": "",
+				"foo/bar/b.txt":       "",
+			},
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a.txt",
+        "b.txt",
+        "//foo:a.txt",
+        "//foo:bar/b.txt",
+    ]`,
+				}),
+			},
+		},
+		{
+			Description:                "filegroup with glob with Android.bp file as boundary",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Blueprint: `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+			Filesystem: map[string]string{
+				"a.txt":              "",
+				"b.txt":              "",
+				"foo/Android.bp":     "",
+				"foo/a.txt":          "",
+				"foo/bar/Android.bp": "",
+				"foo/bar/b.txt":      "",
+			},
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a.txt",
+        "b.txt",
+        "//foo:a.txt",
+        "//foo/bar:b.txt",
+    ]`,
+				}),
+			},
+		},
+		{
+			Description:                "filegroup with glob in subdir with kept BUILD and BUILD.bazel file",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Dir:                        "other",
+			KeepBuildFileForDirs: []string{
+				"other/foo",
+				"other/foo/bar",
+				// deliberately not other/foo/baz/BUILD.
+			},
+			Filesystem: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "fg_foo",
+    srcs: ["**/*.txt"],
+    bazel_module: { bp2build_available: true },
+}`,
+				"other/a.txt":               "",
+				"other/b.txt":               "",
+				"other/foo/BUILD":           "",
+				"other/foo/a.txt":           "",
+				"other/foo/bar/BUILD.bazel": "",
+				"other/foo/bar/b.txt":       "",
+				"other/foo/baz/BUILD":       "",
+				"other/foo/baz/c.txt":       "",
+			},
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"srcs": `[
+        "a.txt",
+        "b.txt",
+        "//other/foo:a.txt",
+        "//other/foo/bar:b.txt",
+        "//other/foo:baz/c.txt",
+    ]`,
+				}),
+			},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.Description, func(t *testing.T) {
+			RunBp2BuildTestCaseSimple(t, testCase)
+		})
+	}
+}
+
 func TestGlobExcludeSrcs(t *testing.T) {
 	testCases := []Bp2buildTestCase{
 		{
@@ -1518,6 +1731,22 @@
 			},
 		},
 		{
+			Description:                "Required into data test, cyclic self reference is filtered out",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
+filegroup {
+    name: "fg_foo",
+    required: ["reqd", "fg_foo"],
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
+					"data": `[":reqd"]`,
+				}),
+			},
+		},
+		{
 			Description:                "Required via arch into data test",
 			ModuleTypeUnderTest:        "python_library",
 			ModuleTypeUnderTestFactory: python.PythonLibraryFactory,
@@ -1597,3 +1826,54 @@
 		})
 	}
 }
+
+func TestLicensesAttrConversion(t *testing.T) {
+	RunBp2BuildTestCase(t,
+		func(ctx android.RegistrationContext) {
+			ctx.RegisterModuleType("license", android.LicenseFactory)
+		},
+		Bp2buildTestCase{
+			Description:                "Test that licenses: attribute is converted",
+			ModuleTypeUnderTest:        "filegroup",
+			ModuleTypeUnderTestFactory: android.FileGroupFactory,
+			Blueprint: `
+license {
+    name: "my_license",
+}
+filegroup {
+    name: "my_filegroup",
+    licenses: ["my_license"],
+}
+`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTargetNoRestrictions("filegroup", "my_filegroup", AttrNameToString{
+					"applicable_licenses": `[":my_license"]`,
+				}),
+				MakeBazelTargetNoRestrictions("android_license", "my_license", AttrNameToString{}),
+			},
+		})
+}
+
+func TestGenerateApiBazelTargets(t *testing.T) {
+	bp := `
+	custom {
+		name: "foo",
+		api: "foo.txt",
+	}
+	`
+	expectedBazelTarget := MakeBazelTarget(
+		"custom_api_contribution",
+		"foo",
+		AttrNameToString{
+			"api": `"foo.txt"`,
+		},
+	)
+	registerCustomModule := func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+	}
+	RunApiBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: []string{expectedBazelTarget},
+		Description:          "Generating API contribution Bazel targets for custom module",
+	})
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 28d2c75..a8e557d 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -85,6 +85,7 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "api": attr.string(),
         "arch_paths": attr.string_list(),
         "arch_paths_exclude": attr.string_list(),
         # bazel_module start
@@ -119,6 +120,7 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "api": attr.string(),
         "arch_paths": attr.string_list(),
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
@@ -149,6 +151,7 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "api": attr.string(),
         "arch_paths": attr.string_list(),
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index c23779e..8aa2c3e 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -201,26 +201,65 @@
 	})
 }
 
-func TestCcBinaryVersionScript(t *testing.T) {
+func TestCcBinaryVersionScriptAndDynamicList(t *testing.T) {
 	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
-		description: `version script`,
+		description: `version script and dynamic list`,
 		blueprint: `
 {rule_name} {
     name: "foo",
     include_build_directory: false,
     version_script: "vs",
+    dynamic_list: "dynamic.list",
 }
 `,
 		targets: []testBazelTarget{
 			{"cc_binary", "foo", AttrNameToString{
-				"additional_linker_inputs": `["vs"]`,
-				"linkopts":                 `["-Wl,--version-script,$(location vs)"]`,
+				"additional_linker_inputs": `[
+        "vs",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "-Wl,--version-script,$(location vs)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
 			},
 			},
 		},
 	})
 }
 
+func TestCcBinaryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+		ldflags: [
+			"--nospace_flag",
+			"-z spaceflag",
+		],
+		version_script: "version_script",
+		dynamic_list: "dynamic.list",
+    include_build_directory: false,
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "--nospace_flag",
+        "-z",
+        "spaceflag",
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			}}},
+	})
+}
+
 func TestCcBinarySplitSrcsByLang(t *testing.T) {
 	runCcHostBinaryTestCase(t, ccBinaryBp2buildTestCase{
 		description: "split srcs by lang",
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 1b8e9b4..0d6d5b8 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -870,9 +870,9 @@
 			})}})
 }
 
-func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
+func TestCcLibraryNonConfiguredVersionScriptAndDynamicList(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library non-configured version script",
+		Description:                "cc_library non-configured version script and dynamic list",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Dir:                        "foo/bar",
@@ -882,6 +882,7 @@
     name: "a",
     srcs: ["a.cpp"],
     version_script: "v.map",
+    dynamic_list: "dynamic.list",
     bazel_module: { bp2build_available: true },
     include_build_directory: false,
 }
@@ -889,17 +890,23 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
-			"additional_linker_inputs": `["v.map"]`,
-			"linkopts":                 `["-Wl,--version-script,$(location v.map)"]`,
-			"srcs":                     `["a.cpp"]`,
+			"additional_linker_inputs": `[
+        "v.map",
+        "dynamic.list",
+    ]`,
+			"linkopts": `[
+        "-Wl,--version-script,$(location v.map)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			"srcs": `["a.cpp"]`,
 		}),
 	},
 	)
 }
 
-func TestCcLibraryConfiguredVersionScript(t *testing.T) {
+func TestCcLibraryConfiguredVersionScriptAndDynamicList(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library configured version script",
+		Description:                "cc_library configured version script and dynamic list",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Dir:                        "foo/bar",
@@ -911,9 +918,11 @@
    arch: {
      arm: {
        version_script: "arm.map",
+       dynamic_list: "dynamic_arm.list",
      },
      arm64: {
        version_script: "arm64.map",
+       dynamic_list: "dynamic_arm64.list",
      },
    },
 
@@ -925,13 +934,25 @@
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
 			"additional_linker_inputs": `select({
-        "//build/bazel/platforms/arch:arm": ["arm.map"],
-        "//build/bazel/platforms/arch:arm64": ["arm64.map"],
+        "//build/bazel/platforms/arch:arm": [
+            "arm.map",
+            "dynamic_arm.list",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "arm64.map",
+            "dynamic_arm64.list",
+        ],
         "//conditions:default": [],
     })`,
 			"linkopts": `select({
-        "//build/bazel/platforms/arch:arm": ["-Wl,--version-script,$(location arm.map)"],
-        "//build/bazel/platforms/arch:arm64": ["-Wl,--version-script,$(location arm64.map)"],
+        "//build/bazel/platforms/arch:arm": [
+            "-Wl,--version-script,$(location arm.map)",
+            "-Wl,--dynamic-list,$(location dynamic_arm.list)",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "-Wl,--version-script,$(location arm64.map)",
+            "-Wl,--dynamic-list,$(location dynamic_arm64.list)",
+        ],
         "//conditions:default": [],
     })`,
 			"srcs": `["a.cpp"]`,
@@ -940,6 +961,46 @@
 	)
 }
 
+func TestCcLibraryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"version_script": "",
+			"dynamic.list":   "",
+		},
+		Blueprint: `
+cc_library {
+    name: "foo",
+    ldflags: [
+        "--nospace_flag",
+        "-z spaceflag",
+    ],
+    version_script: "version_script",
+    dynamic_list: "dynamic.list",
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "--nospace_flag",
+        "-z",
+        "spaceflag",
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedLibs(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library shared_libs",
@@ -2682,6 +2743,205 @@
 	)
 }
 
+func TestCcApiContributionsWithHdrs(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libfoo",
+		stubs: { symbol_file: "libfoo.map.txt", versions: ["28", "29", "current"] },
+		llndk: { symbol_file: "libfoo.map.txt", override_export_include_dirs: ["dir2"]},
+		export_include_dirs: ["dir1"],
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTarget(
+			"cc_api_library_headers",
+			"libfoo.systemapi.headers",
+			AttrNameToString{
+				"export_includes": `["dir1"]`,
+			}),
+		MakeBazelTarget(
+			"cc_api_library_headers",
+			"libfoo.vendorapi.headers",
+			AttrNameToString{
+				"export_includes": `["dir2"]`,
+			}),
+		MakeBazelTarget(
+			"cc_api_contribution",
+			"libfoo.contribution",
+			AttrNameToString{
+				"api":          `"libfoo.map.txt"`,
+				"library_name": `"libfoo"`,
+				"api_surfaces": `[
+        "systemapi",
+        "vendorapi",
+    ]`,
+				"hdrs": `[
+        ":libfoo.systemapi.headers",
+        ":libfoo.vendorapi.headers",
+    ]`,
+			}),
+	}
+	RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+		Blueprint:            bp,
+		Description:          "cc API contributions to systemapi and vendorapi",
+		ExpectedBazelTargets: expectedBazelTargets,
+	})
+}
+
+func TestCcApiSurfaceCombinations(t *testing.T) {
+	testCases := []struct {
+		bp                  string
+		expectedApi         string
+		expectedApiSurfaces string
+		description         string
+	}{
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				stubs: {symbol_file: "a.map.txt"},
+			}`,
+			expectedApi:         `"a.map.txt"`,
+			expectedApiSurfaces: `["systemapi"]`,
+			description:         "Library that contributes to systemapi",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				llndk: {symbol_file: "a.map.txt"},
+			}`,
+			expectedApi:         `"a.map.txt"`,
+			expectedApiSurfaces: `["vendorapi"]`,
+			description:         "Library that contributes to vendorapi",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				llndk: {symbol_file: "a.map.txt"},
+				stubs: {symbol_file: "a.map.txt"},
+			}`,
+			expectedApi: `"a.map.txt"`,
+			expectedApiSurfaces: `[
+        "systemapi",
+        "vendorapi",
+    ]`,
+			description: "Library that contributes to systemapi and vendorapi",
+		},
+	}
+	for _, testCase := range testCases {
+		expectedBazelTargets := []string{
+			MakeBazelTarget(
+				"cc_api_contribution",
+				"a.contribution",
+				AttrNameToString{
+					"library_name": `"a"`,
+					"hdrs":         `[]`,
+					"api":          testCase.expectedApi,
+					"api_surfaces": testCase.expectedApiSurfaces,
+				},
+			),
+		}
+		RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+			Blueprint:            testCase.bp,
+			Description:          testCase.description,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	}
+}
+
+// llndk struct property in Soong provides users with several options to configure the exported include dirs
+// Test the generated bazel targets for the different configurations
+func TestCcVendorApiHeaders(t *testing.T) {
+	testCases := []struct {
+		bp                     string
+		expectedIncludes       string
+		expectedSystemIncludes string
+		description            string
+	}{
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				export_include_dirs: ["include"],
+				export_system_include_dirs: ["base_system_include"],
+				llndk: {
+					symbol_file: "a.map.txt",
+					export_headers_as_system: true,
+				},
+			}
+			`,
+			expectedIncludes: "",
+			expectedSystemIncludes: `[
+        "base_system_include",
+        "include",
+    ]`,
+			description: "Headers are exported as system to API surface",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				export_include_dirs: ["include"],
+				export_system_include_dirs: ["base_system_include"],
+				llndk: {
+					symbol_file: "a.map.txt",
+					override_export_include_dirs: ["llndk_include"],
+				},
+			}
+			`,
+			expectedIncludes:       `["llndk_include"]`,
+			expectedSystemIncludes: `["base_system_include"]`,
+			description:            "Non-system Headers are ovverriden before export to API surface",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				export_include_dirs: ["include"],
+				export_system_include_dirs: ["base_system_include"],
+				llndk: {
+					symbol_file: "a.map.txt",
+					override_export_include_dirs: ["llndk_include"],
+					export_headers_as_system: true,
+				},
+			}
+			`,
+			expectedIncludes: "", // includes are set to nil
+			expectedSystemIncludes: `[
+        "base_system_include",
+        "llndk_include",
+    ]`,
+			description: "System Headers are extended before export to API surface",
+		},
+	}
+	for _, testCase := range testCases {
+		attrs := AttrNameToString{}
+		if testCase.expectedIncludes != "" {
+			attrs["export_includes"] = testCase.expectedIncludes
+		}
+		if testCase.expectedSystemIncludes != "" {
+			attrs["export_system_includes"] = testCase.expectedSystemIncludes
+		}
+
+		expectedBazelTargets := []string{
+			MakeBazelTarget("cc_api_library_headers", "a.vendorapi.headers", attrs),
+			// Create a target for cc_api_contribution target
+			MakeBazelTarget("cc_api_contribution", "a.contribution", AttrNameToString{
+				"api":          `"a.map.txt"`,
+				"api_surfaces": `["vendorapi"]`,
+				"hdrs":         `[":a.vendorapi.headers"]`,
+				"library_name": `"a"`,
+			}),
+		}
+		RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+			Blueprint:            testCase.bp,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	}
+}
+
 func TestCcLibraryStubsAcrossConfigsDuplicatesRemoved(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "stub target generation of the same lib across configs should not result in duplicates",
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 5fa7cac..7d9db6f 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -123,6 +123,69 @@
 	})
 }
 
+func TestCcApiHeaders(t *testing.T) {
+	fs := map[string]string{
+		"bar/Android.bp": `cc_library_headers { name: "bar_headers", }`,
+	}
+	bp := `
+	cc_library_headers {
+		name: "foo_headers",
+		export_include_dirs: ["dir1", "dir2"],
+		export_header_lib_headers: ["bar_headers"],
+
+		arch: {
+			arm: {
+				export_include_dirs: ["dir_arm"],
+			},
+			x86: {
+				export_include_dirs: ["dir_x86"],
+			},
+		},
+
+		target: {
+			android: {
+				export_include_dirs: ["dir1", "dir_android"],
+			},
+			windows: {
+				export_include_dirs: ["dir_windows"],
+			},
+		}
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.arm", AttrNameToString{
+			"export_includes": `["dir_arm"]`,
+			"arch":            `"arm"`,
+		}),
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.x86", AttrNameToString{
+			"export_includes": `["dir_x86"]`,
+			"arch":            `"x86"`,
+		}),
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.androidos", AttrNameToString{
+			"export_includes": `["dir_android"]`, // common includes are deduped
+		}),
+		// Windows headers are not exported
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution", AttrNameToString{
+			"export_includes": `[
+        "dir1",
+        "dir2",
+    ]`,
+			"deps": `[
+        "//bar:bar_headers.contribution",
+        ":foo_headers.contribution.arm",
+        ":foo_headers.contribution.x86",
+        ":foo_headers.contribution.androidos",
+    ]`,
+		}),
+	}
+	RunApiBp2BuildTestCase(t, cc.RegisterLibraryHeadersBuildComponents, Bp2buildTestCase{
+		Blueprint:            bp,
+		Description:          "Header library contributions to API surfaces",
+		ExpectedBazelTargets: expectedBazelTargets,
+		Filesystem:           fs,
+	})
+}
+
 func TestCcLibraryHeadersOsSpecificHeader(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library_headers test with os-specific header_libs props",
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index b1a9240..b86f607 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -338,22 +338,66 @@
 	})
 }
 
-func TestCcLibrarySharedVersionScript(t *testing.T) {
+func TestCcLibrarySharedVersionScriptAndDynamicList(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared version script",
+		Description: "cc_library_shared version script and dynamic list",
 		Filesystem: map[string]string{
 			"version_script": "",
+			"dynamic.list":   "",
 		},
 		Blueprint: soongCcLibrarySharedPreamble + `
 cc_library_shared {
     name: "foo_shared",
     version_script: "version_script",
+    dynamic_list: "dynamic.list",
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
-				"additional_linker_inputs": `["version_script"]`,
-				"linkopts":                 `["-Wl,--version-script,$(location version_script)"]`,
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryLdflagsSplitBySpaceSoongAdded(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+		Filesystem: map[string]string{
+			"version_script": "",
+			"dynamic.list":   "",
+		},
+		Blueprint: `
+cc_library_shared {
+    name: "foo",
+    ldflags: [
+        "--nospace_flag",
+        "-z spaceflag",
+    ],
+    version_script: "version_script",
+    dynamic_list: "dynamic.list",
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "--nospace_flag",
+        "-z",
+        "spaceflag",
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
 			}),
 		},
 	})
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index e3ea9a0..b47d1f1 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -836,6 +836,10 @@
             "not-for-lib32.c",
             "for-lib64.c",
         ],
+        "//build/bazel/platforms/arch:riscv64": [
+            "not-for-lib32.c",
+            "for-lib64.c",
+        ],
         "//build/bazel/platforms/arch:x86": [
             "not-for-lib64.c",
             "for-lib32.c",
@@ -867,6 +871,7 @@
 			"for-lib64.c":          "",
 			"not-for-arm.c":        "",
 			"not-for-arm64.c":      "",
+			"not-for-riscv64.c":    "",
 			"not-for-x86.c":        "",
 			"not-for-x86_64.c":     "",
 			"not-for-lib32.c":      "",
@@ -881,6 +886,7 @@
    arch: {
        arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
        arm64: { srcs: ["for-arm64.c"], exclude_srcs: ["not-for-arm64.c"] },
+       riscv64: { srcs: ["for-riscv64.c"], exclude_srcs: ["not-for-riscv64.c"] },
        x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] },
        x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] },
    },
@@ -896,6 +902,7 @@
         "//build/bazel/platforms/arch:arm": [
             "not-for-arm64.c",
             "not-for-lib64.c",
+            "not-for-riscv64.c",
             "not-for-x86.c",
             "not-for-x86_64.c",
             "for-arm.c",
@@ -904,15 +911,26 @@
         "//build/bazel/platforms/arch:arm64": [
             "not-for-arm.c",
             "not-for-lib32.c",
+            "not-for-riscv64.c",
             "not-for-x86.c",
             "not-for-x86_64.c",
             "for-arm64.c",
             "for-lib64.c",
         ],
+        "//build/bazel/platforms/arch:riscv64": [
+            "not-for-arm.c",
+            "not-for-arm64.c",
+            "not-for-lib32.c",
+            "not-for-x86.c",
+            "not-for-x86_64.c",
+            "for-riscv64.c",
+            "for-lib64.c",
+        ],
         "//build/bazel/platforms/arch:x86": [
             "not-for-arm.c",
             "not-for-arm64.c",
             "not-for-lib64.c",
+            "not-for-riscv64.c",
             "not-for-x86_64.c",
             "for-x86.c",
             "for-lib32.c",
@@ -921,6 +939,7 @@
             "not-for-arm.c",
             "not-for-arm64.c",
             "not-for-lib32.c",
+            "not-for-riscv64.c",
             "not-for-x86.c",
             "for-x86_64.c",
             "for-lib64.c",
@@ -930,6 +949,7 @@
             "not-for-arm64.c",
             "not-for-lib32.c",
             "not-for-lib64.c",
+            "not-for-riscv64.c",
             "not-for-x86.c",
             "not-for-x86_64.c",
         ],
diff --git a/bp2build/cc_prebuilt_binary_conversion_test.go b/bp2build/cc_prebuilt_binary_conversion_test.go
new file mode 100644
index 0000000..0e8048c
--- /dev/null
+++ b/bp2build/cc_prebuilt_binary_conversion_test.go
@@ -0,0 +1,125 @@
+// Copyright 2022 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 (
+	"fmt"
+	"testing"
+
+	"android/soong/cc"
+)
+
+func runCcPrebuiltBinaryTestCase(t *testing.T, testCase Bp2buildTestCase) {
+	t.Helper()
+	description := fmt.Sprintf("cc_prebuilt_binary: %s", testCase.Description)
+	testCase.ModuleTypeUnderTest = "cc_prebuilt_binary"
+	testCase.ModuleTypeUnderTestFactory = cc.PrebuiltBinaryFactory
+	testCase.Description = description
+	t.Run(description, func(t *testing.T) {
+		t.Helper()
+		RunBp2BuildTestCaseSimple(t, testCase)
+	})
+}
+
+func TestPrebuiltBinary(t *testing.T) {
+	runCcPrebuiltBinaryTestCase(t,
+		Bp2buildTestCase{
+			Description: "simple",
+			Filesystem: map[string]string{
+				"bin": "",
+			},
+			Blueprint: `
+cc_prebuilt_binary {
+	name: "bintest",
+	srcs: ["bin"],
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{
+					"src": `"bin"`,
+				})},
+		})
+}
+
+func TestPrebuiltBinaryWithStrip(t *testing.T) {
+	runCcPrebuiltBinaryTestCase(t,
+		Bp2buildTestCase{
+			Description: "with strip",
+			Filesystem: map[string]string{
+				"bin": "",
+			},
+			Blueprint: `
+cc_prebuilt_binary {
+	name: "bintest",
+	srcs: ["bin"],
+	strip: { all: true },
+	bazel_module: { bp2build_available: true },
+}`, ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{
+					"src": `"bin"`,
+					"strip": `{
+        "all": True,
+    }`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltBinaryWithArchVariance(t *testing.T) {
+	runCcPrebuiltBinaryTestCase(t,
+		Bp2buildTestCase{
+			Description: "with arch variance",
+			Filesystem: map[string]string{
+				"bina": "",
+				"binb": "",
+			},
+			Blueprint: `
+cc_prebuilt_binary {
+	name: "bintest",
+	arch: {
+		arm64: { srcs: ["bina"], },
+		arm: { srcs: ["binb"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`, ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_binary", "bintest", AttrNameToString{
+					"src": `select({
+        "//build/bazel/platforms/arch:arm": "binb",
+        "//build/bazel/platforms/arch:arm64": "bina",
+        "//conditions:default": None,
+    })`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltBinaryMultipleSrcsFails(t *testing.T) {
+	runCcPrebuiltBinaryTestCase(t,
+		Bp2buildTestCase{
+			Description: "fails because multiple sources",
+			Filesystem: map[string]string{
+				"bina": "",
+				"binb": "",
+			},
+			Blueprint: `
+cc_prebuilt_binary {
+	name: "bintest",
+	srcs: ["bina", "binb"],
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedErr: fmt.Errorf("Expected at most one source file"),
+		})
+}
+
+// TODO: nosrcs test
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index b6190c6..4d8b8a4 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -34,6 +34,12 @@
 
 	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
+	convertedModulePathMap, err := json.MarshalIndent(metrics.convertedModulePathMap, "", "\t")
+	if err != nil {
+		panic(err)
+	}
+	files = append(files, newFile("metrics", "converted_modules_path_map.json", string(convertedModulePathMap)))
+
 	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
 
 	files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
@@ -91,7 +97,7 @@
 		targets.sort()
 
 		var content string
-		if mode == Bp2Build {
+		if mode == Bp2Build || mode == ApiBp2build {
 			content = `# READ THIS FIRST:
 # This file was automatically generated by bp2build for the Bazel migration project.
 # Feel free to edit or test it, but do *not* check it into your version control system.
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 0cb711c..b696a98 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -116,6 +116,10 @@
 			basename: "converted_modules.txt",
 		},
 		{
+			dir:      "metrics",
+			basename: "converted_modules_path_map.json",
+		},
+		{
 			dir:      "product_config",
 			basename: "soong_config_variables.bzl",
 		},
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 3a21c34..0b45996 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -9,6 +9,7 @@
 	"android/soong/android"
 	"android/soong/shared"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
+
 	"github.com/google/blueprint"
 )
 
@@ -38,6 +39,9 @@
 	// List of converted modules
 	convertedModules []string
 
+	// Map of converted modules and paths to call
+	convertedModulePathMap map[string]string
+
 	// Counts of converted modules by module type.
 	convertedModuleTypeCount map[string]uint64
 
@@ -147,10 +151,11 @@
 	Handcrafted
 )
 
-func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, conversionType ConversionType) {
+func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
 	// Undo prebuilt_ module name prefix modifications
 	moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
 	metrics.convertedModules = append(metrics.convertedModules, moduleName)
+	metrics.convertedModulePathMap[moduleName] = "//" + dir
 	metrics.convertedModuleTypeCount[moduleType] += 1
 	metrics.totalModuleTypeCount[moduleType] += 1
 
diff --git a/bp2build/ndk_headers_conversion_test.go b/bp2build/ndk_headers_conversion_test.go
index c7cc6b2..9d0f1f2 100644
--- a/bp2build/ndk_headers_conversion_test.go
+++ b/bp2build/ndk_headers_conversion_test.go
@@ -79,7 +79,7 @@
 				"hdrs": testCase.expectedHdrs,
 			},
 		)
-		RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 			Description:          testCase.desc,
 			Blueprint:            fmt.Sprintf(bpTemplate, testCase.srcs, testCase.excludeSrcs),
 			ExpectedBazelTargets: []string{expectedBazelTarget},
@@ -121,7 +121,7 @@
 				"include_dir": testCase.expectedIncludeDir,
 			},
 		)
-		RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 			Description:          testCase.desc,
 			Blueprint:            fmt.Sprintf(bpTemplate, testCase.from),
 			ExpectedBazelTargets: []string{expectedBazelTarget},
@@ -156,7 +156,7 @@
     ]`,
 		},
 	)
-	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		Filesystem:           fs,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
diff --git a/bp2build/ndk_library_conversion_test.go b/bp2build/ndk_library_conversion_test.go
index 244ce20..819ab25 100644
--- a/bp2build/ndk_library_conversion_test.go
+++ b/bp2build/ndk_library_conversion_test.go
@@ -37,7 +37,7 @@
 			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 		},
 	)
-	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
 	})
@@ -69,7 +69,7 @@
 			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 		},
 	)
-	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		Filesystem:           fs,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index e8b9bc4..1b538d0 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -4,6 +4,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/genrule"
 	"android/soong/python"
 )
 
@@ -12,6 +13,8 @@
 	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
 		ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
 		ctx.RegisterModuleType("python_library_host", python.PythonLibraryHostFactory)
+		ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+		ctx.RegisterModuleType("python_defaults", python.DefaultsFactory)
 	}, tc)
 }
 
@@ -165,3 +168,152 @@
 		},
 	})
 }
+
+func TestPythonBinaryMainIsNotSpecified(t *testing.T) {
+	runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+		Description:                "python_binary_host main label in same package",
+		ModuleTypeUnderTest:        "python_binary_host",
+		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+		Blueprint: `python_binary_host {
+    name: "foo",
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
+				"imports": `["."]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPythonBinaryMainIsLabel(t *testing.T) {
+	runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+		Description:                "python_binary_host main label in same package",
+		ModuleTypeUnderTest:        "python_binary_host",
+		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+		Blueprint: `python_binary_host {
+    name: "foo",
+    main: ":a",
+    bazel_module: { bp2build_available: true },
+}
+
+genrule {
+		name: "a",
+		bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
+				"main":    `":a"`,
+				"imports": `["."]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPythonBinaryMainIsSubpackageFile(t *testing.T) {
+	runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+		Description:                "python_binary_host main is subpackage file",
+		ModuleTypeUnderTest:        "python_binary_host",
+		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+		Filesystem: map[string]string{
+			"a/Android.bp": "",
+			"a/b.py":       "",
+		},
+		Blueprint: `python_binary_host {
+    name: "foo",
+    main: "a/b.py",
+    bazel_module: { bp2build_available: true },
+}
+
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
+				"main":    `"//a:b.py"`,
+				"imports": `["."]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPythonBinaryMainIsSubDirFile(t *testing.T) {
+	runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+		Description:                "python_binary_host main is file in sub directory that is not Bazel package",
+		ModuleTypeUnderTest:        "python_binary_host",
+		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+		Filesystem: map[string]string{
+			"a/b.py": "",
+		},
+		Blueprint: `python_binary_host {
+    name: "foo",
+    main: "a/b.py",
+    bazel_module: { bp2build_available: true },
+}
+
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
+				"main":    `"a/b.py"`,
+				"imports": `["."]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPythonBinaryDuplicatesInRequired(t *testing.T) {
+	runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+		Description:                "python_binary_host duplicates in required attribute of the module and its defaults",
+		ModuleTypeUnderTest:        "python_binary_host",
+		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+		Blueprint: `python_binary_host {
+    name: "foo",
+    main: "a.py",
+		defaults: ["d"],
+    required: [
+        "r1",
+    ],
+    bazel_module: { bp2build_available: true },
+}
+
+python_defaults {
+    name: "d",
+    required: [
+        "r1",
+        "r2",
+    ],
+}` + simpleModuleDoNotConvertBp2build("genrule", "r1") +
+			simpleModuleDoNotConvertBp2build("genrule", "r2"),
+
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
+				"main":    `"a.py"`,
+				"imports": `["."]`,
+				"data": `[
+        ":r1",
+        ":r2",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 023ec96..7c39a11 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -285,6 +285,17 @@
 			// Neither is a directory. Merge them.
 			srcBuildFile := shared.JoinPath(topdir, srcChild)
 			generatedBuildFile := shared.JoinPath(topdir, buildFilesChild)
+			// Add the src and generated build files as dependencies so that bp2build
+			// is rerun when they change. Currently, this is only really necessary
+			// for srcBuildFile, because if we regenerate the generated build files
+			// we will always rerun the symlink forest generation as well. If that
+			// is later split up into separate, fully dependency-tracing steps, then
+			// we'll need srcBuildFile as well. Adding srcBuildFile here today
+			// technically makes it a dependency of bp2build_workspace_marker, which
+			// also implicitly outputs that file, but since bp2build_workspace_marker
+			// will always have a newer timestamp than the generatedBuildFile it
+			// shouldn't be a problem.
+			*acc = append(*acc, srcBuildFile, generatedBuildFile)
 			err = mergeBuildFiles(shared.JoinPath(topdir, forestChild), srcBuildFile, generatedBuildFile, cfg.IsEnvTrue("BP2BUILD_VERBOSE"))
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 8ce8bb2..31aa830 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -24,19 +24,14 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/android/allowlists"
 	"android/soong/bazel"
 )
 
 var (
-	// A default configuration for tests to not have to specify bp2build_available on top level targets.
-	bp2buildConfig = android.NewBp2BuildAllowlist().SetDefaultConfig(
-		allowlists.Bp2BuildConfig{
-			android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
-		},
-	)
-
 	buildDir string
 )
 
@@ -87,9 +82,30 @@
 	// An error with a string contained within the string of the expected error
 	ExpectedErr         error
 	UnconvertedDepsMode unconvertedDepsMode
+
+	// For every directory listed here, the BUILD file for that directory will
+	// be merged with the generated BUILD file. This allows custom BUILD targets
+	// to be used in tests, or use BUILD files to draw package boundaries.
+	KeepBuildFileForDirs []string
 }
 
 func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+	bp2buildSetup := func(ctx *android.TestContext) {
+		registerModuleTypes(ctx)
+		ctx.RegisterForBazelConversion()
+	}
+	runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc)
+}
+
+func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+	apiBp2BuildSetup := func(ctx *android.TestContext) {
+		registerModuleTypes(ctx)
+		ctx.RegisterForApiBazelConversion()
+	}
+	runBp2BuildTestCaseWithSetup(t, apiBp2BuildSetup, tc)
+}
+
+func runBp2BuildTestCaseWithSetup(t *testing.T, setup func(ctx *android.TestContext), tc Bp2buildTestCase) {
 	t.Helper()
 	dir := "."
 	filesystem := make(map[string][]byte)
@@ -105,10 +121,21 @@
 	config := android.TestConfig(buildDir, nil, tc.Blueprint, filesystem)
 	ctx := android.NewTestContext(config)
 
-	registerModuleTypes(ctx)
+	setup(ctx)
 	ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
+
+	// A default configuration for tests to not have to specify bp2build_available on top level targets.
+	bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig(
+		allowlists.Bp2BuildConfig{
+			android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
+		},
+	)
+	for _, f := range tc.KeepBuildFileForDirs {
+		bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{
+			f: /*recursive=*/ false,
+		})
+	}
 	ctx.RegisterBp2BuildConfig(bp2buildConfig)
-	ctx.RegisterForBazelConversion()
 
 	_, parseErrs := ctx.ParseFileList(dir, toParse)
 	if errored(t, tc, parseErrs) {
@@ -188,6 +215,8 @@
 
 	// Prop used to indicate this conversion should be 1 module -> multiple targets
 	One_to_many_prop *bool
+
+	Api *string // File describing the APIs of this module
 }
 
 type customModule struct {
@@ -310,6 +339,7 @@
 	String_ptr_prop     *string
 	String_list_prop    []string
 	Arch_paths          bazel.LabelListAttribute
+	Api                 bazel.LabelAttribute
 }
 
 func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@@ -354,6 +384,23 @@
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 }
 
+var _ android.ApiProvider = (*customModule)(nil)
+
+func (c *customModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class: "custom_api_contribution",
+	}
+	apiAttribute := bazel.MakeLabelAttribute(
+		android.BazelLabelForModuleSrcSingle(ctx, proptools.String(c.props.Api)).Label,
+	)
+	attrs := &customBazelModuleAttributes{
+		Api: *apiAttribute,
+	}
+	ctx.CreateBazelTargetModule(props,
+		android.CommonAttributes{Name: c.Name()},
+		attrs)
+}
+
 // A bp2build mutator that uses load statements and creates a 1:M mapping from
 // module to target.
 func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
diff --git a/bpf/bpf.go b/bpf/bpf.go
index e89cc4e..60a410d 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -21,6 +21,8 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/bazel"
+	"android/soong/bazel/cquery"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -93,6 +95,7 @@
 
 type bpf struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties BpfProperties
 
@@ -237,6 +240,35 @@
 	}
 }
 
+var _ android.MixedBuildBuildable = (*bpf)(nil)
+
+func (bpf *bpf) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+	return true
+}
+
+func (bpf *bpf) QueueBazelCall(ctx android.BaseModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	bazelCtx.QueueBazelRequest(
+		bpf.GetBazelLabel(ctx, bpf),
+		cquery.GetOutputFiles,
+		android.GetConfigKey(ctx))
+}
+
+func (bpf *bpf) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	objPaths, err := bazelCtx.GetOutputFiles(bpf.GetBazelLabel(ctx, bpf), android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+		return
+	}
+
+	bazelOuts := android.Paths{}
+	for _, p := range objPaths {
+		bazelOuts = append(bazelOuts, android.PathForBazelOut(ctx, p))
+	}
+	bpf.objs = bazelOuts
+}
+
 // Implements OutputFileFileProducer interface so that the obj output can be used in the data property
 // of other modules.
 func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) {
@@ -260,5 +292,39 @@
 	module.AddProperties(&module.properties)
 
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitBazelModule(module)
 	return module
 }
+
+type bazelBpfAttributes struct {
+	Srcs              bazel.LabelListAttribute
+	Copts             bazel.StringListAttribute
+	Absolute_includes bazel.StringListAttribute
+	Btf               *bool
+	// TODO(b/249528391): Add support for sub_dir
+}
+
+// bpf bp2build converter
+func (b *bpf) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if ctx.ModuleType() != "bpf" {
+		return
+	}
+
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, b.properties.Srcs))
+	copts := bazel.MakeStringListAttribute(b.properties.Cflags)
+	absolute_includes := bazel.MakeStringListAttribute(b.properties.Include_dirs)
+	btf := b.properties.Btf
+
+	attrs := bazelBpfAttributes{
+		Srcs:              srcs,
+		Copts:             copts,
+		Absolute_includes: absolute_includes,
+		Btf:               btf,
+	}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "bpf",
+		Bzl_load_location: "//build/bazel/rules/bpf:bpf.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: b.Name()}, &attrs)
+}
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index 6e39096..a2010ff 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -71,3 +71,26 @@
 		`\QAndroid.bp:2:3: module "bpf_invalid_name.o" variant "android_common": invalid character '_' in source name\E`)).
 		RunTestWithBp(t, bp)
 }
+
+func TestBpfWithBazel(t *testing.T) {
+	bp := `
+		bpf {
+			name: "bpf.o",
+			srcs: ["bpf.c"],
+			bazel_module: { label: "//bpf" },
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForBpfTest, android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: "outputbase",
+				LabelToOutputFiles: map[string][]string{
+					"//bpf": []string{"bpf.o"}}}
+		})).RunTestWithBp(t, bp)
+
+	output := result.Module("bpf.o", "android_common").(*bpf)
+
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/bpf.o"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, output.objs.Strings())
+}
diff --git a/build_test.bash b/build_test.bash
index 8b91e2c..92819a1 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -29,6 +29,9 @@
     linux_bionic
     mainline_sdk
     ndk
+
+    # New architecture bringup, fails without ALLOW_MISSING_DEPENDENCIES=true
+    aosp_riscv64
 )
 
 # To track how long we took to startup. %N isn't supported on Darwin, but
@@ -59,7 +62,7 @@
 
 echo
 echo "Running Bazel smoke test..."
-STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 info
+STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 help
 
 echo
 echo "Running Soong test..."
diff --git a/cc/androidmk.go b/cc/androidmk.go
index a9ba1a9..58bb57c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -546,7 +546,7 @@
 
 	entries.SubName += c.baseProperties.Androidmk_suffix
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		c.libraryDecorator.androidMkWriteExportedFlags(entries)
 
 		if c.shared() || c.static() {
@@ -567,6 +567,10 @@
 			if c.tocFile.Valid() {
 				entries.SetString("LOCAL_SOONG_TOC", c.tocFile.String())
 			}
+
+			if c.shared() && len(c.Properties.Overrides) > 0 {
+				entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, c.Properties.Overrides), " "))
+			}
 		}
 
 		if !c.shared() { // static or header
diff --git a/cc/api_level.go b/cc/api_level.go
index fd145a9..fdff5cb 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,7 @@
 	"android/soong/android"
 )
 
-func minApiForArch(ctx android.BaseModuleContext,
+func minApiForArch(ctx android.EarlyModuleContext,
 	arch android.ArchType) android.ApiLevel {
 
 	switch arch {
@@ -28,6 +28,8 @@
 		return ctx.Config().MinSupportedSdkVersion()
 	case android.Arm64, android.X86_64:
 		return android.FirstLp64Version
+	case android.Riscv64:
+		return android.FutureApiLevel
 	default:
 		panic(fmt.Errorf("Unknown arch %q", arch))
 	}
diff --git a/cc/binary.go b/cc/binary.go
index 3351fd7..a6d7507 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -540,6 +540,12 @@
 	return binary.toolPath
 }
 
+func (binary *binaryDecorator) overriddenModules() []string {
+	return binary.Properties.Overrides
+}
+
+var _ overridable = (*binaryDecorator)(nil)
+
 func init() {
 	pctx.HostBinToolVariable("verifyHostBionicCmd", "host_bionic_verify")
 }
@@ -571,25 +577,20 @@
 
 func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx))
 }
 
 func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKey(ctx))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
 	}
 
-	if len(filePaths) != 1 {
-		ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
-		return
-	}
-	outputFilePath := android.PathForBazelOut(ctx, filePaths[0])
+	outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
-	// TODO(b/220164721): We need to decide if we should return the stripped as the unstripped.
-	handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
+	handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
 }
 
 func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
diff --git a/cc/binary_test.go b/cc/binary_test.go
index cba5974..db6fb3a 100644
--- a/cc/binary_test.go
+++ b/cc/binary_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"android/soong/bazel/cquery"
 	"testing"
 
 	"android/soong/android"
@@ -30,8 +31,11 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.BazelContext = android.MockBazelContext{
 		OutputBaseDir: "outputbase",
-		LabelToOutputFiles: map[string][]string{
-			"//foo/bar:bar": []string{"foo"},
+		LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+			"//foo/bar:bar": cquery.CcUnstrippedInfo{
+				OutputFile:       "foo",
+				UnstrippedOutput: "foo.unstripped",
+			},
 		},
 	}
 	ctx := testCcWithConfig(t, config)
@@ -46,7 +50,7 @@
 	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 
 	unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
-	expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+	expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped"
 	android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
 }
 
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 9b85ec4..d6d052f 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -169,12 +169,20 @@
 	}
 }
 
+func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
+		for config, props := range configToProps {
+			parseFunc(axis, config, props)
+		}
+	}
+}
+
 // Parses properties common to static and shared libraries. Also used for prebuilt libraries.
 func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
-		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, true, filterOutStdFlag))
+		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
 		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 
@@ -227,32 +235,30 @@
 	Enabled bazel.BoolAttribute
 }
 
+func parseSrc(ctx android.BazelConversionPathContext, srcLabelAttribute *bazel.LabelAttribute, axis bazel.ConfigurationAxis, config string, srcs []string) {
+	srcFileError := func() {
+		ctx.ModuleErrorf("parseSrc: Expected at most one source file for %s %s\n", axis, config)
+	}
+	if len(srcs) > 1 {
+		srcFileError()
+		return
+	} else if len(srcs) == 0 {
+		return
+	}
+	if srcLabelAttribute.SelectValue(axis, config) != nil {
+		srcFileError()
+		return
+	}
+	srcLabelAttribute.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, srcs[0]))
+}
+
 // NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
 func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) prebuiltAttributes {
-	manySourceFileError := func(axis bazel.ConfigurationAxis, config string) {
-		ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most one source file for %s %s\n", axis, config)
-	}
+
 	var srcLabelAttribute bazel.LabelAttribute
-
-	parseSrcs := func(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, srcs []string) {
-		if len(srcs) > 1 {
-			manySourceFileError(axis, config)
-			return
-		} else if len(srcs) == 0 {
-			return
-		}
-		if srcLabelAttribute.SelectValue(axis, config) != nil {
-			manySourceFileError(axis, config)
-			return
-		}
-
-		src := android.BazelLabelForModuleSrcSingle(ctx, srcs[0])
-		srcLabelAttribute.SetSelectValue(axis, config, src)
-	}
-
 	bp2BuildPropParseHelper(ctx, module, &prebuiltLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
 		if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
-			parseSrcs(ctx, axis, config, prebuiltLinkerProperties.Srcs)
+			parseSrc(ctx, &srcLabelAttribute, axis, config, prebuiltLinkerProperties.Srcs)
 		}
 	})
 
@@ -261,7 +267,7 @@
 		if props.Enabled != nil {
 			enabledLabelAttribute.SetSelectValue(axis, config, props.Enabled)
 		}
-		parseSrcs(ctx, axis, config, props.Srcs)
+		parseSrc(ctx, &srcLabelAttribute, axis, config, props.Srcs)
 	}
 
 	if isStatic {
@@ -284,11 +290,16 @@
 	}
 }
 
-func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
-	for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
-		for config, props := range configToProps {
-			parseFunc(axis, config, props)
+func bp2BuildParsePrebuiltBinaryProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
+	var srcLabelAttribute bazel.LabelAttribute
+	bp2BuildPropParseHelper(ctx, module, &prebuiltLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+		if props, ok := props.(*prebuiltLinkerProperties); ok {
+			parseSrc(ctx, &srcLabelAttribute, axis, config, props.Srcs)
 		}
+	})
+
+	return prebuiltAttributes{
+		Src: srcLabelAttribute,
 	}
 }
 
@@ -365,7 +376,7 @@
 	return false
 }
 
-func parseCommandLineFlags(soongFlags []string, noCoptsTokenization bool, filterOut ...filterOutFn) []string {
+func parseCommandLineFlags(soongFlags []string, filterOut ...filterOutFn) []string {
 	var result []string
 	for _, flag := range soongFlags {
 		skipFlag := false
@@ -380,15 +391,7 @@
 		// Soong's cflags can contain spaces, like `-include header.h`. For
 		// Bazel's copts, split them up to be compatible with the
 		// no_copts_tokenization feature.
-		if noCoptsTokenization {
-			result = append(result, strings.Split(flag, " ")...)
-		} else {
-			// Soong's Version Script and Dynamic List Properties are added as flags
-			// to Bazel's linkopts using "($location label)" syntax.
-			// Splitting on spaces would separate this into two different flags
-			// "($ location" and "label)"
-			result = append(result, flag)
-		}
+		result = append(result, strings.Split(flag, " ")...)
 	}
 	return result
 }
@@ -422,10 +425,10 @@
 	// overridden. In Bazel we always allow overriding, via flags; however, this can cause
 	// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
 	// cases.
-	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, true, filterOutStdFlag, filterOutClangUnknownCflags))
-	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, true, nil))
-	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, true, filterOutClangUnknownCflags))
-	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, true, filterOutClangUnknownCflags))
+	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags))
+	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
+	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, filterOutClangUnknownCflags))
+	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, filterOutClangUnknownCflags))
 	ca.rtti.SetSelectValue(axis, config, props.Rtti)
 }
 
@@ -1031,19 +1034,26 @@
 			axisFeatures = append(axisFeatures, "-static_flag")
 		}
 	}
+
+	// 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)
+
+	additionalLinkerInputs := bazel.LabelList{}
 	if props.Version_script != nil {
 		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
-		la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
+		additionalLinkerInputs.Add(&label)
 		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
 	}
 
 	if props.Dynamic_list != nil {
 		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Dynamic_list)
-		la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
+		additionalLinkerInputs.Add(&label)
 		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--dynamic-list,$(location %s)", label.Label))
 	}
 
-	la.linkopts.SetSelectValue(axis, config, parseCommandLineFlags(linkerFlags, false, filterOutClangUnknownCflags))
+	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.
diff --git a/cc/builder.go b/cc/builder.go
index cb21b1f..39f7dc3 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -672,11 +672,16 @@
 			tidyCmd := "${config.ClangBin}/clang-tidy"
 
 			rule := clangTidy
+			reducedCFlags := moduleFlags
 			if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
 				rule = clangTidyRE
+				// b/248371171, work around RBE input processor problem
+				// some cflags rejected by input processor, but usually
+				// do not affect included files or clang-tidy
+				reducedCFlags = config.TidyReduceCFlags(reducedCFlags)
 			}
 
-			sharedCFlags := shareFlags("cFlags", moduleFlags)
+			sharedCFlags := shareFlags("cFlags", reducedCFlags)
 			srcRelPath := srcFile.Rel()
 
 			// Add the .tidy rule
@@ -1135,7 +1140,3 @@
 		},
 	})
 }
-
-func mingwCmd(toolchain config.Toolchain, cmd string) string {
-	return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
-}
diff --git a/cc/cc.go b/cc/cc.go
index 1c845f6..eb7c639 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -616,6 +616,10 @@
 	XrefCcFiles() android.Paths
 }
 
+type overridable interface {
+	overriddenModules() []string
+}
+
 type libraryDependencyKind int
 
 const (
@@ -3330,6 +3334,11 @@
 			return android.Paths{c.outputFile.Path()}, nil
 		}
 		return android.Paths{}, nil
+	case "unstripped":
+		if c.linker != nil {
+			return android.PathsIfNonNil(c.linker.unstrippedOutputFilePath()), nil
+		}
+		return nil, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
@@ -3617,6 +3626,16 @@
 		return err
 	}
 
+	// A dependency only needs to support a min_sdk_version at least
+	// as high as  the api level that the architecture was introduced in.
+	// 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)
+	if sdkVersion.LessThan(minApiForArch) {
+		sdkVersion = minApiForArch
+	}
+
 	if ver.GreaterThan(sdkVersion) {
 		return fmt.Errorf("newer SDK(%v)", ver)
 	}
@@ -3637,6 +3656,13 @@
 	return c.UseVndk() && c.IsVndk()
 }
 
+func (c *Module) overriddenModules() []string {
+	if o, ok := c.linker.(overridable); ok {
+		return o.overriddenModules()
+	}
+	return nil
+}
+
 var _ snapshot.RelativeInstallPath = (*Module)(nil)
 
 type moduleType int
@@ -3698,7 +3724,9 @@
 	prebuilt := c.IsPrebuilt()
 	switch c.typ() {
 	case binary:
-		if !prebuilt {
+		if prebuilt {
+			prebuiltBinaryBp2Build(ctx, c)
+		} else {
 			binaryBp2build(ctx, c)
 		}
 	case testBin:
@@ -3729,6 +3757,24 @@
 		} else {
 			sharedOrStaticLibraryBp2Build(ctx, c, false)
 		}
+	}
+}
+
+var _ android.ApiProvider = (*Module)(nil)
+
+func (c *Module) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	if c.IsPrebuilt() {
+		return
+	}
+	switch c.typ() {
+	case fullLibrary:
+		apiContributionBp2Build(ctx, c)
+	case sharedLibrary:
+		apiContributionBp2Build(ctx, c)
+	case headerLibrary:
+		// Aggressively generate api targets for all header modules
+		// This is necessary since the header module does not know if it is a dep of API surface stub library
+		apiLibraryHeadersBp2Build(ctx, c)
 	case ndkLibrary:
 		ndkLibraryBp2build(ctx, c)
 	}
@@ -3821,6 +3867,15 @@
 	}
 }
 
+func (c *Module) Partition() string {
+	if p, ok := c.installer.(interface {
+		getPartition() string
+	}); ok {
+		return p.getPartition()
+	}
+	return ""
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var BoolPtr = proptools.BoolPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 01ac133..6a22bd0 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3260,6 +3260,102 @@
 	}
 }
 
+func pathsToBase(paths android.Paths) []string {
+	var ret []string
+	for _, p := range paths {
+		ret = append(ret, p.Base())
+	}
+	return ret
+}
+
+func TestStaticLibArchiveArgs(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library_static {
+			name: "foo",
+			srcs: ["foo.c"],
+		}
+
+		cc_library_static {
+			name: "bar",
+			srcs: ["bar.c"],
+		}
+
+		cc_library_shared {
+			name: "qux",
+			srcs: ["qux.c"],
+		}
+
+		cc_library_static {
+			name: "baz",
+			srcs: ["baz.c"],
+			static_libs: ["foo"],
+			shared_libs: ["qux"],
+			whole_static_libs: ["bar"],
+		}`)
+
+	variant := "android_arm64_armv8-a_static"
+	arRule := ctx.ModuleForTests("baz", variant).Rule("ar")
+
+	// For static libraries, the object files of a whole static dep are included in the archive
+	// directly
+	if g, w := pathsToBase(arRule.Inputs), []string{"bar.o", "baz.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("Expected input objects %q, got %q", w, g)
+	}
+
+	// non whole static dependencies are not linked into the archive
+	if len(arRule.Implicits) > 0 {
+		t.Errorf("Expected 0 additional deps, got %q", arRule.Implicits)
+	}
+}
+
+func TestSharedLibLinkingArgs(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library_static {
+			name: "foo",
+			srcs: ["foo.c"],
+		}
+
+		cc_library_static {
+			name: "bar",
+			srcs: ["bar.c"],
+		}
+
+		cc_library_shared {
+			name: "qux",
+			srcs: ["qux.c"],
+		}
+
+		cc_library_shared {
+			name: "baz",
+			srcs: ["baz.c"],
+			static_libs: ["foo"],
+			shared_libs: ["qux"],
+			whole_static_libs: ["bar"],
+		}`)
+
+	variant := "android_arm64_armv8-a_shared"
+	linkRule := ctx.ModuleForTests("baz", variant).Rule("ld")
+	libFlags := linkRule.Args["libFlags"]
+	// When dynamically linking, we expect static dependencies to be found on the command line
+	if expected := "foo.a"; !strings.Contains(libFlags, expected) {
+		t.Errorf("Static lib %q was not found in %q", expected, libFlags)
+	}
+	// When dynamically linking, we expect whole static dependencies to be found on the command line
+	if expected := "bar.a"; !strings.Contains(libFlags, expected) {
+		t.Errorf("Static lib %q was not found in %q", expected, libFlags)
+	}
+
+	// When dynamically linking, we expect shared dependencies to be found on the command line
+	if expected := "qux.so"; !strings.Contains(libFlags, expected) {
+		t.Errorf("Shared lib %q was not found in %q", expected, libFlags)
+	}
+
+	// We should only have the objects from the shared library srcs, not the whole static dependencies
+	if g, w := pathsToBase(linkRule.Inputs), []string{"baz.o"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("Expected input objects %q, got %q", w, g)
+	}
+}
+
 func TestStaticExecutable(t *testing.T) {
 	ctx := testCc(t, `
 		cc_binary {
@@ -3543,14 +3639,6 @@
 			defaults: ["defaults"],
 		}`)
 
-	pathsToBase := func(paths android.Paths) []string {
-		var ret []string
-		for _, p := range paths {
-			ret = append(ret, p.Base())
-		}
-		return ret
-	}
-
 	shared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared").Rule("ld")
 	if g, w := pathsToBase(shared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("libshared ld rule wanted %q, got %q", w, g)
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 64a121e..fdc94ad 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -21,6 +21,7 @@
 
         "arm_device.go",
         "arm64_device.go",
+        "riscv64_device.go",
         "x86_device.go",
         "x86_64_device.go",
 
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 66087e6..d7f9618 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -74,7 +74,7 @@
 			"-mcpu=kryo",
 		},
 		"kryo385": []string{
-			// Use cortex-a53 because kryo385 is not supported in GCC/clang.
+			// Use cortex-a53 because kryo385 is not supported in clang.
 			"-mcpu=cortex-a53",
 		},
 		"exynos-m1": []string{
@@ -86,16 +86,7 @@
 	}
 )
 
-const (
-	arm64GccVersion = "4.9"
-)
-
 func init() {
-	pctx.StaticVariable("arm64GccVersion", arm64GccVersion)
-
-	pctx.SourcePathVariable("Arm64GccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
-
 	exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
 	exportedVars.ExportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
 
@@ -164,24 +155,12 @@
 	return "arm64"
 }
 
-func (t *toolchainArm64) GccRoot() string {
-	return "${config.Arm64GccRoot}"
-}
-
-func (t *toolchainArm64) GccTriple() string {
-	return "aarch64-linux-android"
-}
-
-func (t *toolchainArm64) GccVersion() string {
-	return arm64GccVersion
-}
-
 func (t *toolchainArm64) IncludeFlags() string {
 	return ""
 }
 
 func (t *toolchainArm64) ClangTriple() string {
-	return t.GccTriple()
+	return "aarch64-linux-android"
 }
 
 func (t *toolchainArm64) Cflags() string {
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index d702c61..b53a097 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -79,7 +79,7 @@
 		"cortex-a7": []string{
 			"-mcpu=cortex-a7",
 			"-mfpu=neon-vfpv4",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -91,7 +91,7 @@
 		"cortex-a15": []string{
 			"-mcpu=cortex-a15",
 			"-mfpu=neon-vfpv4",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -100,7 +100,7 @@
 		"cortex-a53": []string{
 			"-mcpu=cortex-a53",
 			"-mfpu=neon-fp-armv8",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -109,7 +109,7 @@
 		"cortex-a55": []string{
 			"-mcpu=cortex-a55",
 			"-mfpu=neon-fp-armv8",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -118,7 +118,7 @@
 		"cortex-a75": []string{
 			"-mcpu=cortex-a55",
 			"-mfpu=neon-fp-armv8",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -127,7 +127,7 @@
 		"cortex-a76": []string{
 			"-mcpu=cortex-a55",
 			"-mfpu=neon-fp-armv8",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -136,7 +136,7 @@
 		"krait": []string{
 			"-mcpu=krait",
 			"-mfpu=neon-vfpv4",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -147,16 +147,16 @@
 			// even though clang does.
 			"-mcpu=cortex-a53",
 			"-mfpu=neon-fp-armv8",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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",
 		},
 		"kryo385": []string{
-			// Use cortex-a53 because kryo385 is not supported in GCC/clang.
+			// Use cortex-a53 because kryo385 is not supported in clang.
 			"-mcpu=cortex-a53",
-			// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+			// 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
@@ -166,17 +166,12 @@
 )
 
 const (
-	name          = "arm"
-	armGccVersion = "4.9"
-	gccTriple     = "arm-linux-androideabi"
-	clangTriple   = "armv7a-linux-androideabi"
+	name        = "arm"
+	ndkTriple   = "arm-linux-androideabi"
+	clangTriple = "armv7a-linux-androideabi"
 )
 
 func init() {
-	pctx.StaticVariable("armGccVersion", armGccVersion)
-
-	pctx.SourcePathVariable("ArmGccRoot", "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
-
 	// Just exported. Not created as a Ninja static variable.
 	exportedVars.ExportString("ArmClangTriple", clangTriple)
 
@@ -255,18 +250,6 @@
 	return name
 }
 
-func (t *toolchainArm) GccRoot() string {
-	return "${config.ArmGccRoot}"
-}
-
-func (t *toolchainArm) GccTriple() string {
-	return gccTriple
-}
-
-func (t *toolchainArm) GccVersion() string {
-	return armGccVersion
-}
-
 func (t *toolchainArm) IncludeFlags() string {
 	return ""
 }
@@ -278,7 +261,7 @@
 
 func (t *toolchainArm) ndkTriple() string {
 	// Use current NDK include path, while ClangTriple is changed.
-	return t.GccTriple()
+	return ndkTriple
 }
 
 func (t *toolchainArm) ToolchainCflags() string {
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 01b1e63..2cabdc8 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -75,10 +75,6 @@
 	)
 )
 
-const (
-	darwinGccVersion = "4.2.1"
-)
-
 func init() {
 	pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
 		return getMacTools(ctx).sdkRoot
@@ -100,12 +96,6 @@
 		return getMacTools(ctx).toolPath
 	})
 
-	pctx.StaticVariable("DarwinGccVersion", darwinGccVersion)
-	pctx.SourcePathVariable("DarwinGccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/host/i686-apple-darwin-${DarwinGccVersion}")
-
-	pctx.StaticVariable("DarwinGccTriple", "i686-apple-darwin11")
-
 	pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
 	pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
 	pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " "))
@@ -196,30 +186,6 @@
 	return "x86_64"
 }
 
-func (t *toolchainDarwinArm) GccRoot() string {
-	panic("unimplemented")
-}
-
-func (t *toolchainDarwinArm) GccTriple() string {
-	panic("unimplemented")
-}
-
-func (t *toolchainDarwinArm) GccVersion() string {
-	panic("unimplemented")
-}
-
-func (t *toolchainDarwinX86) GccRoot() string {
-	return "${config.DarwinGccRoot}"
-}
-
-func (t *toolchainDarwinX86) GccTriple() string {
-	return "${config.DarwinGccTriple}"
-}
-
-func (t *toolchainDarwinX86) GccVersion() string {
-	return darwinGccVersion
-}
-
 func (t *toolchainDarwin) IncludeFlags() string {
 	return ""
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index bf80907..4e4d174 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -312,8 +312,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r468909"
-	ClangDefaultShortVersion = "15.0.2"
+	ClangDefaultVersion      = "clang-r468909b"
+	ClangDefaultShortVersion = "15.0.3"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
new file mode 100644
index 0000000..d8918f1
--- /dev/null
+++ b/cc/config/riscv64_device.go
@@ -0,0 +1,140 @@
+// Copyright 2022 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 config
+
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	riscv64Cflags = []string{
+		// Help catch common 32/64-bit errors.
+		"-Werror=implicit-function-declaration",
+	}
+
+	riscv64ArchVariantCflags = map[string][]string{}
+
+	riscv64Ldflags = []string{
+		"-Wl,--hash-style=gnu",
+		"-Wl,-z,separate-code",
+	}
+
+	riscv64Lldflags = append(riscv64Ldflags,
+		"-Wl,-z,max-page-size=4096")
+
+	riscv64Cppflags = []string{}
+
+	riscv64CpuVariantCflags = map[string][]string{}
+)
+
+const ()
+
+func init() {
+
+	exportedVars.ExportStringListStaticVariable("Riscv64Ldflags", riscv64Ldflags)
+	exportedVars.ExportStringListStaticVariable("Riscv64Lldflags", riscv64Lldflags)
+
+	exportedVars.ExportStringListStaticVariable("Riscv64Cflags", riscv64Cflags)
+	exportedVars.ExportStringListStaticVariable("Riscv64Cppflags", riscv64Cppflags)
+
+	exportedVars.ExportVariableReferenceDict("Riscv64ArchVariantCflags", riscv64ArchVariantCflagsVar)
+	exportedVars.ExportVariableReferenceDict("Riscv64CpuVariantCflags", riscv64CpuVariantCflagsVar)
+	exportedVars.ExportVariableReferenceDict("Riscv64CpuVariantLdflags", riscv64CpuVariantLdflags)
+}
+
+var (
+	riscv64ArchVariantCflagsVar = map[string]string{}
+
+	riscv64CpuVariantCflagsVar = map[string]string{}
+
+	riscv64CpuVariantLdflags = map[string]string{}
+)
+
+type toolchainRiscv64 struct {
+	toolchainBionic
+	toolchain64Bit
+
+	ldflags         string
+	lldflags        string
+	toolchainCflags string
+}
+
+func (t *toolchainRiscv64) Name() string {
+	return "riscv64"
+}
+
+func (t *toolchainRiscv64) IncludeFlags() string {
+	return ""
+}
+
+func (t *toolchainRiscv64) ClangTriple() string {
+	return "riscv64-linux-android"
+}
+
+func (t *toolchainRiscv64) Cflags() string {
+	return "${config.Riscv64Cflags}"
+}
+
+func (t *toolchainRiscv64) Cppflags() string {
+	return "${config.Riscv64Cppflags}"
+}
+
+func (t *toolchainRiscv64) Ldflags() string {
+	return t.ldflags
+}
+
+func (t *toolchainRiscv64) Lldflags() string {
+	return t.lldflags
+}
+
+func (t *toolchainRiscv64) ToolchainCflags() string {
+	return t.toolchainCflags
+}
+
+func (toolchainRiscv64) LibclangRuntimeLibraryArch() string {
+	return "riscv64"
+}
+
+func riscv64ToolchainFactory(arch android.Arch) Toolchain {
+	switch arch.ArchVariant {
+	case "":
+	default:
+		panic(fmt.Sprintf("Unknown Riscv64 architecture version: %q", arch.ArchVariant))
+	}
+
+	toolchainCflags := []string{riscv64ArchVariantCflagsVar[arch.ArchVariant]}
+	toolchainCflags = append(toolchainCflags,
+		variantOrDefault(riscv64CpuVariantCflagsVar, arch.CpuVariant))
+
+	extraLdflags := variantOrDefault(riscv64CpuVariantLdflags, arch.CpuVariant)
+	return &toolchainRiscv64{
+		ldflags: strings.Join([]string{
+			"${config.Riscv64Ldflags}",
+			extraLdflags,
+		}, " "),
+		lldflags: strings.Join([]string{
+			"${config.Riscv64Lldflags}",
+			extraLdflags,
+		}, " "),
+		toolchainCflags: strings.Join(toolchainCflags, " "),
+	}
+}
+
+func init() {
+	registerToolchainFactory(android.Android, android.Riscv64, riscv64ToolchainFactory)
+}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 23bda66..af49e88 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"regexp"
 	"strings"
 )
 
@@ -237,3 +238,11 @@
 	}
 	return flags
 }
+
+var (
+	removedCFlags = regexp.MustCompile(" -fsanitize=[^ ]*memtag-[^ ]* ")
+)
+
+func TidyReduceCFlags(flags string) string {
+	return removedCFlags.ReplaceAllString(flags, " ")
+}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index d9eaf53..eb71aa1 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -63,11 +63,6 @@
 type Toolchain interface {
 	Name() string
 
-	GccRoot() string
-	GccTriple() string
-	// GccVersion should return a real value, not a ninja reference
-	GccVersion() string
-
 	IncludeFlags() string
 
 	ClangTriple() string
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index aebda0b..e2b0f06 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -80,17 +80,7 @@
 	}
 )
 
-const (
-	x86_64GccVersion = "4.9"
-)
-
 func init() {
-
-	pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
-
-	pctx.SourcePathVariable("X86_64GccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
-
 	exportedVars.ExportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
 	exportedVars.ExportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
 
@@ -128,24 +118,12 @@
 	return "x86_64"
 }
 
-func (t *toolchainX86_64) GccRoot() string {
-	return "${config.X86_64GccRoot}"
-}
-
-func (t *toolchainX86_64) GccTriple() string {
-	return "x86_64-linux-android"
-}
-
-func (t *toolchainX86_64) GccVersion() string {
-	return x86_64GccVersion
-}
-
 func (t *toolchainX86_64) IncludeFlags() string {
 	return ""
 }
 
 func (t *toolchainX86_64) ClangTriple() string {
-	return t.GccTriple()
+	return "x86_64-linux-android"
 }
 
 func (t *toolchainX86_64) ToolchainLdflags() string {
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 421b083..3001ab4 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -88,16 +88,7 @@
 	}
 )
 
-const (
-	x86GccVersion = "4.9"
-)
-
 func init() {
-	pctx.StaticVariable("x86GccVersion", x86GccVersion)
-
-	pctx.SourcePathVariable("X86GccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
-
 	exportedVars.ExportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
 	exportedVars.ExportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
 
@@ -134,18 +125,6 @@
 	return "x86"
 }
 
-func (t *toolchainX86) GccRoot() string {
-	return "${config.X86GccRoot}"
-}
-
-func (t *toolchainX86) GccTriple() string {
-	return "x86_64-linux-android"
-}
-
-func (t *toolchainX86) GccVersion() string {
-	return x86GccVersion
-}
-
 func (t *toolchainX86) IncludeFlags() string {
 	return ""
 }
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 976cc25..96a53bf 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -66,13 +66,20 @@
 		"host_bionic_linker_script")
 )
 
+const (
+	x86_64GccVersion = "4.9"
+)
+
 func init() {
+
 	pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
 	pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
 	pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLdflags, " "))
 
 	// Use the device gcc toolchain for now
-	pctx.StaticVariable("LinuxBionicGccRoot", "${X86_64GccRoot}")
+	pctx.StaticVariable("LinuxBionicGccVersion", x86_64GccVersion)
+	pctx.SourcePathVariable("LinuxBionicGccRoot",
+		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${LinuxBionicGccVersion}")
 }
 
 type toolchainLinuxBionic struct {
@@ -84,18 +91,6 @@
 	return "x86_64"
 }
 
-func (t *toolchainLinuxBionic) GccRoot() string {
-	return "${config.LinuxBionicGccRoot}"
-}
-
-func (t *toolchainLinuxBionic) GccTriple() string {
-	return "x86_64-linux-android"
-}
-
-func (t *toolchainLinuxBionic) GccVersion() string {
-	return "4.9"
-}
-
 func (t *toolchainLinuxBionic) IncludeFlags() string {
 	return ""
 }
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 07b95e1..740405e 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -180,18 +180,6 @@
 	return "x86_64"
 }
 
-func (t *toolchainLinux) GccRoot() string {
-	return "${config.LinuxGccRoot}"
-}
-
-func (t *toolchainLinux) GccTriple() string {
-	return "${config.LinuxGccTriple}"
-}
-
-func (t *toolchainLinux) GccVersion() string {
-	return linuxGccVersion
-}
-
 func (t *toolchainLinux) IncludeFlags() string {
 	return ""
 }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index a33606f..561c500 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -175,24 +175,12 @@
 	return "x86_64"
 }
 
-func (t *toolchainWindows) GccRoot() string {
-	return "${config.WindowsGccRoot}"
-}
-
-func (t *toolchainWindows) GccTriple() string {
-	return "${config.WindowsGccTriple}"
-}
-
 func (t *toolchainWindows) ToolchainCflags() string {
-	return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+	return "-B" + filepath.Join("${config.WindowsGccRoot}", "${config.WindowsGccTriple}", "bin")
 }
 
 func (t *toolchainWindows) ToolchainLdflags() string {
-	return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
-}
-
-func (t *toolchainWindows) GccVersion() string {
-	return windowsGccVersion
+	return "-B" + filepath.Join("${config.WindowsGccRoot}", "${config.WindowsGccTriple}", "bin")
 }
 
 func (t *toolchainWindows) IncludeFlags() string {
diff --git a/cc/coverage.go b/cc/coverage.go
index d0902ea..a7356f8 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -108,6 +108,12 @@
 			if EnableContinuousCoverage(ctx) {
 				flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
 			}
+
+			// http://b/248022906, http://b/247941801  enabling coverage and hwasan-globals
+			// instrumentation together causes duplicate-symbol errors for __llvm_profile_filename.
+			if c, ok := ctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(Hwasan) {
+				flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-hwasan-globals=0")
+			}
 		}
 	}
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8a8c107..13c94ad 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -103,6 +103,7 @@
 	*baseCompiler
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
 	installedSharedDeps []string
+	sharedLibraries     android.Paths
 }
 
 func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -142,13 +143,6 @@
 	return flags
 }
 
-func UnstrippedOutputFile(module android.Module) android.Path {
-	if mod, ok := module.(LinkableInterface); ok {
-		return mod.UnstrippedOutputFile()
-	}
-	panic("UnstrippedOutputFile called on non-LinkableInterface module: " + module.Name())
-}
-
 // IsValidSharedDependency takes a module and determines if it is a unique shared library
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
@@ -270,22 +264,9 @@
 	}
 
 	// Grab the list of required shared libraries.
-	seen := make(map[string]bool)
-	var sharedLibraries android.Paths
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if seen[child.Name()] {
-			return false
-		}
-		seen[child.Name()] = true
+	fuzzBin.sharedLibraries = CollectAllSharedDependencies(ctx)
 
-		if IsValidSharedDependency(child) {
-			sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
-			return true
-		}
-		return false
-	})
-
-	for _, lib := range sharedLibraries {
+	for _, lib := range fuzzBin.sharedLibraries {
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			sharedLibraryInstallLocation(
 				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
@@ -412,9 +393,6 @@
 		archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
 		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
-		// Grab the list of required shared libraries.
-		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, UnstrippedOutputFile, IsValidSharedDependency)
-
 		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
@@ -422,10 +400,10 @@
 		files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
 
 		// Package shared libraries
-		files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
+		files = append(files, GetSharedLibsToZip(fuzzModule.sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
-		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
+		files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
@@ -494,3 +472,46 @@
 	}
 	return files
 }
+
+// CollectAllSharedDependencies search over the provided module's dependencies using
+// VisitDirectDeps and WalkDeps to enumerate all shared library dependencies.
+// VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
+// runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
+// have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
+func CollectAllSharedDependencies(ctx android.ModuleContext) android.Paths {
+	seen := make(map[string]bool)
+	recursed := make(map[string]bool)
+
+	var sharedLibraries android.Paths
+
+	// Enumerate the first level of dependencies, as we discard all non-library
+	// modules in the BFS loop below.
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if !IsValidSharedDependency(dep) {
+			return
+		}
+		if seen[ctx.OtherModuleName(dep)] {
+			return
+		}
+		seen[ctx.OtherModuleName(dep)] = true
+		sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped"))
+	})
+
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if !IsValidSharedDependency(child) {
+			return false
+		}
+		if !seen[ctx.OtherModuleName(child)] {
+			seen[ctx.OtherModuleName(child)] = true
+			sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped"))
+		}
+
+		if recursed[ctx.OtherModuleName(child)] {
+			return false
+		}
+		recursed[ctx.OtherModuleName(child)] = true
+		return true
+	})
+
+	return sharedLibraries
+}
diff --git a/cc/library.go b/cc/library.go
index 441eb79..a590b22 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -30,6 +30,7 @@
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
 )
 
 // LibraryProperties is a collection of properties shared by cc library rules/cc.
@@ -288,6 +289,16 @@
 	None                         bazel.BoolAttribute
 }
 
+func stripAttrsFromLinkerAttrs(la *linkerAttributes) stripAttributes {
+	return stripAttributes{
+		Keep_symbols:                 la.stripKeepSymbols,
+		Keep_symbols_and_debug_frame: la.stripKeepSymbolsAndDebugFrame,
+		Keep_symbols_list:            la.stripKeepSymbolsList,
+		All:                          la.stripAll,
+		None:                         la.stripNone,
+	}
+}
+
 func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
 	// For some cc_library modules, their static variants are ready to be
 	// converted, but not their shared variants. For these modules, delegate to
@@ -394,13 +405,7 @@
 
 		Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
-		Strip: stripAttributes{
-			Keep_symbols:                 linkerAttrs.stripKeepSymbols,
-			Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
-			Keep_symbols_list:            linkerAttrs.stripKeepSymbolsList,
-			All:                          linkerAttrs.stripAll,
-			None:                         linkerAttrs.stripNone,
-		},
+		Strip:    stripAttrsFromLinkerAttrs(&linkerAttrs),
 		Features: baseAttributes.features,
 	}
 
@@ -466,6 +471,147 @@
 	}
 }
 
+func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	apiSurfaces := make([]string, 0)
+	apiHeaders := make([]string, 0)
+	// systemapi (non-null `stubs` property)
+	if module.HasStubsVariants() {
+		apiSurfaces = append(apiSurfaces, android.SystemApi.String())
+		apiIncludes := getSystemApiIncludes(ctx, module)
+		if !apiIncludes.isEmpty() {
+			createApiHeaderTarget(ctx, apiIncludes)
+			apiHeaders = append(apiHeaders, apiIncludes.name)
+		}
+	}
+	// vendorapi (non-null `llndk` property)
+	if module.HasLlndkStubs() {
+		apiSurfaces = append(apiSurfaces, android.VendorApi.String())
+		apiIncludes := getVendorApiIncludes(ctx, module)
+		if !apiIncludes.isEmpty() {
+			createApiHeaderTarget(ctx, apiIncludes)
+			apiHeaders = append(apiHeaders, apiIncludes.name)
+		}
+	}
+	// 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)
+	if len(apiSurfaces) > 0 {
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_api_contribution",
+			Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+		}
+		attrs := &bazelCcApiContributionAttributes{
+			Library_name: module.Name(),
+			Api_surfaces: bazel.MakeStringListAttribute(apiSurfaces),
+			Api:          apiLabelAttribute(ctx, module),
+			Hdrs: bazel.MakeLabelListAttribute(
+				bazel.MakeLabelListFromTargetNames(apiHeaders),
+			),
+		}
+		ctx.CreateBazelTargetModule(
+			props,
+			android.CommonAttributes{
+				Name:     android.ApiContributionTargetName(module.Name()),
+				SkipData: proptools.BoolPtr(true),
+			},
+			attrs,
+		)
+	}
+}
+
+// Native apis are versioned in a single .map.txt for all api surfaces
+// Pick any one of the .map.txt files
+func apiLabelAttribute(ctx android.TopDownMutatorContext, module *Module) bazel.LabelAttribute {
+	var apiFile *string
+	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 {
+		ctx.ModuleErrorf("API surface library does not have any API file")
+	}
+	apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label
+	return *bazel.MakeLabelAttribute(apiLabel)
+}
+
+// wrapper struct to flatten the arch and os specific export_include_dirs
+// flattening is necessary since we want to export apis of all arches even when we build for x86 (e.g.)
+type bazelCcApiLibraryHeadersAttributes struct {
+	bazelCcLibraryHeadersAttributes
+
+	Arch *string
+}
+
+func (a *bazelCcApiLibraryHeadersAttributes) isEmpty() bool {
+	return a.Export_includes.IsEmpty() &&
+		a.Export_system_includes.IsEmpty() &&
+		a.Deps.IsEmpty()
+}
+
+type apiIncludes struct {
+	name  string // name of the Bazel target in the generated bp2build workspace
+	attrs bazelCcApiLibraryHeadersAttributes
+}
+
+func (includes *apiIncludes) isEmpty() bool {
+	return includes.attrs.isEmpty()
+}
+
+func (includes *apiIncludes) addDep(name string) {
+	l := bazel.Label{Label: ":" + name}
+	ll := bazel.MakeLabelList([]bazel.Label{l})
+	lla := bazel.MakeLabelListAttribute(ll)
+	includes.attrs.Deps.Append(lla)
+}
+
+func getSystemApiIncludes(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)
+	systemIncludes := android.FirstUniqueStrings(flagProps.Export_system_include_dirs)
+	headerLibs := android.FirstUniqueStrings(linkProps.Export_header_lib_headers)
+	attrs := bazelCcLibraryHeadersAttributes{
+		Export_includes:        bazel.MakeStringListAttribute(includes),
+		Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+		Deps:                   bazel.MakeLabelListAttribute(apiHeaderLabels(ctx, headerLibs)),
+	}
+
+	return apiIncludes{
+		name: c.Name() + ".systemapi.headers",
+		attrs: bazelCcApiLibraryHeadersAttributes{
+			bazelCcLibraryHeadersAttributes: attrs,
+		},
+	}
+}
+
+func getVendorApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+	baseProps := c.library.(*libraryDecorator).flagExporter.Properties
+	llndkProps := c.library.(*libraryDecorator).Properties.Llndk
+	includes := baseProps.Export_include_dirs
+	systemIncludes := baseProps.Export_system_include_dirs
+	// LLNDK can override the base includes
+	if llndkIncludes := llndkProps.Override_export_include_dirs; llndkIncludes != nil {
+		includes = llndkIncludes
+	}
+	if proptools.Bool(llndkProps.Export_headers_as_system) {
+		systemIncludes = append(systemIncludes, includes...)
+		includes = nil
+	}
+
+	attrs := bazelCcLibraryHeadersAttributes{
+		Export_includes:        bazel.MakeStringListAttribute(includes),
+		Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+		Deps:                   bazel.MakeLabelListAttribute(apiHeaderLabels(ctx, llndkProps.Export_llndk_headers)),
+	}
+	return apiIncludes{
+		name: c.Name() + ".vendorapi.headers",
+		attrs: bazelCcApiLibraryHeadersAttributes{
+			bazelCcLibraryHeadersAttributes: attrs,
+		},
+	}
+}
+
 // cc_library creates both static and/or shared libraries for a device and/or
 // host. By default, a cc_library has a single variant that targets the device.
 // Specifying `host_supported: true` also creates a library that targets the
@@ -739,7 +885,7 @@
 	outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 
-	handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
+	handler.module.linker.(*libraryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, ccInfo.UnstrippedOutput)
 
 	var tocFile android.OptionalPath
 	if len(ccInfo.TocFile) > 0 {
@@ -1089,6 +1235,12 @@
 		} else {
 			flag = "--systemapi"
 		}
+		// b/184712170, unless the lib is an NDK library, exclude all public symbols from
+		// the stub so that it is mandated that all symbols are explicitly marked with
+		// either apex or systemapi.
+		if !ctx.Module().(*Module).IsNdk(ctx.Config()) {
+			flag = flag + " --no-ndk"
+		}
 		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
 			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
 		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
@@ -2227,10 +2379,20 @@
 	mod.ModuleBase.MakeUninstallable()
 }
 
+func (library *libraryDecorator) getPartition() string {
+	return library.path.Partition()
+}
+
 func (library *libraryDecorator) getAPIListCoverageXMLPath() android.ModuleOutPath {
 	return library.apiListCoverageXmlPath
 }
 
+func (library *libraryDecorator) overriddenModules() []string {
+	return library.Properties.Overrides
+}
+
+var _ overridable = (*libraryDecorator)(nil)
+
 var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
 
 // versioningMacroNamesList returns a singleton map, where keys are "version macro names",
@@ -2681,13 +2843,7 @@
 			Absolute_includes:        compilerAttrs.absoluteIncludes,
 			Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
-			Strip: stripAttributes{
-				Keep_symbols:                 linkerAttrs.stripKeepSymbols,
-				Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
-				Keep_symbols_list:            linkerAttrs.stripKeepSymbolsList,
-				All:                          linkerAttrs.stripAll,
-				None:                         linkerAttrs.stripNone,
-			},
+			Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
 
 			Features: baseAttributes.features,
 
diff --git a/cc/library_headers.go b/cc/library_headers.go
index a683f58..1c4f354 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/bazel/cquery"
@@ -145,3 +147,118 @@
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
 }
+
+// Append .contribution suffix to input labels
+func apiBazelTargets(ll bazel.LabelList) bazel.LabelList {
+	labels := make([]bazel.Label, 0)
+	for _, l := range ll.Includes {
+		labels = append(labels, bazel.Label{
+			Label: android.ApiContributionTargetName(l.Label),
+		})
+	}
+	return bazel.MakeLabelList(labels)
+}
+
+func apiLibraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	// cc_api_library_headers have a 1:1 mapping to arch/no-arch
+	// 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)
+	// arch and os specific includes
+	archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
+	for _, arch := range allArches { // sorted iteration
+		archApiInclude := archApiIncludes[arch]
+		if !archApiInclude.isEmpty() {
+			createApiHeaderTarget(ctx, archApiInclude)
+			apiIncludes.addDep(archApiInclude.name)
+		}
+	}
+	// os==android includes
+	if !androidOsIncludes.isEmpty() {
+		createApiHeaderTarget(ctx, androidOsIncludes)
+		apiIncludes.addDep(androidOsIncludes.name)
+	}
+
+	if !apiIncludes.isEmpty() {
+		// override the name from <mod>.systemapi.headers --> <mod>.contribution
+		apiIncludes.name = android.ApiContributionTargetName(module.Name())
+		createApiHeaderTarget(ctx, apiIncludes)
+	}
+}
+
+func createApiHeaderTarget(ctx android.TopDownMutatorContext, includes apiIncludes) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_api_library_headers",
+		Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+	}
+	ctx.CreateBazelTargetModule(
+		props,
+		android.CommonAttributes{
+			Name:     includes.name,
+			SkipData: proptools.BoolPtr(true),
+		},
+		&includes.attrs,
+	)
+}
+
+var (
+	allArches = []string{"arm", "arm64", "x86", "x86_64"}
+)
+
+type archApiIncludes map[string]apiIncludes
+
+func archOsSpecificApiIncludes(ctx android.TopDownMutatorContext, module *Module) (archApiIncludes, apiIncludes) {
+	baseProps := bp2BuildParseBaseProps(ctx, module)
+	i := bp2BuildParseExportedIncludes(ctx, module, &baseProps.includes)
+	archRet := archApiIncludes{}
+	for _, arch := range allArches {
+		includes := i.Includes.SelectValue(
+			bazel.ArchConfigurationAxis,
+			arch)
+		systemIncludes := i.SystemIncludes.SelectValue(
+			bazel.ArchConfigurationAxis,
+			arch)
+		deps := baseProps.deps.SelectValue(
+			bazel.ArchConfigurationAxis,
+			arch)
+		attrs := bazelCcLibraryHeadersAttributes{
+			Export_includes:        bazel.MakeStringListAttribute(includes),
+			Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+		}
+		apiDeps := apiBazelTargets(deps)
+		if !apiDeps.IsEmpty() {
+			attrs.Deps = bazel.MakeLabelListAttribute(apiDeps)
+		}
+		apiIncludes := apiIncludes{
+			name: android.ApiContributionTargetName(module.Name()) + "." + arch,
+			attrs: bazelCcApiLibraryHeadersAttributes{
+				bazelCcLibraryHeadersAttributes: attrs,
+				Arch:                            proptools.StringPtr(arch),
+			},
+		}
+		archRet[arch] = apiIncludes
+	}
+
+	// apiIncludes for os == Android
+	androidOsDeps := baseProps.deps.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid)
+	androidOsAttrs := bazelCcLibraryHeadersAttributes{
+		Export_includes: bazel.MakeStringListAttribute(
+			i.Includes.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid),
+		),
+		Export_system_includes: bazel.MakeStringListAttribute(
+			i.SystemIncludes.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid),
+		),
+	}
+	androidOsApiDeps := apiBazelTargets(androidOsDeps)
+	if !androidOsApiDeps.IsEmpty() {
+		androidOsAttrs.Deps = bazel.MakeLabelListAttribute(androidOsApiDeps)
+	}
+	osRet := apiIncludes{
+		name: android.ApiContributionTargetName(module.Name()) + ".androidos",
+		attrs: bazelCcApiLibraryHeadersAttributes{
+			bazelCcLibraryHeadersAttributes: androidOsAttrs,
+		},
+	}
+	return archRet, osRet
+}
diff --git a/cc/library_test.go b/cc/library_test.go
index 6d5eda2..2bc9967 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -259,6 +259,7 @@
 				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"},
@@ -294,6 +295,7 @@
 	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)
diff --git a/cc/linkable.go b/cc/linkable.go
index 2316d86..0522fc6 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -253,6 +253,9 @@
 
 	// VndkVersion returns the VNDK version string for this module.
 	VndkVersion() string
+
+	// Partition returns the partition string for this module.
+	Partition() string
 }
 
 var (
diff --git a/cc/lto.go b/cc/lto.go
index b3e5e74..581856b 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -15,9 +15,9 @@
 package cc
 
 import (
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // LTO (link-time optimization) allows the compiler to optimize and generate
@@ -49,9 +49,12 @@
 
 	// Dep properties indicate that this module needs to be built with LTO
 	// since it is an object dependency of an LTO module.
-	FullDep  bool `blueprint:"mutated"`
-	ThinDep  bool `blueprint:"mutated"`
-	NoLtoDep bool `blueprint:"mutated"`
+	FullEnabled  bool `blueprint:"mutated"`
+	ThinEnabled  bool `blueprint:"mutated"`
+	NoLtoEnabled bool `blueprint:"mutated"`
+	FullDep      bool `blueprint:"mutated"`
+	ThinDep      bool `blueprint:"mutated"`
+	NoLtoDep     bool `blueprint:"mutated"`
 
 	// Use clang lld instead of gnu ld.
 	Use_clang_lld *bool
@@ -70,7 +73,7 @@
 
 func (lto *lto) begin(ctx BaseModuleContext) {
 	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
-		lto.Properties.Lto.Never = proptools.BoolPtr(true)
+		lto.Properties.NoLtoEnabled = true
 	}
 }
 
@@ -151,15 +154,15 @@
 }
 
 func (lto *lto) FullLTO() bool {
-	return lto != nil && Bool(lto.Properties.Lto.Full)
+	return lto != nil && (proptools.Bool(lto.Properties.Lto.Full) || lto.Properties.FullEnabled)
 }
 
 func (lto *lto) ThinLTO() bool {
-	return lto != nil && Bool(lto.Properties.Lto.Thin)
+	return lto != nil && (proptools.Bool(lto.Properties.Lto.Thin) || lto.Properties.ThinEnabled)
 }
 
 func (lto *lto) Never() bool {
-	return lto != nil && Bool(lto.Properties.Lto.Never)
+	return lto != nil && (proptools.Bool(lto.Properties.Lto.Never) || lto.Properties.NoLtoEnabled)
 }
 
 func GlobalThinLTO(ctx android.BaseModuleContext) bool {
@@ -255,13 +258,13 @@
 
 				// LTO properties for dependencies
 				if name == "lto-full" {
-					variation.lto.Properties.Lto.Full = proptools.BoolPtr(true)
+					variation.lto.Properties.FullEnabled = true
 				}
 				if name == "lto-thin" {
-					variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true)
+					variation.lto.Properties.ThinEnabled = true
 				}
 				if name == "lto-none" {
-					variation.lto.Properties.Lto.Never = proptools.BoolPtr(true)
+					variation.lto.Properties.NoLtoEnabled = true
 				}
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
diff --git a/cc/makevars.go b/cc/makevars.go
index de8a8f2..c70d4a6 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -314,8 +314,6 @@
 		ctx.Strict(makePrefix+"LD", "${config.ClangBin}/lld")
 		ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain))
 		ctx.Strict(makePrefix+"TOOLS_PREFIX", "${config.ClangBin}/llvm-")
-		// TODO: GCC version is obsolete now that GCC has been removed.
-		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
 	}
 
 	if target.Os.Class == android.Host {
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 08e2a39..7354be9 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -173,7 +173,9 @@
 	}, attrs)
 }
 
-func (h *headerModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+var _ android.ApiProvider = (*headerModule)(nil)
+
+func (h *headerModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
 	// Generate `cc_api_headers` target for Multi-tree API export
 	createCcApiHeadersTarget(ctx, h.properties.Srcs, h.properties.Exclude_srcs, h.properties.From)
 }
@@ -192,7 +194,6 @@
 	module := &headerModule{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
-	android.InitBazelModule(module)
 	return module
 }
 
@@ -263,7 +264,9 @@
 	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
 }
 
-func (h *versionedHeaderModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+var _ android.ApiProvider = (*versionedHeaderModule)(nil)
+
+func (h *versionedHeaderModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
 	// Glob all .h files under `From`
 	includePattern := headerGlobPattern(proptools.String(h.properties.From))
 	// Generate `cc_api_headers` target for Multi-tree API export
@@ -319,7 +322,6 @@
 	module.AddProperties(&module.properties)
 
 	android.InitAndroidModule(module)
-	android.InitBazelModule(module)
 
 	return module
 }
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 06ded3f..49a919e 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -581,12 +581,12 @@
 }
 
 // Names of the cc_api_header targets in the bp2build workspace
-func (s *stubDecorator) apiHeaderLabels(ctx android.TopDownMutatorContext) bazel.LabelList {
+func apiHeaderLabels(ctx android.TopDownMutatorContext, hdrLibs []string) bazel.LabelList {
 	addSuffix := func(ctx android.BazelConversionPathContext, module blueprint.Module) string {
 		label := android.BazelModuleLabel(ctx, module)
 		return android.ApiContributionTargetName(label)
 	}
-	return android.BazelLabelForModuleDepsWithFn(ctx, s.properties.Export_header_libs, addSuffix)
+	return android.BazelLabelForModuleDepsWithFn(ctx, hdrLibs, addSuffix)
 }
 
 func ndkLibraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
@@ -604,7 +604,7 @@
 		apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(symbolFile)).Label
 		attrs.Api = *bazel.MakeLabelAttribute(apiLabel)
 	}
-	apiHeaders := stubLibrary.apiHeaderLabels(ctx)
+	apiHeaders := apiHeaderLabels(ctx, stubLibrary.properties.Export_header_libs)
 	attrs.Hdrs = bazel.MakeLabelListAttribute(apiHeaders)
 	apiContributionTargetName := android.ApiContributionTargetName(ctx.ModuleName())
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: apiContributionTargetName}, attrs)
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index f893d41..efad70a 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -111,6 +111,11 @@
         action='store_true',
         dest='systemapi',
         help='Use the SystemAPI variant.')
+    parser.add_argument(
+        '--no-ndk',
+        action='store_false',
+        dest='ndk',
+        help='Do not include NDK APIs.')
 
     parser.add_argument('--api-map',
                         type=resolved_path,
@@ -147,7 +152,7 @@
         verbosity = 2
     logging.basicConfig(level=verbose_map[verbosity])
 
-    filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi)
+    filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi, args.ndk)
     with args.symbol_file.open() as symbol_file:
         try:
           versions = symbolfile.SymbolFileParser(symbol_file, api_map, filt).parse()
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 450719b..1e0bdf3 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -424,6 +424,45 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+    def test_integration_with_nondk(self) -> None:
+        input_file = io.StringIO(textwrap.dedent("""\
+            VERSION_1 {
+                global:
+                    foo;
+                    bar; # apex
+                local:
+                    *;
+            };
+        """))
+        f = copy(self.filter)
+        f.apex = True
+        f.ndk = False   # ndk symbols should be excluded
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
+        versions = parser.parse()
+
+        src_file = io.StringIO()
+        version_file = io.StringIO()
+        symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.apex = True
+        f.ndk = False   # ndk symbols should be excluded
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file, f)
+        generator.write(versions)
+
+        expected_src = textwrap.dedent("""\
+            void bar() {}
+        """)
+        self.assertEqual(expected_src, src_file.getvalue())
+
+        expected_version = textwrap.dedent("""\
+            VERSION_1 {
+                global:
+                    bar;
+            };
+        """)
+        self.assertEqual(expected_version, version_file.getvalue())
+
     def test_empty_stub(self) -> None:
         """Tests that empty stubs can be generated.
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 867c36c..3756810 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -33,7 +33,7 @@
 	ctx.RegisterModuleType("cc_prebuilt_library_static", PrebuiltStaticLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_test_library_shared", PrebuiltSharedTestLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_object", prebuiltObjectFactory)
-	ctx.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
+	ctx.RegisterModuleType("cc_prebuilt_binary", PrebuiltBinaryFactory)
 }
 
 type prebuiltLinkerInterface interface {
@@ -668,15 +668,21 @@
 }
 
 // cc_prebuilt_binary installs a precompiled executable in srcs property in the
-// device's directory.
-func prebuiltBinaryFactory() android.Module {
+// device's directory, for both the host and device
+func PrebuiltBinaryFactory() android.Module {
 	module, _ := NewPrebuiltBinary(android.HostAndDeviceSupported)
 	return module.Init()
 }
 
+type prebuiltBinaryBazelHandler struct {
+	module    *Module
+	decorator *binaryDecorator
+}
+
 func NewPrebuiltBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
-	module, binary := newBinary(hod, false)
+	module, binary := newBinary(hod, true)
 	module.compiler = nil
+	module.bazelHandler = &prebuiltBinaryBazelHandler{module, binary}
 
 	prebuilt := &prebuiltBinaryLinker{
 		binaryDecorator: binary,
@@ -690,6 +696,53 @@
 	return module, binary
 }
 
+var _ BazelHandler = (*prebuiltBinaryBazelHandler)(nil)
+
+func (h *prebuiltBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+	bazelCtx := ctx.Config().BazelContext
+	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+}
+
+func (h *prebuiltBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+	bazelCtx := ctx.Config().BazelContext
+	outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+		return
+	}
+	if len(outputs) != 1 {
+		ctx.ModuleErrorf("Expected a single output for `%s`, but got:\n%v", label, outputs)
+		return
+	}
+	out := android.PathForBazelOut(ctx, outputs[0])
+	h.module.outputFile = android.OptionalPathForPath(out)
+	h.module.maybeUnhideFromMake()
+}
+
+type bazelPrebuiltBinaryAttributes struct {
+	Src   bazel.LabelAttribute
+	Strip stripAttributes
+}
+
+func prebuiltBinaryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	prebuiltAttrs := bp2BuildParsePrebuiltBinaryProps(ctx, module)
+
+	var la linkerAttributes
+	la.convertStripProps(ctx, module)
+	attrs := &bazelPrebuiltBinaryAttributes{
+		Src:   prebuiltAttrs.Src,
+		Strip: stripAttrsFromLinkerAttrs(&la),
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_prebuilt_binary",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_binary.bzl",
+	}
+
+	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+}
+
 type Sanitized struct {
 	None struct {
 		Srcs []string `android:"path,arch_variant"`
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index e959157..95fa99e 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -663,3 +663,47 @@
 		testFunc(t, disabledSourceStublibBp+prebuiltStublibBp+installedlibBp)
 	})
 }
+
+func TestPrebuiltBinaryNoSrcsNoError(t *testing.T) {
+	const bp = `
+cc_prebuilt_binary {
+	name: "bintest",
+	srcs: [],
+}`
+	ctx := testPrebuilt(t, bp, map[string][]byte{})
+	mod := ctx.ModuleForTests("bintest", "android_arm64_armv8-a").Module().(*Module)
+	android.AssertBoolEquals(t, `expected no srcs to yield no output file`, false, mod.OutputFile().Valid())
+}
+
+func TestPrebuiltBinaryMultipleSrcs(t *testing.T) {
+	const bp = `
+cc_prebuilt_binary {
+	name: "bintest",
+	srcs: ["foo", "bar"],
+}`
+	testCcError(t, `Android.bp:4:6: module "bintest" variant "android_arm64_armv8-a": srcs: multiple prebuilt source files`, bp)
+}
+
+func TestPrebuiltBinaryWithBazel(t *testing.T) {
+	const bp = `
+cc_prebuilt_binary {
+	name: "bintest",
+	srcs: ["bin"],
+	bazel_module: { label: "//bin/foo:foo" },
+}`
+	const outBaseDir = "outputbase"
+	const expectedOut = outBaseDir + "/execroot/__main__/bin"
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir:      outBaseDir,
+		LabelToOutputFiles: map[string][]string{"//bin/foo:foo": []string{"bin"}},
+	}
+	ctx := testCcWithConfig(t, config)
+	bin := ctx.ModuleForTests("bintest", "android_arm64_armv8-a").Module().(*Module)
+	out := bin.OutputFile()
+	if !out.Valid() {
+		t.Error("Invalid output file")
+		return
+	}
+	android.AssertStringEquals(t, "output file", expectedOut, out.String())
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 436b149..0b47f0e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -881,8 +881,12 @@
 	switch t {
 	case Asan:
 		sanitize.Properties.Sanitize.Address = bPtr
+		// For ASAN variant, we need to disable Memtag_stack
+		sanitize.Properties.Sanitize.Memtag_stack = nil
 	case Hwasan:
 		sanitize.Properties.Sanitize.Hwaddress = bPtr
+		// For HWAsan variant, we need to disable Memtag_stack
+		sanitize.Properties.Sanitize.Memtag_stack = nil
 	case tsan:
 		sanitize.Properties.Sanitize.Thread = bPtr
 	case intOverflow:
@@ -895,6 +899,7 @@
 		sanitize.Properties.Sanitize.Memtag_heap = bPtr
 	case Memtag_stack:
 		sanitize.Properties.Sanitize.Memtag_stack = bPtr
+		// We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
 	case Fuzzer:
 		sanitize.Properties.Sanitize.Fuzzer = bPtr
 	default:
diff --git a/cc/strip.go b/cc/strip.go
index c60e135..5c32d8b 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -56,7 +56,9 @@
 	forceEnable := Bool(stripper.StripProperties.Strip.All) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame)
-	return !forceDisable && (forceEnable || defaultEnable)
+	// create_minidebuginfo doesn't work for riscv64 yet, disable stripping for now
+	riscv64 := actx.Arch().ArchType == android.Riscv64
+	return !forceDisable && (forceEnable || defaultEnable) && !riscv64
 }
 
 // Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl.
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 471a12f..94c8567 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -41,6 +41,7 @@
 ALL_ARCHITECTURES = (
     Arch('arm'),
     Arch('arm64'),
+    Arch('riscv64'),
     Arch('x86'),
     Arch('x86_64'),
 )
@@ -208,12 +209,14 @@
     symbol should be omitted or not
     """
 
-    def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi: bool = False):
+    def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi:
+                 bool = False, ndk: bool = True):
         self.arch = arch
         self.api = api
         self.llndk = llndk
         self.apex = apex
         self.systemapi = systemapi
+        self.ndk = ndk
 
     def _should_omit_tags(self, tags: Tags) -> bool:
         """Returns True if the tagged object should be omitted.
@@ -253,8 +256,13 @@
 
     def should_omit_symbol(self, symbol: Symbol) -> bool:
         """Returns True if the symbol should be omitted."""
-        return self._should_omit_tags(symbol.tags)
+        if not symbol.tags.has_mode_tags and not self.ndk:
+            # Symbols that don't have mode tags are NDK. They are usually
+            # included, but have to be omitted if NDK symbols are explicitly
+            # filtered-out
+            return True
 
+        return self._should_omit_tags(symbol.tags)
 
 def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
     """Returns true if the symbol is present for the given architecture."""
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index e17a8d0..856b9d7 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -308,6 +308,20 @@
     def assertInclude(self, f: Filter, s: Symbol) -> None:
         self.assertFalse(f.should_omit_symbol(s))
 
+    def test_omit_ndk(self) -> None:
+        f_ndk = self.filter
+        f_nondk = copy(f_ndk)
+        f_nondk.ndk = False
+        f_nondk.apex = True
+
+        s_ndk = Symbol('foo', Tags())
+        s_nonndk = Symbol('foo', Tags.from_strs(['apex']))
+
+        self.assertInclude(f_ndk, s_ndk)
+        self.assertOmit(f_ndk, s_nonndk)
+        self.assertOmit(f_nondk, s_ndk)
+        self.assertInclude(f_nondk, s_nonndk)
+
     def test_omit_llndk(self) -> None:
         f_none = self.filter
         f_llndk = copy(f_none)
diff --git a/cc/testing.go b/cc/testing.go
index e42b9fa..992069b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"encoding/json"
 	"path/filepath"
 	"testing"
 
@@ -736,3 +737,22 @@
 		t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
 	}
 }
+
+func checkOverrides(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, jsonPath string, expected []string) {
+	t.Helper()
+	out := singleton.MaybeOutput(jsonPath)
+	content := android.ContentFromFileRuleForTests(t, out)
+
+	var flags snapshotJsonFlags
+	if err := json.Unmarshal([]byte(content), &flags); err != nil {
+		t.Errorf("Error while unmarshalling json %q: %s", jsonPath, err.Error())
+		return
+	}
+
+	for _, moduleName := range expected {
+		if !android.InList(moduleName, flags.Overrides) {
+			t.Errorf("expected %q to be in %q: %q", moduleName, flags.Overrides, content)
+			return
+		}
+	}
+}
diff --git a/cc/tidy.go b/cc/tidy.go
index 082cf88..2ba13ca 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -76,9 +76,12 @@
 	if tidy.Properties.Tidy != nil && !*tidy.Properties.Tidy {
 		return flags
 	}
-	// Some projects like external/* and vendor/* have clang-tidy disabled by default.
-	// They can enable clang-tidy explicitly with the "tidy:true" property.
-	if config.NoClangTidyForDir(ctx.ModuleDir()) && !proptools.Bool(tidy.Properties.Tidy) {
+	// Some projects like external/* and vendor/* have clang-tidy disabled by default,
+	// unless they are enabled explicitly with the "tidy:true" property or
+	// when TIDY_EXTERNAL_VENDOR is set to true.
+	if !ctx.Config().IsEnvTrue("TIDY_EXTERNAL_VENDOR") &&
+		!proptools.Bool(tidy.Properties.Tidy) &&
+		config.NoClangTidyForDir(ctx.ModuleDir()) {
 		return flags
 	}
 	// If not explicitly disabled, set flags.Tidy to generate .tidy rules.
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 77e6f6f..2dcf26e 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -239,6 +239,9 @@
 		}
 		prop.RuntimeLibs = m.SnapshotRuntimeLibs()
 		prop.Required = m.RequiredModuleNames()
+		if o, ok := m.(overridable); ok {
+			prop.Overrides = o.overriddenModules()
+		}
 		for _, path := range m.InitRc() {
 			prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base()))
 		}
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 2bb43ab..6a98778 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -42,6 +42,13 @@
 	}
 
 	cc_library {
+		name: "libvendor_override",
+		vendor: true,
+		nocrt: true,
+		overrides: ["libvendor"],
+	}
+
+	cc_library {
 		name: "libvendor_available",
 		vendor_available: true,
 		nocrt: true,
@@ -65,6 +72,13 @@
 		nocrt: true,
 	}
 
+	cc_binary {
+		name: "vendor_bin_override",
+		vendor: true,
+		nocrt: true,
+		overrides: ["vendor_bin"],
+	}
+
 	cc_prebuilt_library_static {
 		name: "libb",
 		vendor_available: true,
@@ -150,6 +164,8 @@
 			jsonFiles = append(jsonFiles,
 				filepath.Join(binaryDir, "vendor_bin.json"),
 				filepath.Join(binaryDir, "vendor_available_bin.json"))
+
+			checkOverrides(t, ctx, snapshotSingleton, filepath.Join(binaryDir, "vendor_bin_override.json"), []string{"vendor_bin"})
 		}
 
 		// For header libraries, all vendor:true and vendor_available modules are captured.
@@ -161,6 +177,8 @@
 		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
 		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
 		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+
+		checkOverrides(t, ctx, snapshotSingleton, filepath.Join(sharedDir, "libvendor_override.so.json"), []string{"libvendor"})
 	}
 
 	for _, jsonFile := range jsonFiles {
@@ -506,11 +524,13 @@
 				],
 				shared_libs: [
 					"libvendor",
+					"libvendor_override",
 					"libvendor_available",
 					"lib64",
 				],
 				binaries: [
 					"bin",
+					"bin_override",
 				],
 			},
 			arm: {
@@ -526,6 +546,7 @@
 				],
 				shared_libs: [
 					"libvendor",
+					"libvendor_override",
 					"libvendor_available",
 					"lib32",
 				],
@@ -577,6 +598,30 @@
 		},
 	}
 
+	vendor_snapshot_shared {
+		name: "libvendor_override",
+		version: "31",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		overrides: ["libvendor"],
+		shared_libs: [
+			"libvendor_without_snapshot",
+			"libvendor_available",
+			"libvndk",
+		],
+		arch: {
+			arm64: {
+				src: "override/libvendor.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+			arm: {
+				src: "override/libvendor.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
 	vendor_snapshot_static {
 		name: "lib32",
 		version: "31",
@@ -745,6 +790,21 @@
 	}
 
 	vendor_snapshot_binary {
+		name: "bin_override",
+		version: "31",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		overrides: ["bin"],
+		arch: {
+			arm64: {
+				src: "override/bin",
+			},
+		},
+		symlinks: ["binfoo", "binbar"],
+	}
+
+	vendor_snapshot_binary {
 		name: "bin32",
 		version: "31",
 		target_arch: "arm64",
@@ -793,6 +853,7 @@
 		"framework/symbol.txt":             nil,
 		"vendor/Android.bp":                []byte(vendorProprietaryBp),
 		"vendor/bin":                       nil,
+		"vendor/override/bin":              nil,
 		"vendor/bin32":                     nil,
 		"vendor/bin.cpp":                   nil,
 		"vendor/client.cpp":                nil,
@@ -806,6 +867,7 @@
 		"vendor/libvendor.a":               nil,
 		"vendor/libvendor.cfi.a":           nil,
 		"vendor/libvendor.so":              nil,
+		"vendor/override/libvendor.so":     nil,
 		"vendor/lib32.a":                   nil,
 		"vendor/lib32.so":                  nil,
 		"vendor/lib64.a":                   nil,
@@ -958,6 +1020,23 @@
 	if inList(binaryVariant, binVariants) {
 		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
 	}
+
+	// test overrides property
+	binOverrideModule := ctx.ModuleForTests("bin_override.vendor_binary.31.arm64", binaryVariant)
+	binOverrideModule.Output("bin")
+	binOverrideMkEntries := android.AndroidMkEntriesForTest(t, ctx, binOverrideModule.Module())
+	binOverrideEntry := binOverrideMkEntries[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	if !inList("bin", binOverrideEntry) {
+		t.Errorf("bin_override must override bin but was %q\n", binOverrideEntry)
+	}
+
+	libvendorOverrideModule := ctx.ModuleForTests("libvendor_override.vendor_shared.31.arm64", sharedVariant)
+	libvendorOverrideModule.Output("libvendor.so")
+	libvendorOverrideMkEntries := android.AndroidMkEntriesForTest(t, ctx, libvendorOverrideModule.Module())
+	libvendorOverrideEntry := libvendorOverrideMkEntries[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	if !inList("libvendor", libvendorOverrideEntry) {
+		t.Errorf("libvendor_override must override libvendor but was %q\n", libvendorOverrideEntry)
+	}
 }
 
 func TestVendorSnapshotSanitizer(t *testing.T) {
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index d4a57bf..ba0648d 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -150,6 +150,7 @@
 var defaultMinSdkVersion string
 var useVersion string
 var staticDeps bool
+var writeCmd bool
 var jetifier bool
 
 func InList(s string, list []string) bool {
@@ -206,6 +207,10 @@
 	return p.Packaging == "jar"
 }
 
+func (p Pom) IsApk() bool {
+	return p.Packaging == "apk"
+}
+
 func (p Pom) IsHostModule() bool {
 	return hostModuleNames.IsHostModule(p.GroupId, p.ArtifactId)
 }
@@ -243,6 +248,8 @@
 func (p Pom) ImportModuleType() string {
 	if p.IsAar() {
 		return "android_library_import"
+	} else if p.IsApk() {
+		return "android_app_import"
 	} else if p.IsHostOnly() {
 		return "java_import_host"
 	} else {
@@ -253,6 +260,8 @@
 func (p Pom) BazelImportTargetType() string {
 	if p.IsAar() {
 		return "aar_import"
+	} else if p.IsApk() {
+		return "apk_import"
 	} else {
 		return "java_import"
 	}
@@ -261,6 +270,8 @@
 func (p Pom) ImportProperty() string {
 	if p.IsAar() {
 		return "aars"
+	} else if p.IsApk() {
+		return "apk"
 	} else {
 		return "jars"
 	}
@@ -269,6 +280,8 @@
 func (p Pom) BazelImportProperty() string {
 	if p.IsAar() {
 		return "aar"
+	} else if p.IsApk() {
+		return "apk"
 	} else {
 		return "jars"
 	}
@@ -492,8 +505,12 @@
 var bpTemplate = template.Must(template.New("bp").Parse(`
 {{.ImportModuleType}} {
     name: "{{.BpName}}",
+    {{- if .IsApk}}
+    {{.ImportProperty}}: "{{.ArtifactFile}}",
+    {{- else}}
     {{.ImportProperty}}: ["{{.ArtifactFile}}"],
     sdk_version: "{{.SdkVersion}}",
+    {{- end}}
     {{- if .Jetifier}}
     jetifier: true,
     {{- end}}
@@ -534,8 +551,14 @@
     ],
     {{- end}}
     {{- else if not .IsHostOnly}}
+    {{- if not .IsApk}}
     min_sdk_version: "{{.DefaultMinSdkVersion}}",
     {{- end}}
+    {{- end}}
+    {{- if .IsApk}}
+    presigned: true
+    {{- end}}
+
 }
 `))
 
@@ -810,6 +833,9 @@
   -use-version <version>
      If the maven directory contains multiple versions of artifacts and their pom files,
      -use-version can be used to only write Android.bp files for a specific version of those artifacts.
+  -write-cmd
+     Whether to write the command line arguments used to generate the build file as a comment at
+     the top of the build file itself.
   -jetifier
      Sets jetifier: true for all modules.
   <dir>
@@ -837,6 +863,7 @@
 	flag.StringVar(&defaultMinSdkVersion, "default-min-sdk-version", "24", "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24")
 	flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
 	flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
+	flag.BoolVar(&writeCmd, "write-cmd", true, "Write command line arguments as a comment")
 	flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules")
 	flag.StringVar(&regen, "regen", "", "Rewrite specified file")
 	flag.BoolVar(&pom2build, "pom2build", false, "If true, will generate a Bazel BUILD file *instead* of a .bp file")
@@ -964,8 +991,13 @@
 	if pom2build {
 		commentString = "#"
 	}
-	fmt.Fprintln(buf, commentString, "Automatically generated with:")
-	fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
+
+	fmt.Fprintln(buf, commentString, "This is a generated file. Do not modify directly.")
+
+	if writeCmd {
+		fmt.Fprintln(buf, commentString, "Automatically generated with:")
+		fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
+	}
 
 	if prepend != "" {
 		contents, err := ioutil.ReadFile(prepend)
@@ -985,7 +1017,7 @@
 
 	for _, pom := range poms {
 		var err error
-		if staticDeps {
+		if staticDeps && !pom.IsApk() {
 			err = depsTemplate.Execute(buf, pom)
 		} else {
 			err = template.Execute(buf, pom)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 0b8cc88..0ba25c8 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -24,6 +24,7 @@
 	"time"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/bp2build"
 	"android/soong/shared"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
@@ -48,11 +49,12 @@
 	delveListen string
 	delvePath   string
 
-	moduleGraphFile   string
-	moduleActionsFile string
-	docFile           string
-	bazelQueryViewDir string
-	bp2buildMarker    string
+	moduleGraphFile     string
+	moduleActionsFile   string
+	docFile             string
+	bazelQueryViewDir   string
+	bazelApiBp2buildDir string
+	bp2buildMarker      string
 
 	cmdlineArgs bootstrap.Args
 )
@@ -81,6 +83,7 @@
 	flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
 	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+	flag.StringVar(&bazelApiBp2buildDir, "bazel_api_bp2build_dir", "", "path to the bazel api_bp2build directory relative to --top")
 	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
@@ -129,6 +132,8 @@
 		buildMode = android.Bp2build
 	} else if bazelQueryViewDir != "" {
 		buildMode = android.GenerateQueryView
+	} else if bazelApiBp2buildDir != "" {
+		buildMode = android.ApiBp2build
 	} else if moduleGraphFile != "" {
 		buildMode = android.GenerateModuleGraph
 	} else if docFile != "" {
@@ -162,10 +167,15 @@
 		return configuration.BazelContext.InvokeBazel(configuration)
 	}
 	ctx.SetBeforePrepareBuildActionsHook(bazelHook)
-
 	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, ctx.Context, configuration)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
+	bazelPaths, err := readBazelPaths(configuration)
+	if err != nil {
+		panic("Bazel deps file not found: " + err.Error())
+	}
+	ninjaDeps = append(ninjaDeps, bazelPaths...)
+
 	globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
 	ninjaDeps = append(ninjaDeps, globListFiles...)
 
@@ -178,7 +188,7 @@
 	defer ctx.EventHandler.End("queryview")
 	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
 	absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
-	if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
+	if err := createBazelWorkspace(codegenContext, absoluteQueryViewDir); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
@@ -186,6 +196,96 @@
 	touch(shared.JoinPath(topDir, queryviewMarker))
 }
 
+// Run the code-generation phase to convert API contributions to BUILD files.
+// Return marker file for the new synthetic workspace
+func runApiBp2build(configuration android.Config, extraNinjaDeps []string) string {
+	// Create a new context and register mutators that are only meaningful to API export
+	ctx := android.NewContext(configuration)
+	ctx.EventHandler.Begin("api_bp2build")
+	defer ctx.EventHandler.End("api_bp2build")
+	ctx.SetNameInterface(newNameResolver(configuration))
+	ctx.RegisterForApiBazelConversion()
+
+	// Register the Android.bp files in the tree
+	// Add them to the workspace's .d file
+	ctx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+	if paths, err := ctx.ListModulePaths("."); err == nil {
+		extraNinjaDeps = append(extraNinjaDeps, paths...)
+	} else {
+		panic(err)
+	}
+
+	// Run the loading and analysis phase
+	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs,
+		bootstrap.StopBeforePrepareBuildActions,
+		ctx.Context,
+		configuration)
+	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+
+	// Add the globbed dependencies
+	globs := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
+	ninjaDeps = append(ninjaDeps, globs...)
+
+	// Run codegen to generate BUILD files
+	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.ApiBp2build)
+	absoluteApiBp2buildDir := shared.JoinPath(topDir, bazelApiBp2buildDir)
+	if err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir); err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+
+	// Create soong_injection repository
+	soongInjectionFiles := bp2build.CreateSoongInjectionFiles(configuration, bp2build.CodegenMetrics{})
+	absoluteSoongInjectionDir := shared.JoinPath(topDir, configuration.SoongOutDir(), bazel.SoongInjectionDirName)
+	for _, file := range soongInjectionFiles {
+		writeReadOnlyFile(absoluteSoongInjectionDir, file)
+	}
+
+	workspace := shared.JoinPath(configuration.SoongOutDir(), "api_bp2build")
+
+	excludes := bazelArtifacts()
+	// Exclude all src BUILD files
+	excludes = append(excludes, apiBuildFileExcludes()...)
+
+	// Create the symlink forest
+	symlinkDeps := bp2build.PlantSymlinkForest(
+		configuration,
+		topDir,
+		workspace,
+		bazelApiBp2buildDir,
+		".",
+		excludes)
+	ninjaDeps = append(ninjaDeps, symlinkDeps...)
+
+	workspaceMarkerFile := workspace + ".marker"
+	writeDepFile(workspaceMarkerFile, *ctx.EventHandler, ninjaDeps)
+	touch(shared.JoinPath(topDir, workspaceMarkerFile))
+	return workspaceMarkerFile
+}
+
+// With some exceptions, api_bp2build does not have any dependencies on the checked-in BUILD files
+// Exclude them from the generated workspace to prevent unrelated errors during the loading phase
+func apiBuildFileExcludes() []string {
+	ret := make([]string, 0)
+
+	srcs, err := getExistingBazelRelatedFiles(topDir)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+		os.Exit(1)
+	}
+	for _, src := range srcs {
+		if src != "WORKSPACE" &&
+			src != "BUILD" &&
+			src != "BUILD.bazel" &&
+			!strings.HasPrefix(src, "build/bazel") &&
+			!strings.HasPrefix(src, "prebuilts/clang") {
+			ret = append(ret, src)
+		}
+	}
+	return ret
+}
+
 func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
 	if len(metricsDir) < 1 {
 		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
@@ -248,6 +348,8 @@
 		return bp2buildMarker
 	} else if configuration.IsMixedBuildsEnabled() {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
+	} else if configuration.BuildMode == android.ApiBp2build {
+		return runApiBp2build(configuration, extraNinjaDeps)
 	} else {
 		var stopBefore bootstrap.StopBefore
 		if configuration.BuildMode == android.GenerateModuleGraph {
@@ -476,12 +578,22 @@
 	return files, nil
 }
 
+func bazelArtifacts() []string {
+	return []string{
+		"bazel-bin",
+		"bazel-genfiles",
+		"bazel-out",
+		"bazel-testlogs",
+		"bazel-" + filepath.Base(topDir),
+	}
+}
+
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
 func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
+	var codegenMetrics bp2build.CodegenMetrics
 	eventHandler := metrics.EventHandler{}
-	var metrics bp2build.CodegenMetrics
 	eventHandler.Do("bp2build", func() {
 
 		// Register an alternate set of singletons and mutators for bazel
@@ -494,43 +606,43 @@
 		bp2buildCtx.SetNameInterface(newNameResolver(configuration))
 		bp2buildCtx.RegisterForBazelConversion()
 
+		var ninjaDeps []string
+		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+
 		// The bp2build process is a purely functional process that only depends on
 		// Android.bp files. It must not depend on the values of per-build product
 		// configurations or variables, since those will generate different BUILD
 		// files based on how the user has configured their tree.
 		bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
-		modulePaths, err := bp2buildCtx.ListModulePaths(".")
-		if err != nil {
+		if modulePaths, err := bp2buildCtx.ListModulePaths("."); err != nil {
 			panic(err)
+		} else {
+			ninjaDeps = append(ninjaDeps, modulePaths...)
 		}
 
-		extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
-
 		// Run the loading and analysis pipeline to prepare the graph of regular
 		// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 		// from the regular Modules.
-		blueprintArgs := cmdlineArgs
-		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
-		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+		eventHandler.Do("bootstrap", func() {
+			blueprintArgs := cmdlineArgs
+			bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
+			ninjaDeps = append(ninjaDeps, bootstrapDeps...)
+		})
 
 		globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
 		ninjaDeps = append(ninjaDeps, globListFiles...)
 
 		// Run the code-generation phase to convert BazelTargetModules to BUILD files
-		// and print conversion metrics to the user.
+		// and print conversion codegenMetrics to the user.
 		codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
-		metrics = bp2build.Codegen(codegenContext)
+		eventHandler.Do("codegen", func() {
+			codegenMetrics = bp2build.Codegen(codegenContext)
+		})
 
 		generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
 		workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
 
-		excludes := []string{
-			"bazel-bin",
-			"bazel-genfiles",
-			"bazel-out",
-			"bazel-testlogs",
-			"bazel-" + filepath.Base(topDir),
-		}
+		excludes := bazelArtifacts()
 
 		if outDir[0] != '/' {
 			excludes = append(excludes, outDir)
@@ -547,11 +659,17 @@
 
 		excludes = append(excludes, getTemporaryExcludes()...)
 
-		symlinkForestDeps := bp2build.PlantSymlinkForest(
-			configuration, topDir, workspaceRoot, generatedRoot, ".", excludes)
+		// PlantSymlinkForest() returns all the directories that were readdir()'ed.
+		// Such a directory SHOULD be added to `ninjaDeps` so that a child directory
+		// or file created/deleted under it would trigger an update of the symlink
+		// forest.
+		eventHandler.Do("symlink_forest", func() {
+			symlinkForestDeps := bp2build.PlantSymlinkForest(
+				configuration, topDir, workspaceRoot, generatedRoot, ".", excludes)
+			ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+		})
 
 		ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
-		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 
 		writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
 
@@ -563,9 +681,9 @@
 	// for queryview, since that's a total repo-wide conversion and there's a
 	// 1:1 mapping for each module.
 	if configuration.IsEnvTrue("BP2BUILD_VERBOSE") {
-		metrics.Print()
+		codegenMetrics.Print()
 	}
-	writeBp2BuildMetrics(&metrics, configuration, eventHandler)
+	writeBp2BuildMetrics(&codegenMetrics, configuration, eventHandler)
 }
 
 // Write Bp2Build metrics into $LOG_DIR
@@ -586,3 +704,14 @@
 	}
 	codegenMetrics.Write(metricsDir)
 }
+
+func readBazelPaths(configuration android.Config) ([]string, error) {
+	depsPath := configuration.Getenv("BAZEL_DEPS_FILE")
+
+	data, err := os.ReadFile(depsPath)
+	if err != nil {
+		return nil, err
+	}
+	paths := strings.Split(strings.TrimSpace(string(data)), "\n")
+	return paths, nil
+}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 983dbf0..cd1d6fb 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -23,8 +23,9 @@
 	"android/soong/bp2build"
 )
 
-func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
-	os.RemoveAll(bazelQueryViewDir)
+// A helper function to generate a Read-only Bazel workspace in outDir
+func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string) error {
+	os.RemoveAll(outDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
 	res, err := bp2build.GenerateBazelTargets(ctx, true)
@@ -33,9 +34,9 @@
 	}
 
 	filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(),
-		bp2build.QueryView)
+		ctx.Mode())
 	for _, f := range filesToWrite {
-		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
+		if err := writeReadOnlyFile(outDir, f); err != nil {
 			return err
 		}
 	}
diff --git a/cmd/zip2zip/BUILD.bazel b/cmd/zip2zip/BUILD.bazel
new file mode 100644
index 0000000..1915a2d
--- /dev/null
+++ b/cmd/zip2zip/BUILD.bazel
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+alias(
+    name = "zip2zip",
+    actual = "//prebuilts/build-tools:linux-x86/bin/zip2zip",
+)
diff --git a/compliance/build_license_metadata/build_license_metadata.go b/compliance/build_license_metadata/build_license_metadata.go
index 53d2407..fb4b784 100644
--- a/compliance/build_license_metadata/build_license_metadata.go
+++ b/compliance/build_license_metadata/build_license_metadata.go
@@ -66,6 +66,7 @@
 
 	packageName := flags.String("p", "", "license package name")
 	moduleType := newMultiString(flags, "mt", "module type")
+	moduleName := flags.String("mn", "", "module name")
 	kinds := newMultiString(flags, "k", "license kinds")
 	moduleClass := newMultiString(flags, "mc", "module class")
 	conditions := newMultiString(flags, "c", "license conditions")
@@ -83,6 +84,7 @@
 
 	metadata := license_metadata_proto.LicenseMetadata{}
 	metadata.PackageName = proto.String(*packageName)
+	metadata.ModuleName = proto.String(*moduleName)
 	metadata.ModuleTypes = *moduleType
 	metadata.ModuleClasses = *moduleClass
 	metadata.IsContainer = proto.Bool(*isContainer)
diff --git a/compliance/license_metadata_proto/license_metadata.pb.go b/compliance/license_metadata_proto/license_metadata.pb.go
index 44dbc78..69aa377 100644
--- a/compliance/license_metadata_proto/license_metadata.pb.go
+++ b/compliance/license_metadata_proto/license_metadata.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.19.0
+// 	protoc-gen-go v1.28.0
+// 	protoc        v3.12.4
 // source: license_metadata.proto
 
 package license_metadata_proto
@@ -40,7 +40,9 @@
 	unknownFields protoimpl.UnknownFields
 
 	// package_name identifies the source package. License texts are named relative to the package name.
-	PackageName   *string  `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"`
+	PackageName *string `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"`
+	// module_name identifies the target
+	ModuleName    *string  `protobuf:"bytes,14,opt,name=module_name,json=moduleName" json:"module_name,omitempty"`
 	ModuleTypes   []string `protobuf:"bytes,2,rep,name=module_types,json=moduleTypes" json:"module_types,omitempty"`
 	ModuleClasses []string `protobuf:"bytes,3,rep,name=module_classes,json=moduleClasses" json:"module_classes,omitempty"`
 	// projects identifies the git project(s) containing the associated source code.
@@ -104,6 +106,13 @@
 	return ""
 }
 
+func (x *LicenseMetadata) GetModuleName() string {
+	if x != nil && x.ModuleName != nil {
+		return *x.ModuleName
+	}
+	return ""
+}
+
 func (x *LicenseMetadata) GetModuleTypes() []string {
 	if x != nil {
 		return x.ModuleTypes
@@ -311,52 +320,54 @@
 	0x0a, 0x16, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
 	0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
 	0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-	0x22, 0x8a, 0x04, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61,
+	0x22, 0xab, 0x04, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61,
 	0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f,
 	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b,
-	0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6d,
-	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f,
-	0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
-	0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65,
-	0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x04, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x23, 0x0a,
-	0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x05,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x69, 0x6e,
-	0x64, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11,
-	0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x78,
-	0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
-	0x65, 0x54, 0x65, 0x78, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e,
-	0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73,
-	0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75, 0x69,
-	0x6c, 0x74, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x12,
-	0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x03,
-	0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a,
-	0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0b, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e,
-	0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x49, 0x6e, 0x73, 0x74,
-	0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d,
-	0x61, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x04,
-	0x64, 0x65, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
-	0x61, 0x74, 0x61, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70,
-	0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x22, 0x50, 0x0a,
-	0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x66,
-	0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
-	0x66, 0x72, 0x6f, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74,
-	0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68, 0x22,
-	0x4b, 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70, 0x65,
-	0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6e,
-	0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
-	0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x31, 0x5a, 0x2f,
-	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6f,
-	0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
-	0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b,
+	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73,
+	0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x04,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x23,
+	0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18,
+	0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x69,
+	0x6e, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x63,
+	0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x11, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x65,
+	0x78, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e,
+	0x73, 0x65, 0x54, 0x65, 0x78, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x63, 0x6f,
+	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69,
+	0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75,
+	0x69, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x74,
+	0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x43,
+	0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0b, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65,
+	0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x49, 0x6e, 0x73,
+	0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
+	0x4d, 0x61, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3f, 0x0a,
+	0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65,
+	0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x22, 0x50,
+	0x0a, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09,
+	0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x08, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e,
+	0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68,
+	0x22, 0x4b, 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70,
+	0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61,
+	0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x31, 0x5a,
+	0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63,
+	0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
+	0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/compliance/license_metadata_proto/license_metadata.proto b/compliance/license_metadata_proto/license_metadata.proto
index 1b4f34f..61f8126 100644
--- a/compliance/license_metadata_proto/license_metadata.proto
+++ b/compliance/license_metadata_proto/license_metadata.proto
@@ -21,6 +21,9 @@
   // package_name identifies the source package. License texts are named relative to the package name.
   optional string package_name = 1;
 
+  // module_name identifies the target
+  optional string module_name = 14;
+
   repeated string module_types = 2;
 
   repeated string module_classes = 3;
@@ -54,6 +57,9 @@
 
   // deps lists the license metadata files depended on.
   repeated AnnotatedDependency deps = 13;
+
+  // next id: 15
+
 }
 
 // InstallMap messages describe the mapping from an input filesystem file to the path to the file
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 7520f58..baad58e 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -296,27 +296,37 @@
 }
 
 func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if p.properties.Src == nil {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-		return
-	}
-	p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
-
-	// Determine the output file basename.
-	// If Filename is set, use the name specified by the property.
-	// If Filename_from_src is set, use the source file name.
-	// Otherwise use the module name.
 	filename := proptools.String(p.properties.Filename)
 	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
-	if filename != "" {
-		if filenameFromSrc {
-			ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
-			return
+	if p.properties.Src != nil {
+		p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+
+		// Determine the output file basename.
+		// If Filename is set, use the name specified by the property.
+		// If Filename_from_src is set, use the source file name.
+		// Otherwise use the module name.
+		if filename != "" {
+			if filenameFromSrc {
+				ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+				return
+			}
+		} else if filenameFromSrc {
+			filename = p.sourceFilePath.Base()
+		} else {
+			filename = ctx.ModuleName()
 		}
-	} else if filenameFromSrc {
-		filename = p.sourceFilePath.Base()
+	} else if ctx.Config().AllowMissingDependencies() {
+		// If no srcs was set and AllowMissingDependencies is enabled then
+		// mark the module as missing dependencies and set a fake source path
+		// and file name.
+		ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
+		p.sourceFilePath = android.PathForModuleSrc(ctx)
+		if filename == "" {
+			filename = ctx.ModuleName()
+		}
 	} else {
-		filename = ctx.ModuleName()
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+		return
 	}
 	p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
 
@@ -678,9 +688,10 @@
 	Filename_from_src bazel.BoolAttribute
 }
 
-// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
-// All prebuilt_* modules are PrebuiltEtc, which we treat uniformily as *PrebuiltFile*
-func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+// Bp2buildHelper returns a bazelPrebuiltFileAttributes used for the conversion
+// of prebuilt_*  modules. bazelPrebuiltFileAttributes has the common attributes
+// used by both prebuilt_etc_xml and other prebuilt_* moodules
+func (module *PrebuiltEtc) Bp2buildHelper(ctx android.TopDownMutatorContext) *bazelPrebuiltFileAttributes {
 	var src bazel.LabelAttribute
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltEtcProperties{}) {
 		for config, p := range configToProps {
@@ -727,10 +738,6 @@
 	}
 
 	var dir = module.installDirBase
-	// prebuilt_file supports only `etc` or `usr/share`
-	if !(dir == "etc" || dir == "usr/share") {
-		return
-	}
 	if subDir := module.subdirProperties.Sub_dir; subDir != nil {
 		dir = dir + "/" + *subDir
 	}
@@ -752,6 +759,22 @@
 		attrs.Filename_from_src = bazel.BoolAttribute{Value: moduleProps.Filename_from_src}
 	}
 
+	return attrs
+
+}
+
+// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
+// prebuilt_* modules (except prebuilt_etc_xml) are PrebuiltEtc,
+// which we treat as *PrebuiltFile*
+func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	var dir = module.installDirBase
+	// prebuilt_file supports only `etc` or `usr/share`
+	if !(dir == "etc" || dir == "usr/share") {
+		return
+	}
+
+	attrs := module.Bp2buildHelper(ctx)
+
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "prebuilt_file",
 		Bzl_load_location: "//build/bazel/rules:prebuilt_file.bzl",
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index cf1f6d7..a6477dd 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -195,6 +195,30 @@
 	}
 }
 
+func TestPrebuiltEtcAllowMissingDependencies(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltEtcTest,
+		android.PrepareForTestDisallowNonExistentPaths,
+		android.FixtureModifyConfig(
+			func(config android.Config) {
+				config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+			}),
+	).RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo.conf",
+			filename_from_src: true,
+			arch: {
+				x86: {
+					src: "x86.conf",
+				},
+			},
+		}
+	`)
+
+	android.AssertStringEquals(t, "expected error rule", "android/soong/android.Error",
+		result.ModuleForTests("foo.conf", "android_arm64_armv8-a").Output("foo.conf").Rule.String())
+}
+
 func TestPrebuiltRootInstallDirPath(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_root {
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index c8cd21b..5e5769b 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -151,6 +151,8 @@
 	// If there's a Java fuzzer with JNI, a different version of Jazzer would
 	// need to be added to the fuzzer package than one without JNI
 	IsJni *bool `json:"is_jni,omitempty"`
+	// List of modules for monitoring coverage drops in directories (e.g. "libicu")
+	Target_modules []string `json:"target_modules,omitempty"`
 }
 
 type FuzzFrameworks struct {
@@ -379,42 +381,3 @@
 	sort.Strings(fuzzTargets)
 	ctx.Strict(targets, strings.Join(fuzzTargets, " "))
 }
-
-// CollectAllSharedDependencies performs a breadth-first search over the provided module's
-// dependencies using `visitDirectDeps` to enumerate all shared library
-// dependencies. We require breadth-first expansion, as otherwise we may
-// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
-// from a dependency. This may cause issues when dependencies have explicit
-// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.SingletonContext, module android.Module, unstrippedOutputFile func(module android.Module) android.Path, isValidSharedDependency func(dependency android.Module) bool) android.Paths {
-	var fringe []android.Module
-
-	seen := make(map[string]bool)
-
-	// Enumerate the first level of dependencies, as we discard all non-library
-	// modules in the BFS loop below.
-	ctx.VisitDirectDeps(module, func(dep android.Module) {
-		if isValidSharedDependency(dep) {
-			fringe = append(fringe, dep)
-		}
-	})
-
-	var sharedLibraries android.Paths
-
-	for i := 0; i < len(fringe); i++ {
-		module := fringe[i]
-		if seen[module.Name()] {
-			continue
-		}
-		seen[module.Name()] = true
-
-		sharedLibraries = append(sharedLibraries, unstrippedOutputFile(module))
-		ctx.VisitDirectDeps(module, func(dep android.Module) {
-			if isValidSharedDependency(dep) && !seen[dep.Name()] {
-				fringe = append(fringe, dep)
-			}
-		})
-	}
-
-	return sharedLibraries
-}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 01279eb..d4ed363 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -909,13 +909,7 @@
 			cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
 			cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
 		}
-
-		genDir := "$(RULEDIR)"
-		if ctx.ModuleType() == "gensrcs" {
-			genDir = "$(GENDIR)"
-		}
-
-		cmd = strings.Replace(cmd, "$(genDir)", genDir, -1)
+		cmd = strings.Replace(cmd, "$(genDir)", "$(RULEDIR)", -1)
 		if len(tools.Value.Includes) > 0 {
 			cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
 			cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index cd941cc..63f8fa9 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -790,6 +790,94 @@
 		result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
 }
 
+func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external-protos/path/Android.bp": []byte(`
+				filegroup {
+					name: "external-protos",
+					srcs: ["baz/baz.proto", "bar.proto"],
+				}
+			`),
+			"package-dir/Android.bp": []byte(`
+				gensrcs {
+					name: "module-name",
+					cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+					srcs: [
+						"src/foo.proto",
+						":external-protos",
+					],
+					output_extension: "proto.h",
+				}
+			`),
+		}),
+	).RunTest(t)
+
+	exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
+	gen := result.Module("module-name", "").(*Module)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"include path",
+		[]string{exportedIncludeDir},
+		gen.exportedIncludeDirs,
+	)
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"files",
+		[]string{
+			exportedIncludeDir + "/package-dir/src/foo.proto.h",
+			exportedIncludeDir + "/external-protos/path/baz/baz.proto.h",
+			exportedIncludeDir + "/external-protos/path/bar.proto.h",
+		},
+		gen.outputFiles,
+	)
+}
+
+func TestGenSrcsWithSrcsFromExternalPackage(t *testing.T) {
+	bp := `
+		gensrcs {
+			name: "module-name",
+			cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+			srcs: [
+				":external-protos",
+			],
+			output_extension: "proto.h",
+		}
+	`
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external-protos/path/Android.bp": []byte(`
+				filegroup {
+					name: "external-protos",
+					srcs: ["foo/foo.proto", "bar.proto"],
+				}
+			`),
+		}),
+	).RunTestWithBp(t, bp)
+
+	exportedIncludeDir := "out/soong/.intermediates/module-name/gen/gensrcs"
+	gen := result.Module("module-name", "").(*Module)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"include path",
+		[]string{exportedIncludeDir},
+		gen.exportedIncludeDirs,
+	)
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"files",
+		[]string{
+			exportedIncludeDir + "/external-protos/path/foo/foo.proto.h",
+			exportedIncludeDir + "/external-protos/path/bar.proto.h",
+		},
+		gen.outputFiles,
+	)
+}
+
 func TestPrebuiltTool(t *testing.T) {
 	testcases := []struct {
 		name             string
diff --git a/java/Android.bp b/java/Android.bp
index 9df4ab4..0bf7a0b 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -43,6 +43,7 @@
         "dexpreopt_bootjars.go",
         "dexpreopt_check.go",
         "dexpreopt_config.go",
+        "dexpreopt_config_testing.go",
         "droiddoc.go",
         "droidstubs.go",
         "fuzz.go",
@@ -64,6 +65,7 @@
         "plugin.go",
         "prebuilt_apis.go",
         "proto.go",
+        "resourceshrinker.go",
         "robolectric.go",
         "rro.go",
         "sdk.go",
@@ -86,6 +88,7 @@
         "dex_test.go",
         "dexpreopt_test.go",
         "dexpreopt_bootjars_test.go",
+        "dexpreopt_config_test.go",
         "droiddoc_test.go",
         "droidstubs_test.go",
         "genrule_test.go",
diff --git a/java/aapt2.go b/java/aapt2.go
index 5346ddf..7845a0b 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -256,17 +256,21 @@
 
 var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert",
 	blueprint.RuleParams{
-		Command:     `${config.Aapt2Cmd} convert --output-format proto $in -o $out`,
+		Command:     `${config.Aapt2Cmd} convert --output-format $format $in -o $out`,
 		CommandDeps: []string{"${config.Aapt2Cmd}"},
-	})
+	}, "format",
+)
 
 // Converts xml files and resource tables (resources.arsc) in the given jar/apk file to a proto
 // format. The proto definition is available at frameworks/base/tools/aapt2/Resources.proto.
-func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) {
+func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path, format string) {
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        aapt2ConvertRule,
 		Input:       in,
 		Output:      out,
-		Description: "convert to proto",
+		Description: "convert to " + format,
+		Args: map[string]string{
+			"format": format,
+		},
 	})
 }
diff --git a/java/aar.go b/java/aar.go
index d5996ba..6261f29 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -270,7 +270,7 @@
 
 func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext,
 	classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string,
-	extraLinkFlags ...string) {
+	enforceDefaultTargetSdkVersion bool, extraLinkFlags ...string) {
 
 	transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
 		aaptLibs(ctx, sdkContext, classLoaderContexts)
@@ -283,15 +283,16 @@
 	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
 
 	manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
-		SdkContext:             sdkContext,
-		ClassLoaderContexts:    classLoaderContexts,
-		IsLibrary:              a.isLibrary,
-		DefaultManifestVersion: a.defaultManifestVersion,
-		UseEmbeddedNativeLibs:  a.useEmbeddedNativeLibs,
-		UsesNonSdkApis:         a.usesNonSdkApis,
-		UseEmbeddedDex:         a.useEmbeddedDex,
-		HasNoCode:              a.hasNoCode,
-		LoggingParent:          a.LoggingParent,
+		SdkContext:                     sdkContext,
+		ClassLoaderContexts:            classLoaderContexts,
+		IsLibrary:                      a.isLibrary,
+		DefaultManifestVersion:         a.defaultManifestVersion,
+		UseEmbeddedNativeLibs:          a.useEmbeddedNativeLibs,
+		UsesNonSdkApis:                 a.usesNonSdkApis,
+		UseEmbeddedDex:                 a.useEmbeddedDex,
+		HasNoCode:                      a.hasNoCode,
+		LoggingParent:                  a.LoggingParent,
+		EnforceDefaultTargetSdkVersion: enforceDefaultTargetSdkVersion,
 	})
 
 	// Add additional manifest files to transitive manifests.
@@ -439,7 +440,7 @@
 		switch depTag {
 		case instrumentationForTag:
 			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
-		case libTag:
+		case sdkLibTag, libTag:
 			if exportPackage != nil {
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
@@ -535,7 +536,7 @@
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
 	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil)
+	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil, false)
 
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
 
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 522b664..c785310 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -43,13 +43,12 @@
 // targetSdkVersion for manifest_fixer
 // 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, sdkContext android.SdkContext) string {
-	targetSdkVersionSpec := sdkContext.TargetSdkVersion(ctx)
-	// Return 10000 for modules targeting "current" if either
-	// 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
+func targetSdkVersionForManifestFixer(ctx android.ModuleContext, params ManifestFixerParams) string {
+	targetSdkVersionSpec := params.SdkContext.TargetSdkVersion(ctx)
+
+	// Check if we want to return 10000
 	// TODO(b/240294501): Determine the rules for handling test apexes
-	if targetSdkVersionSpec.ApiLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module())) {
+	if shouldReturnFinalOrFutureInt(ctx, targetSdkVersionSpec, params.EnforceDefaultTargetSdkVersion) {
 		return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt())
 	}
 	targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx)
@@ -59,6 +58,17 @@
 	return targetSdkVersion
 }
 
+// Return true for modules targeting "current" if either
+// 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 {
+	if enforceDefaultTargetSdkVersion && ctx.Config().PlatformSdkFinal() {
+		return false
+	}
+	return targetSdkVersionSpec.ApiLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
+}
+
 // Helper function that casts android.Module to java.androidTestApp
 // If this type conversion is possible, it queries whether the test app is included in an MTS suite
 func includedInMts(module android.Module) bool {
@@ -69,16 +79,17 @@
 }
 
 type ManifestFixerParams struct {
-	SdkContext             android.SdkContext
-	ClassLoaderContexts    dexpreopt.ClassLoaderContextMap
-	IsLibrary              bool
-	DefaultManifestVersion string
-	UseEmbeddedNativeLibs  bool
-	UsesNonSdkApis         bool
-	UseEmbeddedDex         bool
-	HasNoCode              bool
-	TestOnly               bool
-	LoggingParent          string
+	SdkContext                     android.SdkContext
+	ClassLoaderContexts            dexpreopt.ClassLoaderContextMap
+	IsLibrary                      bool
+	DefaultManifestVersion         string
+	UseEmbeddedNativeLibs          bool
+	UsesNonSdkApis                 bool
+	UseEmbeddedDex                 bool
+	HasNoCode                      bool
+	TestOnly                       bool
+	LoggingParent                  string
+	EnforceDefaultTargetSdkVersion bool
 }
 
 // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
@@ -137,7 +148,7 @@
 	var argsMapper = make(map[string]string)
 
 	if params.SdkContext != nil {
-		targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params.SdkContext)
+		targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
 		args = append(args, "--targetSdkVersion ", targetSdkVersion)
 
 		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
diff --git a/java/androidmk.go b/java/androidmk.go
index 75ac0e7..42b4ef1 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"strings"
 
 	"android/soong/android"
 )
@@ -138,6 +139,7 @@
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "null-suite")
 		androidMkWriteTestData(j.jniFilePaths, entries)
+		androidMkWriteTestData(android.Paths{j.implementationJarFile}, entries)
 	})
 	return entriesList
 }
@@ -398,6 +400,19 @@
 				} else {
 					for _, jniLib := range app.jniLibs {
 						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+						var partitionTag string
+
+						// Mimic the creation of partition_tag in build/make,
+						// which defaults to an empty string when the partition is system.
+						// Otherwise, capitalize with a leading _
+						if jniLib.partition == "system" {
+							partitionTag = ""
+						} else {
+							split := strings.Split(jniLib.partition, "/")
+							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
+						}
+						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
+							jniLib.name+":"+partitionTag)
 					}
 				}
 
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 197da4f..1232cd1 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -19,6 +19,9 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func TestRequired(t *testing.T) {
@@ -252,3 +255,149 @@
 		android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
 	}
 }
+
+func TestJniPartition(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libjni_system",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libjni_system_ext",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			system_ext_specific: true,
+		}
+
+		cc_library {
+			name: "libjni_odm",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			device_specific: true,
+		}
+
+		cc_library {
+			name: "libjni_product",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			product_specific: true,
+		}
+
+		cc_library {
+			name: "libjni_vendor",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			soc_specific: true,
+		}
+
+		android_app {
+			name: "test_app_system_jni_system",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system"],
+		}
+
+		android_app {
+			name: "test_app_system_jni_system_ext",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system_ext"],
+		}
+
+		android_app {
+			name: "test_app_system_ext_jni_system",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system"],
+			system_ext_specific: true
+		}
+
+		android_app {
+			name: "test_app_system_ext_jni_system_ext",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_system_ext"],
+			system_ext_specific: true
+		}
+
+		android_app {
+			name: "test_app_product_jni_product",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_product"],
+			product_specific: true
+		}
+
+		android_app {
+			name: "test_app_vendor_jni_odm",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_odm"],
+			soc_specific: true
+		}
+
+		android_app {
+			name: "test_app_odm_jni_vendor",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_vendor"],
+			device_specific: true
+		}
+		android_app {
+			name: "test_app_system_jni_multiple",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system", "libjni_system_ext"],
+		}
+		android_app {
+			name: "test_app_vendor_jni_multiple",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_odm", "libjni_vendor"],
+			soc_specific: true
+		}
+		`
+	arch := "arm64"
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		cc.PrepareForTestWithCcDefaultModules,
+		android.PrepareForTestWithAndroidMk,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
+		}),
+	).
+		RunTestWithBp(t, bp)
+	testCases := []struct {
+		name           string
+		partitionNames []string
+		partitionTags  []string
+	}{
+		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
+		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
+		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
+		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
+		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
+		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
+		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
+		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
+		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			mod := ctx.ModuleForTests(test.name, "android_common").Module()
+			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
+			for i := range test.partitionNames {
+				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
+				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
+				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
+			}
+		})
+	}
+}
diff --git a/java/app.go b/java/app.go
index 7f37ff3..bbd9d2d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -101,6 +101,15 @@
 	PreventInstall    bool `blueprint:"mutated"`
 	IsCoverageVariant bool `blueprint:"mutated"`
 
+	// It can be set to test the behaviour of default target sdk version.
+	// Only required when updatable: false. It is an error if updatable: true and this is false.
+	Enforce_default_target_sdk_version *bool
+
+	// If set, the targetSdkVersion for the target is set to the latest default API level.
+	// This would be by default false, unless updatable: true or
+	// enforce_default_target_sdk_version: true in which case this defaults to true.
+	EnforceDefaultTargetSdkVersion bool `blueprint:"mutated"`
+
 	// Whether this app is considered mainline updatable or not. When set to true, this will enforce
 	// additional rules to make sure an app can safely be updated. Default is false.
 	// Prefer using other specific properties if build behaviour must be changed; avoid using this
@@ -296,6 +305,18 @@
 		} else {
 			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
 		}
+
+		if !BoolDefault(a.appProperties.Enforce_default_target_sdk_version, true) {
+			ctx.PropertyErrorf("enforce_default_target_sdk_version", "Updatable apps must enforce default target sdk version")
+		}
+		// TODO(b/227460469) after all the modules removes the target sdk version, throw an error if the target sdk version is explicitly set.
+		if a.deviceProperties.Target_sdk_version == nil {
+			a.SetEnforceDefaultTargetSdkVersion(true)
+		}
+	}
+
+	if Bool(a.appProperties.Enforce_default_target_sdk_version) {
+		a.SetEnforceDefaultTargetSdkVersion(true)
 	}
 
 	a.checkPlatformAPI(ctx)
@@ -427,7 +448,7 @@
 		a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion
 	}
 	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts,
-		a.usesLibraryProperties.Exclude_uses_libs, aaptLinkFlags...)
+		a.usesLibraryProperties.Exclude_uses_libs, a.enforceDefaultTargetSdkVersion(), aaptLinkFlags...)
 
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
@@ -668,10 +689,9 @@
 	if lineage := String(a.overridableAppProperties.Lineage); lineage != "" {
 		lineageFile = android.PathForModuleSrc(ctx, lineage)
 	}
-
 	rotationMinSdkVersion := String(a.overridableAppProperties.RotationMinSdkVersion)
 
-	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
+	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, Bool(a.dexProperties.Optimize.Shrink_resources))
 	a.outputFile = packageFile
 	if v4SigningRequested {
 		a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -700,7 +720,7 @@
 		if v4SigningRequested {
 			v4SignatureFile = android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk.idsig")
 		}
-		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
+		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, false)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 		if v4SigningRequested {
 			a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -778,6 +798,7 @@
 						target:         module.Target(),
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
+						partition:      dep.Partition(),
 					})
 				} else {
 					ctx.ModuleErrorf("dependency %q missing output file", otherName)
@@ -866,6 +887,14 @@
 	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depsInfo)
 }
 
+func (a *AndroidApp) enforceDefaultTargetSdkVersion() bool {
+	return a.appProperties.EnforceDefaultTargetSdkVersion
+}
+
+func (a *AndroidApp) SetEnforceDefaultTargetSdkVersion(val bool) {
+	a.appProperties.EnforceDefaultTargetSdkVersion = val
+}
+
 func (a *AndroidApp) Updatable() bool {
 	return Bool(a.appProperties.Updatable)
 }
@@ -1461,6 +1490,20 @@
 	Certificate_name *string
 }
 
+// ParseCertificateToAttribute splits the certificate prop into a certificate
+// label attribute or a certificate_name string attribute.
+func ParseCertificateToAttribute(ctx android.TopDownMutatorContext, certificate *string) (*string, *bazel.Label) {
+	var certificateLabel *bazel.Label
+	certificateName := proptools.StringDefault(certificate, "")
+	certModule := android.SrcIsModule(certificateName)
+	if certModule != "" {
+		c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
+		certificateLabel = &c
+		certificate = nil
+	}
+	return certificate, certificateLabel
+}
+
 // ConvertWithBp2build is used to convert android_app to Bazel.
 func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
@@ -1470,15 +1513,7 @@
 
 	aapt := a.convertAaptAttrsWithBp2Build(ctx)
 
-	var certificate *bazel.Label
-	certificateNamePtr := a.overridableAppProperties.Certificate
-	certificateName := proptools.StringDefault(certificateNamePtr, "")
-	certModule := android.SrcIsModule(certificateName)
-	if certModule != "" {
-		c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
-		certificate = &c
-		certificateNamePtr = nil
-	}
+	certificateName, certificate := ParseCertificateToAttribute(ctx, a.overridableAppProperties.Certificate)
 	attrs := &bazelAndroidAppAttributes{
 		commonAttrs,
 		aapt,
@@ -1486,7 +1521,7 @@
 		// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
 		a.overridableAppProperties.Package_name,
 		certificate,
-		certificateNamePtr,
+		certificateName,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/java/app_builder.go b/java/app_builder.go
index 18a9751..d20a6bf 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -52,7 +52,7 @@
 	})
 
 func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
-	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) {
+	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string, shrinkResources bool) {
 
 	unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
 	unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
@@ -65,7 +65,6 @@
 	if jniJarFile != nil {
 		inputs = append(inputs, jniJarFile)
 	}
-
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      combineApk,
 		Inputs:    inputs,
@@ -73,6 +72,11 @@
 		Implicits: deps,
 	})
 
+	if shrinkResources {
+		shrunkenApk := android.PathForModuleOut(ctx, "resource-shrunken", unsignedApk.Base())
+		ShrinkResources(ctx, unsignedApk, shrunkenApk)
+		unsignedApk = shrunkenApk
+	}
 	SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 }
 
@@ -84,7 +88,6 @@
 		certificateArgs = append(certificateArgs, c.Pem.String(), c.Key.String())
 		deps = append(deps, c.Pem, c.Key)
 	}
-
 	outputFiles := android.WritablePaths{signedApk}
 	var flags []string
 	if v4SignatureFile != nil {
@@ -182,7 +185,7 @@
 	packageFile, jniJarFile, dexJarFile android.Path) {
 
 	protoResJarFile := android.PathForModuleOut(ctx, "package-res.pb.apk")
-	aapt2Convert(ctx, protoResJarFile, packageFile)
+	aapt2Convert(ctx, protoResJarFile, packageFile, "proto")
 
 	var zips android.Paths
 
diff --git a/java/app_set.go b/java/app_set.go
index 694b167..d99fadb 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -90,10 +90,11 @@
 }
 
 var TargetCpuAbi = map[string]string{
-	"arm":    "ARMEABI_V7A",
-	"arm64":  "ARM64_V8A",
-	"x86":    "X86",
-	"x86_64": "X86_64",
+	"arm":     "ARMEABI_V7A",
+	"arm64":   "ARM64_V8A",
+	"riscv64": "RISCV64",
+	"x86":     "X86",
+	"x86_64":  "X86_64",
 }
 
 func SupportedAbis(ctx android.ModuleContext) []string {
diff --git a/java/app_test.go b/java/app_test.go
index 23635b9..e216c63 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -3057,6 +3057,179 @@
 	}
 }
 
+func TestDefaultAppTargetSdkVersionForUpdatableModules(t *testing.T) {
+	platform_sdk_codename := "Tiramisu"
+	platform_sdk_version := 33
+	testCases := []struct {
+		name                     string
+		platform_sdk_final       bool
+		targetSdkVersionInBp     *string
+		targetSdkVersionExpected *string
+		updatable                bool
+	}{
+		{
+			name:                     "Non-Updatable Module: Android.bp has older targetSdkVersion",
+			targetSdkVersionInBp:     proptools.StringPtr("29"),
+			targetSdkVersionExpected: proptools.StringPtr("29"),
+			updatable:                false,
+		},
+		{
+			name:                     "Updatable Module: Android.bp has older targetSdkVersion",
+			targetSdkVersionInBp:     proptools.StringPtr("30"),
+			targetSdkVersionExpected: proptools.StringPtr("30"),
+			updatable:                true,
+		},
+		{
+			name:                     "Updatable Module: Android.bp has no targetSdkVersion",
+			targetSdkVersionExpected: proptools.StringPtr("10000"),
+			updatable:                true,
+		},
+		{
+			name:                     "[SDK finalised] Non-Updatable Module: Android.bp has older targetSdkVersion",
+			platform_sdk_final:       true,
+			targetSdkVersionInBp:     proptools.StringPtr("30"),
+			targetSdkVersionExpected: proptools.StringPtr("30"),
+			updatable:                false,
+		},
+		{
+			name:                     "[SDK finalised] Updatable Module: Android.bp has older targetSdkVersion",
+			platform_sdk_final:       true,
+			targetSdkVersionInBp:     proptools.StringPtr("30"),
+			targetSdkVersionExpected: proptools.StringPtr("30"),
+			updatable:                true,
+		},
+		{
+			name:                     "[SDK finalised] Updatable Module: Android.bp has targetSdkVersion as platform sdk codename",
+			platform_sdk_final:       true,
+			targetSdkVersionInBp:     proptools.StringPtr(platform_sdk_codename),
+			targetSdkVersionExpected: proptools.StringPtr("33"),
+			updatable:                true,
+		},
+		{
+			name:                     "[SDK finalised] Updatable Module: Android.bp has no targetSdkVersion",
+			platform_sdk_final:       true,
+			targetSdkVersionExpected: proptools.StringPtr("33"),
+			updatable:                true,
+		},
+	}
+	for _, testCase := range testCases {
+		bp := fmt.Sprintf(`
+			android_app {
+				name: "foo",
+				sdk_version: "current",
+				min_sdk_version: "29",
+				target_sdk_version: "%v",
+				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
+
+		fixture := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.PrepareForTestWithAllowMissingDependencies,
+			android.PrepareForTestWithAndroidMk,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				// explicitly set following platform variables to make the test deterministic
+				variables.Platform_sdk_final = &testCase.platform_sdk_final
+				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_apps = []string{"sampleModule"}
+			}),
+		)
+
+		result := fixture.RunTestWithBp(t, bp)
+		foo := result.ModuleForTests("foo", "android_common")
+
+		manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+*testCase.targetSdkVersionExpected)
+	}
+}
+
+func TestEnforceDefaultAppTargetSdkVersionFlag(t *testing.T) {
+	platform_sdk_codename := "Tiramisu"
+	platform_sdk_version := 33
+	testCases := []struct {
+		name                           string
+		enforceDefaultTargetSdkVersion bool
+		expectedError                  string
+		platform_sdk_final             bool
+		targetSdkVersionInBp           string
+		targetSdkVersionExpected       string
+		updatable                      bool
+	}{
+		{
+			name:                           "Not enforcing Target SDK Version: Android.bp has older targetSdkVersion",
+			enforceDefaultTargetSdkVersion: false,
+			targetSdkVersionInBp:           "29",
+			targetSdkVersionExpected:       "29",
+			updatable:                      false,
+		},
+		{
+			name:                           "[SDK finalised] Enforce Target SDK Version: Android.bp has current targetSdkVersion",
+			enforceDefaultTargetSdkVersion: true,
+			platform_sdk_final:             true,
+			targetSdkVersionInBp:           "current",
+			targetSdkVersionExpected:       "33",
+			updatable:                      true,
+		},
+		{
+			name:                           "[SDK finalised] Enforce Target SDK Version: Android.bp has current targetSdkVersion",
+			enforceDefaultTargetSdkVersion: true,
+			platform_sdk_final:             false,
+			targetSdkVersionInBp:           "current",
+			targetSdkVersionExpected:       "10000",
+			updatable:                      false,
+		},
+		{
+			name:                           "Not enforcing Target SDK Version for Updatable app",
+			enforceDefaultTargetSdkVersion: false,
+			expectedError:                  "Updatable apps must enforce default target sdk version",
+			targetSdkVersionInBp:           "29",
+			targetSdkVersionExpected:       "29",
+			updatable:                      true,
+		},
+	}
+	for _, testCase := range testCases {
+		errExpected := testCase.expectedError != ""
+		bp := fmt.Sprintf(`
+			android_app {
+				name: "foo",
+				enforce_default_target_sdk_version: %t,
+				sdk_version: "current",
+				min_sdk_version: "29",
+				target_sdk_version: "%v",
+				updatable: %t
+			}
+			`, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp, testCase.updatable)
+
+		fixture := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.PrepareForTestWithAllowMissingDependencies,
+			android.PrepareForTestWithAndroidMk,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				// explicitly set following platform variables to make the test deterministic
+				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_apps = []string{"sampleModule"}
+			}),
+		)
+
+		errorHandler := android.FixtureExpectsNoErrors
+		if errExpected {
+			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
+		}
+		result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
+
+		if !errExpected {
+			foo := result.ModuleForTests("foo", "android_common")
+			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		}
+	}
+}
+
 func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
diff --git a/java/base.go b/java/base.go
index 7f6132a..96f36e8 100644
--- a/java/base.go
+++ b/java/base.go
@@ -447,9 +447,11 @@
 	// installed file for hostdex copy
 	hostdexInstallFile android.InstallPath
 
-	// list of .java files and srcjars that was passed to javac
-	compiledJavaSrcs android.Paths
-	compiledSrcJars  android.Paths
+	// list of unique .java and .kt source files
+	uniqueSrcFiles android.Paths
+
+	// list of srcjars that was passed to javac
+	compiledSrcJars android.Paths
 
 	// manifest file to use instead of properties.Manifest
 	overrideManifest android.OptionalPath
@@ -530,7 +532,7 @@
 		// TODO(satayev): cover other types as well, e.g. imports
 		case *Library, *AndroidLibrary:
 			switch tag {
-			case bootClasspathTag, libTag, staticLibTag, java9LibTag:
+			case bootClasspathTag, sdkLibTag, libTag, staticLibTag, java9LibTag:
 				j.checkSdkLinkType(ctx, module.(moduleWithSdkDep), tag.(dependencyTag))
 			}
 		}
@@ -893,7 +895,7 @@
 
 	epEnabled := j.properties.Errorprone.Enabled
 	if (ctx.Config().RunErrorProne() && epEnabled == nil) || Bool(epEnabled) {
-		if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
+		if config.ErrorProneClasspath == nil && !ctx.Config().RunningInsideUnitTest() {
 			ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
 		}
 
@@ -1078,15 +1080,26 @@
 
 	jarName := ctx.ModuleName() + ".jar"
 
-	javaSrcFiles := srcFiles.FilterByExt(".java")
-	var uniqueSrcFiles android.Paths
+	var uniqueJavaFiles android.Paths
 	set := make(map[string]bool)
-	for _, v := range javaSrcFiles {
+	for _, v := range srcFiles.FilterByExt(".java") {
 		if _, found := set[v.String()]; !found {
 			set[v.String()] = true
-			uniqueSrcFiles = append(uniqueSrcFiles, v)
+			uniqueJavaFiles = append(uniqueJavaFiles, v)
 		}
 	}
+	var uniqueKtFiles android.Paths
+	for _, v := range srcFiles.FilterByExt(".kt") {
+		if _, found := set[v.String()]; !found {
+			set[v.String()] = true
+			uniqueKtFiles = append(uniqueKtFiles, v)
+		}
+	}
+
+	var uniqueSrcFiles android.Paths
+	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
+	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
+	j.uniqueSrcFiles = uniqueSrcFiles
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
@@ -1094,7 +1107,7 @@
 	//  is used to run all of the annotation processors.
 	disableTurbine := deps.disableTurbine
 
-	// Collect .java files for AIDEGen
+	// Collect .java and .kt files for AIDEGen
 	j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
 
 	var kotlinJars android.Paths
@@ -1132,12 +1145,7 @@
 			flags.kotlincFlags += "$kotlincFlags"
 		}
 
-		var kotlinSrcFiles android.Paths
-		kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
-		kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
-
-		// Collect .kt files for AIDEGen
-		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...)
+		// Collect common .kt files for AIDEGen
 		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
 
 		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
@@ -1150,7 +1158,7 @@
 			// Use kapt for annotation processing
 			kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
 			kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
-			kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+			kotlinKapt(ctx, kaptSrcJar, kaptResJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 			srcJars = append(srcJars, kaptSrcJar)
 			kotlinJars = append(kotlinJars, kaptResJar)
 			// Disable annotation processing in javac, it's already been handled by kapt
@@ -1160,7 +1168,7 @@
 
 		kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
-		kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+		kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
 			return
 		}
@@ -1185,8 +1193,6 @@
 
 	jars := append(android.Paths(nil), kotlinJars...)
 
-	// Store the list of .java files that was passed to javac
-	j.compiledJavaSrcs = uniqueSrcFiles
 	j.compiledSrcJars = srcJars
 
 	enableSharding := false
@@ -1201,12 +1207,12 @@
 			// with sharding enabled. See: b/77284273.
 		}
 		headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
-			j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
+			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
 		if ctx.Failed() {
 			return
 		}
 	}
-	if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
+	if len(uniqueJavaFiles) > 0 || len(srcJars) > 0 {
 		hasErrorproneableFiles := false
 		for _, ext := range j.sourceExtensions {
 			if ext != ".proto" && ext != ".aidl" {
@@ -1231,7 +1237,7 @@
 			errorproneFlags := enableErrorproneFlags(flags)
 			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
 
-			transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil,
+			transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneFlags, nil,
 				"errorprone", "errorprone")
 
 			extraJarDeps = append(extraJarDeps, errorprone)
@@ -1243,8 +1249,8 @@
 			}
 			shardSize := int(*(j.properties.Javac_shard_size))
 			var shardSrcs []android.Paths
-			if len(uniqueSrcFiles) > 0 {
-				shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
+			if len(uniqueJavaFiles) > 0 {
+				shardSrcs = android.ShardPaths(uniqueJavaFiles, shardSize)
 				for idx, shardSrc := range shardSrcs {
 					classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
 						nil, flags, extraJarDeps)
@@ -1257,7 +1263,7 @@
 				jars = append(jars, classes)
 			}
 		} else {
-			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
+			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueJavaFiles, srcJars, flags, extraJarDeps)
 			jars = append(jars, classes)
 		}
 		if ctx.Failed() {
@@ -1955,7 +1961,7 @@
 
 		if dep, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
-			case libTag:
+			case sdkLibTag, libTag:
 				depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
 				deps.classpath = append(deps.classpath, depHeaderJars...)
 				deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...)
@@ -1975,7 +1981,7 @@
 			switch tag {
 			case bootClasspathTag:
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
-			case libTag, instrumentationForTag:
+			case sdkLibTag, libTag, instrumentationForTag:
 				if _, ok := module.(*Plugin); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a libs dependency", otherName)
 				}
@@ -2048,7 +2054,7 @@
 			}
 		} else if dep, ok := module.(android.SourceFileProducer); ok {
 			switch tag {
-			case libTag:
+			case sdkLibTag, libTag:
 				checkProducesJars(ctx, dep)
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
 				deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f5b5f99..3a28c59 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -264,7 +264,7 @@
 	//
 	// If it could not create the files then it will return nil. Otherwise, it will return a map from
 	// android.ArchType to the predefined paths of the boot image files.
-	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch
+	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
@@ -583,23 +583,24 @@
 		// Perform hidden API processing.
 		hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
 
-		var bootImageFilesByArch bootImageFilesByArch
+		var bootImageFiles bootImageOutputs
 		if imageConfig != nil {
 			// Delegate the production of the boot image files to a module type specific method.
 			common := ctx.Module().(commonBootclasspathFragment)
-			bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig)
+			bootImageFiles = common.produceBootImageFiles(ctx, imageConfig)
 
 			if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
 				// Zip the boot image files up, if available. This will generate the zip file in a
 				// predefined location.
-				buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFilesByArch)
+				buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFiles.byArch)
 
 				// Copy the dex jars of this fragment's content modules to their predefined locations.
 				copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
 			}
 
-			for _, variant := range imageConfig.apexVariants() {
-				arch := variant.target.Arch.ArchType.String()
+			for _, variant := range bootImageFiles.variants {
+				archType := variant.config.target.Arch.ArchType
+				arch := archType.String()
 				for _, install := range variant.deviceInstalls {
 					// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
 					installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
@@ -620,7 +621,7 @@
 		// A prebuilt fragment cannot contribute to an apex.
 		if !android.IsModulePrebuilt(ctx.Module()) {
 			// Provide the apex content info.
-			b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
+			b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFiles)
 		}
 	} else {
 		// Versioned fragments are not needed by make.
@@ -663,7 +664,7 @@
 
 // provideApexContentInfo creates, initializes and stores the apex content info for use by other
 // modules.
-func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFilesByArch bootImageFilesByArch) {
+func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFiles bootImageOutputs) {
 	// Construct the apex content info from the config.
 	info := BootclasspathFragmentApexContentInfo{
 		// Populate the apex content info with paths to the dex jars.
@@ -674,14 +675,14 @@
 		info.modules = imageConfig.modules
 		global := dexpreopt.GetGlobalConfig(ctx)
 		if !global.DisableGenerateProfile {
-			info.profilePathOnHost = imageConfig.profilePathOnHost
+			info.profilePathOnHost = bootImageFiles.profile
 			info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
 		}
 
 		info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
 	}
 
-	info.bootImageFilesByArch = bootImageFilesByArch
+	info.bootImageFilesByArch = bootImageFiles.byArch
 
 	// Make the apex content info available for other modules.
 	ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
@@ -934,9 +935,9 @@
 }
 
 // produceBootImageFiles builds the boot image files from the source if it is required.
-func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
 	if SkipDexpreoptBootJars(ctx) {
-		return nil
+		return bootImageOutputs{}
 	}
 
 	// Only generate the boot image if the configuration does not skip it.
@@ -948,21 +949,21 @@
 //
 // If it could not create the files then it will return nil. Otherwise, it will return a map from
 // android.ArchType to the predefined paths of the boot image files.
-func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
+func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
 	global := dexpreopt.GetGlobalConfig(ctx)
 	if !shouldBuildBootImages(ctx.Config(), global) {
-		return nil
+		return bootImageOutputs{}
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce a boot image.
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if apexInfo.IsForPlatform() {
-		return nil
+		return bootImageOutputs{}
 	}
 
 	// Bootclasspath fragment modules that are versioned do not produce a boot image.
 	if android.IsModuleInVersionedSdk(ctx.Module()) {
-		return nil
+		return bootImageOutputs{}
 	}
 
 	// Build a profile for the image config and then use that to build the boot image.
@@ -972,11 +973,11 @@
 	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
 
 	// Build boot image files for the android variants.
-	androidBootImageFilesByArch := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+	bootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
 
 	// Return the boot image files for the android variants for inclusion in an APEX and to be zipped
 	// up for the dist.
-	return androidBootImageFilesByArch
+	return bootImageFiles
 }
 
 func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1277,14 +1278,14 @@
 }
 
 // produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
+func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
 	if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
-		return nil
+		return bootImageOutputs{}
 	}
 
 	di := android.FindDeapexerProviderForModule(ctx)
 	if di == nil {
-		return nil // An error has been reported by FindDeapexerProviderForModule.
+		return bootImageOutputs{} // An error has been reported by FindDeapexerProviderForModule.
 	}
 
 	profile := (android.WritablePath)(nil)
@@ -1302,8 +1303,17 @@
 		// If the boot image files for the android variants are in the prebuilt apex, we must use those
 		// rather than building new ones because those boot image files are going to be used on device.
 		files := bootImageFilesByArch{}
+		bootImageFiles := bootImageOutputs{
+			byArch:  files,
+			profile: profile,
+		}
 		for _, variant := range imageConfig.apexVariants() {
 			arch := variant.target.Arch.ArchType
+			bootImageFiles.variants = append(bootImageFiles.variants, bootImageVariantOutputs{
+				variant,
+				// No device installs needed when installed in APEX.
+				nil,
+			})
 			for _, toPath := range variant.imagesDeps {
 				apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
 				// Get the path to the file that the deapexer extracted from the prebuilt apex file.
@@ -1321,11 +1331,11 @@
 				})
 			}
 		}
-		return files
+		return bootImageFiles
 	} else {
 		if profile == nil {
 			ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
-			return nil
+			return bootImageOutputs{}
 		}
 		// Build boot image files for the android variants from the dex files provided by the contents
 		// of this module.
diff --git a/java/config/config.go b/java/config/config.go
index 03288fe..fab6796 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -83,7 +83,7 @@
 
 	// ErrorProne can use significantly more memory than javac alone, give it a higher heap
 	// size (b/221480398).
-	exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M")
+	exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "8192M")
 	exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
 
 	// D8 invocations are shorter lived, so we restrict their JIT tiering relative to R8.
@@ -132,12 +132,7 @@
 		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
 			return override
 		}
-		switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
-		case "true":
-			return "17"
-		default:
-			return "11"
-		}
+		return "17"
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
@@ -165,6 +160,7 @@
 	pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
 	pctx.HostBinToolVariable("D8Cmd", "d8")
 	pctx.HostBinToolVariable("R8Cmd", "r8")
+	pctx.HostBinToolVariable("ResourceShrinkerCmd", "resourceshrinker")
 	pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
diff --git a/java/dex.go b/java/dex.go
index 2b78703..de36b18 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -63,6 +63,8 @@
 		// classes referenced by the app manifest.  Defaults to false.
 		No_aapt_flags *bool
 
+		Shrink_resources *bool
+
 		// Flags to pass to proguard.
 		Proguard_flags []string
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 4e416fc..b3faae8 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -228,6 +228,11 @@
 }
 
 // Target-independent description of a boot image.
+//
+// WARNING: All fields in this struct should be initialized in the genBootImageConfigs function.
+// Failure to do so can lead to data races if there is no synchronization enforced ordering between
+// the writer and the reader. Fields which break this rule are marked as deprecated and should be
+// removed and replaced with something else, e.g. providers.
 type bootImageConfig struct {
 	// If this image is an extension, the image that it extends.
 	extends *bootImageConfig
@@ -268,14 +273,15 @@
 	zip android.WritablePath
 
 	// Rules which should be used in make to install the outputs.
+	//
+	// Deprecated: Not initialized correctly, see struct comment.
 	profileInstalls android.RuleBuilderInstalls
 
 	// Path to the license metadata file for the module that built the profile.
+	//
+	// Deprecated: Not initialized correctly, see struct comment.
 	profileLicenseMetadataFile android.OptionalPath
 
-	// Path to the image profile file on host (or empty, if profile is not generated).
-	profilePathOnHost android.Path
-
 	// Target-dependent fields.
 	variants []*bootImageVariant
 
@@ -284,6 +290,8 @@
 }
 
 // Target-dependent description of a boot image.
+//
+// WARNING: The warning comment on bootImageConfig applies here too.
 type bootImageVariant struct {
 	*bootImageConfig
 
@@ -314,14 +322,23 @@
 	primaryImagesDeps android.Paths
 
 	// Rules which should be used in make to install the outputs on host.
-	installs           android.RuleBuilderInstalls
-	vdexInstalls       android.RuleBuilderInstalls
+	//
+	// Deprecated: Not initialized correctly, see struct comment.
+	installs android.RuleBuilderInstalls
+
+	// Rules which should be used in make to install the vdex outputs on host.
+	//
+	// Deprecated: Not initialized correctly, see struct comment.
+	vdexInstalls android.RuleBuilderInstalls
+
+	// Rules which should be used in make to install the unstripped outputs on host.
+	//
+	// Deprecated: Not initialized correctly, see struct comment.
 	unstrippedInstalls android.RuleBuilderInstalls
 
-	// Rules which should be used in make to install the outputs on device.
-	deviceInstalls android.RuleBuilderInstalls
-
 	// Path to the license metadata file for the module that built the image.
+	//
+	// Deprecated: Not initialized correctly, see struct comment.
 	licenseMetadataFile android.OptionalPath
 }
 
@@ -548,7 +565,7 @@
 // boot image files.
 //
 // The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX.
-func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch {
+func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageOutputs {
 	return buildBootImageForOsType(ctx, image, profile, android.Android)
 }
 
@@ -563,21 +580,38 @@
 	buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
 }
 
+// bootImageOutputs encapsulates information about boot images that were created/obtained by
+// commonBootclasspathFragment.produceBootImageFiles.
+type bootImageOutputs struct {
+	// Map from arch to the paths to the boot image files created/obtained for that arch.
+	byArch bootImageFilesByArch
+
+	variants []bootImageVariantOutputs
+
+	// The path to the profile file created/obtained for the boot image.
+	profile android.WritablePath
+}
+
 // buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType
 // boot image files are required for and it creates rules to build the boot image
 // files for all the required architectures for them.
 //
 // It returns a map from android.ArchType to the predefined paths of the boot image files.
-func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageFilesByArch {
+func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageOutputs {
 	filesByArch := bootImageFilesByArch{}
+	imageOutputs := bootImageOutputs{
+		byArch:  filesByArch,
+		profile: profile,
+	}
 	for _, variant := range image.variants {
 		if variant.target.Os == requiredOsType {
-			buildBootImageVariant(ctx, variant, profile)
+			variantOutputs := buildBootImageVariant(ctx, variant, profile)
+			imageOutputs.variants = append(imageOutputs.variants, variantOutputs)
 			filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
 		}
 	}
 
-	return filesByArch
+	return imageOutputs
 }
 
 // buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files.
@@ -605,8 +639,13 @@
 	rule.Build("zip_"+image.name, "zip "+image.name+" image")
 }
 
+type bootImageVariantOutputs struct {
+	config         *bootImageVariant
+	deviceInstalls android.RuleBuilderInstalls
+}
+
 // Generate boot image build rules for a specific target.
-func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) {
+func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
 
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
@@ -764,11 +803,20 @@
 	rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
 
 	// save output and installed files for makevars
+	// TODO - these are always the same and so should be initialized in genBootImageConfigs
 	image.installs = rule.Installs()
 	image.vdexInstalls = vdexInstalls
 	image.unstrippedInstalls = unstrippedInstalls
-	image.deviceInstalls = deviceInstalls
-	image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
+
+	// Only set the licenseMetadataFile from the active module.
+	if isActiveModule(ctx.Module()) {
+		image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
+	}
+
+	return bootImageVariantOutputs{
+		image,
+		deviceInstalls,
+	}
 }
 
 const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
@@ -824,8 +872,6 @@
 
 	rule.Build("bootJarsProfile", "profile boot jars")
 
-	image.profilePathOnHost = profile
-
 	return profile
 }
 
diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go
new file mode 100644
index 0000000..b704d09
--- /dev/null
+++ b/java/dexpreopt_config_test.go
@@ -0,0 +1,35 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"runtime"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestBootImageConfig(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skipf("Skipping as boot image config test is only supported on linux not %s", runtime.GOOS)
+	}
+
+	result := android.GroupFixturePreparers(
+		PrepareForBootImageConfigTest,
+	).RunTest(t)
+
+	CheckArtBootImageConfig(t, result)
+	CheckFrameworkBootImageConfig(t, result)
+}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
new file mode 100644
index 0000000..1c236d8
--- /dev/null
+++ b/java/dexpreopt_config_testing.go
@@ -0,0 +1,768 @@
+// Copyright 2022 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.
+
+// Testing support for dexpreopt config.
+//
+// The bootImageConfig/bootImageVariant structs returned by genBootImageConfigs are used in many
+// places in the build and are currently mutated in a number of those locations. This provides
+// comprehensive tests of the fields in those structs to ensure that they have been initialized
+// correctly and where relevant, mutated correctly.
+//
+// This is used in TestBootImageConfig to verify that the
+
+package java
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+// PrepareForBootImageConfigTest is the minimal set of preparers that are needed to be able to use
+// the Check*BootImageConfig methods define here.
+var PrepareForBootImageConfigTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithArchMutator,
+	android.PrepareForTestAccessingMakeVars,
+	FixtureConfigureBootJars("com.android.art:core1", "com.android.art:core2", "platform:framework"),
+)
+
+// normalizedInstall represents a android.RuleBuilderInstall that has been normalized to remove
+// test specific parts of the From path.
+type normalizedInstall struct {
+	from string
+	to   string
+}
+
+// normalizeInstalls converts a slice of android.RuleBuilderInstall into a slice of
+// normalizedInstall to allow them to be compared using android.AssertDeepEquals.
+func normalizeInstalls(installs android.RuleBuilderInstalls) []normalizedInstall {
+	var normalized []normalizedInstall
+	for _, install := range installs {
+		normalized = append(normalized, normalizedInstall{
+			from: install.From.RelativeToTop().String(),
+			to:   install.To,
+		})
+	}
+	return normalized
+}
+
+// assertInstallsEqual normalized the android.RuleBuilderInstalls and compares against the expected
+// normalizedInstalls.
+func assertInstallsEqual(t *testing.T, message string, expected []normalizedInstall, actual android.RuleBuilderInstalls) {
+	t.Helper()
+	normalizedActual := normalizeInstalls(actual)
+	android.AssertDeepEquals(t, message, expected, normalizedActual)
+}
+
+// expectedConfig encapsulates the expected properties that will be set in a bootImageConfig
+//
+// Each field <x> in here is compared against the corresponding field <x> in bootImageConfig.
+type expectedConfig struct {
+	name                     string
+	stem                     string
+	dir                      string
+	symbolsDir               string
+	installDirOnDevice       string
+	installDirOnHost         string
+	profileInstallPathInApex string
+	modules                  android.ConfiguredJarList
+	dexPaths                 []string
+	dexPathsDeps             []string
+	zip                      string
+	variants                 []*expectedVariant
+
+	// Mutated fields
+	profileInstalls            []normalizedInstall
+	profileLicenseMetadataFile string
+}
+
+// expectedVariant encapsulates the expected properties that will be set in a bootImageVariant
+//
+// Each field <x> in here is compared against the corresponding field <x> in bootImageVariant
+// except for archType which is compared against the target.Arch.ArchType field in bootImageVariant.
+type expectedVariant struct {
+	archType          android.ArchType
+	dexLocations      []string
+	dexLocationsDeps  []string
+	imagePathOnHost   string
+	imagePathOnDevice string
+	imagesDeps        []string
+	primaryImages     string
+	primaryImagesDeps []string
+
+	// Mutated fields
+	installs            []normalizedInstall
+	vdexInstalls        []normalizedInstall
+	unstrippedInstalls  []normalizedInstall
+	licenseMetadataFile string
+}
+
+// CheckArtBootImageConfig checks the status of the fields of the bootImageConfig and
+// bootImageVariant structures that are returned from artBootImageConfig.
+//
+// This is before any fields are mutated.
+func CheckArtBootImageConfig(t *testing.T, result *android.TestResult) {
+	checkArtBootImageConfig(t, result, false, "")
+}
+
+// getArtImageConfig gets the ART bootImageConfig that was created during the test.
+func getArtImageConfig(result *android.TestResult) *bootImageConfig {
+	pathCtx := &android.TestPathContext{TestResult: result}
+	imageConfig := artBootImageConfig(pathCtx)
+	return imageConfig
+}
+
+// checkArtBootImageConfig checks the ART boot image.
+//
+// mutated is true if this is called after fields in the image have been mutated by the ART
+// bootclasspath_fragment and false otherwise.
+func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated bool, expectedLicenseMetadataFile string) {
+	imageConfig := getArtImageConfig(result)
+
+	expected := &expectedConfig{
+		name:                     "art",
+		stem:                     "boot",
+		dir:                      "out/soong/test_device/dex_artjars",
+		symbolsDir:               "out/soong/test_device/dex_artjars_unstripped",
+		installDirOnDevice:       "system/framework",
+		installDirOnHost:         "apex/art_boot_images/javalib",
+		profileInstallPathInApex: "etc/boot-image.prof",
+		modules:                  android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2"}),
+		dexPaths:                 []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar"},
+		dexPathsDeps:             []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar"},
+		zip:                      "out/soong/test_device/dex_artjars/art.zip",
+		variants: []*expectedVariant{
+			{
+				archType:          android.Arm64,
+				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
+				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
+				imagePathOnHost:   "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+				imagePathOnDevice: "/system/framework/arm64/boot.art",
+				imagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+						to:   "/apex/art_boot_images/javalib/arm64/boot.art",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
+						to:   "/apex/art_boot_images/javalib/arm64/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.art",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
+						to:   "/apex/art_boot_images/javalib/arm64/boot.vdex",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat",
+						to:   "/apex/art_boot_images/javalib/arm64/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType:          android.Arm,
+				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
+				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
+				imagePathOnHost:   "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+				imagePathOnDevice: "/system/framework/arm/boot.art",
+				imagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+						to:   "/apex/art_boot_images/javalib/arm/boot.art",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
+						to:   "/apex/art_boot_images/javalib/arm/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
+						to:   "/apex/art_boot_images/javalib/arm/boot-core2.art",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/arm/boot-core2.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
+						to:   "/apex/art_boot_images/javalib/arm/boot.vdex",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+						to:   "/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat",
+						to:   "/apex/art_boot_images/javalib/arm/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/arm/boot-core2.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType:          android.X86_64,
+				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
+				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
+				imagePathOnHost:   "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+				imagePathOnDevice: "/system/framework/x86_64/boot.art",
+				imagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot.art",
+					}, {
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+					}, {
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot.vdex",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType:          android.X86,
+				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
+				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
+				imagePathOnHost:   "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+				imagePathOnDevice: "/system/framework/x86/boot.art",
+				imagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+						to:   "/apex/art_boot_images/javalib/x86/boot.art",
+					}, {
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+						to:   "/apex/art_boot_images/javalib/x86/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
+						to:   "/apex/art_boot_images/javalib/x86/boot-core2.art",
+					}, {
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/x86/boot-core2.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
+						to:   "/apex/art_boot_images/javalib/x86/boot.vdex",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+						to:   "/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+						to:   "/apex/art_boot_images/javalib/x86/boot.oat",
+					},
+					{
+						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+						to:   "/apex/art_boot_images/javalib/x86/boot-core2.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+		},
+	}
+
+	checkBootImageConfig(t, imageConfig, mutated, expected)
+}
+
+// getFrameworkImageConfig gets the framework bootImageConfig that was created during the test.
+func getFrameworkImageConfig(result *android.TestResult) *bootImageConfig {
+	pathCtx := &android.TestPathContext{TestResult: result}
+	imageConfig := defaultBootImageConfig(pathCtx)
+	return imageConfig
+}
+
+// CheckFrameworkBootImageConfig checks the status of the fields of the bootImageConfig and
+// bootImageVariant structures that are returned from defaultBootImageConfig.
+//
+// This is before any fields are mutated.
+func CheckFrameworkBootImageConfig(t *testing.T, result *android.TestResult) {
+	checkFrameworkBootImageConfig(t, result, false, "")
+}
+
+// checkFrameworkBootImageConfig checks the framework boot image.
+//
+// mutated is true if this is called after fields in the image have been mutated by the
+// platform_bootclasspath and false otherwise.
+func checkFrameworkBootImageConfig(t *testing.T, result *android.TestResult, mutated bool, expectedLicenseMetadataFile string) {
+	imageConfig := getFrameworkImageConfig(result)
+
+	expected := &expectedConfig{
+		name:                     "boot",
+		stem:                     "boot",
+		dir:                      "out/soong/test_device/dex_bootjars",
+		symbolsDir:               "out/soong/test_device/dex_bootjars_unstripped",
+		installDirOnDevice:       "system/framework",
+		installDirOnHost:         "system/framework",
+		profileInstallPathInApex: "",
+		modules:                  android.CreateTestConfiguredJarList([]string{"platform:framework"}),
+		dexPaths:                 []string{"out/soong/test_device/dex_bootjars_input/framework.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"},
+		zip:                      "out/soong/test_device/dex_bootjars/boot.zip",
+		variants: []*expectedVariant{
+			{
+				archType:     android.Arm64,
+				dexLocations: []string{"/system/framework/framework.jar"},
+				dexLocationsDeps: []string{
+					"/apex/com.android.art/javalib/core1.jar",
+					"/apex/com.android.art/javalib/core2.jar",
+					"/system/framework/framework.jar",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+				imagePathOnDevice: "/system/framework/arm64/boot-framework.art",
+				imagesDeps: []string{
+					"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",
+				},
+				primaryImages: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+				primaryImagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+						to:   "/system/framework/arm64/boot-framework.art",
+					},
+					{
+						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+						to:   "/system/framework/arm64/boot-framework.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
+						to:   "/system/framework/arm64/boot-framework.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat",
+						to:   "/system/framework/arm64/boot-framework.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType:     android.Arm,
+				dexLocations: []string{"/system/framework/framework.jar"},
+				dexLocationsDeps: []string{
+					"/apex/com.android.art/javalib/core1.jar",
+					"/apex/com.android.art/javalib/core2.jar",
+					"/system/framework/framework.jar",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+				imagePathOnDevice: "/system/framework/arm/boot-framework.art",
+				imagesDeps: []string{
+					"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",
+				},
+				primaryImages: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+				primaryImagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+						to:   "/system/framework/arm/boot-framework.art",
+					},
+					{
+						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+						to:   "/system/framework/arm/boot-framework.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
+						to:   "/system/framework/arm/boot-framework.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat",
+						to:   "/system/framework/arm/boot-framework.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType:     android.X86_64,
+				dexLocations: []string{"host/linux-x86/system/framework/framework.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",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+				imagePathOnDevice: "/system/framework/x86_64/boot-framework.art",
+				imagesDeps: []string{
+					"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",
+				},
+				primaryImages: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+				primaryImagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+						to:   "/system/framework/x86_64/boot-framework.art",
+					},
+					{
+						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+						to:   "/system/framework/x86_64/boot-framework.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
+						to:   "/system/framework/x86_64/boot-framework.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat",
+						to:   "/system/framework/x86_64/boot-framework.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+			{
+				archType:     android.X86,
+				dexLocations: []string{"host/linux-x86/system/framework/framework.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",
+				},
+				imagePathOnHost:   "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+				imagePathOnDevice: "/system/framework/x86/boot-framework.art",
+				imagesDeps: []string{
+					"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",
+				},
+				primaryImages: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+				primaryImagesDeps: []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",
+				},
+				installs: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+						to:   "/system/framework/x86/boot-framework.art",
+					},
+					{
+						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+						to:   "/system/framework/x86/boot-framework.oat",
+					},
+				},
+				vdexInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
+						to:   "/system/framework/x86/boot-framework.vdex",
+					},
+				},
+				unstrippedInstalls: []normalizedInstall{
+					{
+						from: "out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat",
+						to:   "/system/framework/x86/boot-framework.oat",
+					},
+				},
+				licenseMetadataFile: expectedLicenseMetadataFile,
+			},
+		},
+		profileInstalls: []normalizedInstall{
+			{from: "out/soong/test_device/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"},
+			{from: "out/soong/test_device/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"},
+		},
+		profileLicenseMetadataFile: expectedLicenseMetadataFile,
+	}
+
+	checkBootImageConfig(t, imageConfig, mutated, expected)
+}
+
+// clearMutatedFields clears fields in the expectedConfig that correspond to fields in the
+// bootImageConfig/bootImageVariant structs which are mutated outside the call to
+// genBootImageConfigs.
+//
+// This allows the resulting expectedConfig struct to be compared against the values of those boot
+// image structs immediately the call to genBootImageConfigs. If this is not called then the
+// expectedConfig struct will expect the boot image structs to have been mutated by the ART
+// bootclasspath_fragment and the platform_bootclasspath.
+func clearMutatedFields(expected *expectedConfig) {
+	expected.profileInstalls = nil
+	expected.profileLicenseMetadataFile = ""
+	for _, variant := range expected.variants {
+		variant.installs = nil
+		variant.vdexInstalls = nil
+		variant.unstrippedInstalls = nil
+		variant.licenseMetadataFile = ""
+	}
+}
+
+// checkBootImageConfig checks a boot image against the expected contents.
+//
+// If mutated is false then this will clear any mutated fields in the expected contents back to the
+// zero value so that they will match the unmodified values in the boot image.
+//
+// It runs the checks in an image specific subtest of the current test.
+func checkBootImageConfig(t *testing.T, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) {
+	if !mutated {
+		clearMutatedFields(expected)
+	}
+
+	t.Run(imageConfig.name, func(t *testing.T) {
+		nestedCheckBootImageConfig(t, imageConfig, expected)
+	})
+}
+
+// nestedCheckBootImageConfig does the work of comparing the image against the expected values and
+// is run in an image specific subtest.
+func nestedCheckBootImageConfig(t *testing.T, imageConfig *bootImageConfig, expected *expectedConfig) {
+	android.AssertStringEquals(t, "name", expected.name, imageConfig.name)
+	android.AssertStringEquals(t, "stem", expected.stem, imageConfig.stem)
+	android.AssertPathRelativeToTopEquals(t, "dir", expected.dir, imageConfig.dir)
+	android.AssertPathRelativeToTopEquals(t, "symbolsDir", expected.symbolsDir, imageConfig.symbolsDir)
+	android.AssertStringEquals(t, "installDirOnDevice", expected.installDirOnDevice, imageConfig.installDirOnDevice)
+	android.AssertStringEquals(t, "installDirOnHost", expected.installDirOnHost, imageConfig.installDirOnHost)
+	android.AssertStringEquals(t, "profileInstallPathInApex", expected.profileInstallPathInApex, imageConfig.profileInstallPathInApex)
+	android.AssertDeepEquals(t, "modules", expected.modules, imageConfig.modules)
+	android.AssertPathsRelativeToTopEquals(t, "dexPaths", expected.dexPaths, imageConfig.dexPaths.Paths())
+	android.AssertPathsRelativeToTopEquals(t, "dexPathsDeps", expected.dexPathsDeps, imageConfig.dexPathsDeps.Paths())
+	// dexPathsByModule is just a different representation of the other information in the config.
+	android.AssertPathRelativeToTopEquals(t, "zip", expected.zip, imageConfig.zip)
+	assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, imageConfig.profileInstalls)
+	android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, imageConfig.profileLicenseMetadataFile.RelativeToTop().String())
+
+	android.AssertIntEquals(t, "variant count", 4, len(imageConfig.variants))
+	for i, variant := range imageConfig.variants {
+		expectedVariant := expected.variants[i]
+		t.Run(variant.target.Arch.ArchType.String(), func(t *testing.T) {
+			android.AssertDeepEquals(t, "archType", expectedVariant.archType, variant.target.Arch.ArchType)
+			android.AssertDeepEquals(t, "dexLocations", expectedVariant.dexLocations, variant.dexLocations)
+			android.AssertDeepEquals(t, "dexLocationsDeps", expectedVariant.dexLocationsDeps, variant.dexLocationsDeps)
+			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)
+			assertInstallsEqual(t, "installs", expectedVariant.installs, variant.installs)
+			assertInstallsEqual(t, "vdexInstalls", expectedVariant.vdexInstalls, variant.vdexInstalls)
+			assertInstallsEqual(t, "unstrippedInstalls", expectedVariant.unstrippedInstalls, variant.unstrippedInstalls)
+			android.AssertStringEquals(t, "licenseMetadataFile", expectedVariant.licenseMetadataFile, variant.licenseMetadataFile.RelativeToTop().String())
+		})
+	}
+}
+
+// CheckMutatedArtBootImageConfig checks the mutated fields in the bootImageConfig/Variant for ART.
+func CheckMutatedArtBootImageConfig(t *testing.T, result *android.TestResult, expectedLicenseMetadataFile string) {
+	checkArtBootImageConfig(t, result, true, expectedLicenseMetadataFile)
+
+	// Check the dexpreopt make vars. Do it in here as it depends on the expected license metadata
+	// file at the moment and it
+	checkDexpreoptMakeVars(t, result, expectedLicenseMetadataFile)
+}
+
+// CheckMutatedFrameworkBootImageConfig checks the mutated fields in the bootImageConfig/Variant for framework.
+func CheckMutatedFrameworkBootImageConfig(t *testing.T, result *android.TestResult, expectedLicenseMetadataFile string) {
+	checkFrameworkBootImageConfig(t, result, true, expectedLicenseMetadataFile)
+}
+
+// checkDexpreoptMakeVars checks the DEXPREOPT_ prefixed make vars produced by dexpreoptBootJars
+// singleton.
+func checkDexpreoptMakeVars(t *testing.T, result *android.TestResult, expectedLicenseMetadataFile string) {
+	vars := result.MakeVarsForTesting(func(variable android.MakeVarVariable) bool {
+		return strings.HasPrefix(variable.Name(), "DEXPREOPT_")
+	})
+
+	out := &strings.Builder{}
+	for _, v := range vars {
+		fmt.Fprintf(out, "%s=%s\n", v.Name(), android.StringRelativeToTop(result.Config, v.Value()))
+	}
+	format := `
+DEXPREOPT_BOOTCLASSPATH_DEX_FILES=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
+DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS=/apex/com.android.art/javalib/core1.jar /apex/com.android.art/javalib/core2.jar /system/framework/framework.jar
+DEXPREOPT_BOOT_JARS_MODULES=platform:framework
+DEXPREOPT_GEN=out/host/linux-x86/bin/dexpreopt_gen
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/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:/apex/art_boot_images/javalib/arm/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/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:/apex/art_boot_images/javalib/arm64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/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:/apex/art_boot_images/javalib/x86/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/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:/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-core2.art:/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:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
+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_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
+DEXPREOPT_IMAGE_DEPS_art_host_x86_64=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
+DEXPREOPT_IMAGE_DEPS_boot_arm=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
+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_LICENSE_METADATA_art_arm=%[1]s
+DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm64=%[1]s
+DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86=%[1]s
+DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86_64=%[1]s
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
+DEXPREOPT_IMAGE_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_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_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
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/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_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
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/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.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex
+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_ZIP_art=out/soong/test_device/dex_artjars/art.zip
+DEXPREOPT_IMAGE_ZIP_boot=out/soong/test_device/dex_bootjars/boot.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
+DEXPREOPT_IMAGE_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art
+DEXPREOPT_IMAGE_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art
+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
+`
+	expected := strings.TrimSpace(fmt.Sprintf(format, expectedLicenseMetadataFile))
+	actual := strings.TrimSpace(out.String())
+	android.AssertStringEquals(t, "vars", expected, actual)
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 15585f1..2173dae 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -267,7 +267,7 @@
 			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
 			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
-			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+			ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
 		}
 	}
 
@@ -367,7 +367,7 @@
 			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
-		case libTag:
+		case libTag, sdkLibTag:
 			if dep, ok := module.(SdkLibraryDependency); ok {
 				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
diff --git a/java/droidstubs.go b/java/droidstubs.go
index d9efb40..5777b18 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -28,7 +28,7 @@
 )
 
 // The values allowed for Droidstubs' Api_levels_sdk_type
-var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"}
+var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"}
 
 func init() {
 	RegisterStubsBuildComponents(android.InitRegistrationContext)
@@ -134,7 +134,7 @@
 	// the dirs which Metalava extracts API levels annotations from.
 	Api_levels_annotations_dirs []string
 
-	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public.
+	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system', 'module-lib' and 'system-server'; defaults to public.
 	Api_levels_sdk_type *string
 
 	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
@@ -445,6 +445,8 @@
 	// for older releases. Similarly, module-lib falls back to system API.
 	var sdkDirs []string
 	switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") {
+	case "system-server":
+		sdkDirs = []string{"system-server", "module-lib", "system", "public"}
 	case "module-lib":
 		sdkDirs = []string{"module-lib", "system", "public"}
 	case "system":
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 2443692..25f8c86 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -122,7 +122,7 @@
 				"some-other-exported-dir",
 			],
 			api_levels_annotations_enabled: true,
-      api_levels_sdk_type: "%s",
+			api_levels_sdk_type: "%s",
 		}
 		`, sdkType),
 		map[string][]byte{
@@ -169,6 +169,21 @@
 	}, patterns)
 }
 
+func TestSystemServerDroidstubs(t *testing.T) {
+	patterns := getAndroidJarPatternsForDroidstubs(t, "system-server")
+
+	android.AssertArrayString(t, "order of patterns", []string{
+		"--android-jar-pattern somedir/%/system-server/android.jar",
+		"--android-jar-pattern someotherdir/%/system-server/android.jar",
+		"--android-jar-pattern somedir/%/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
+		"--android-jar-pattern somedir/%/system/android.jar",
+		"--android-jar-pattern someotherdir/%/system/android.jar",
+		"--android-jar-pattern somedir/%/public/android.jar",
+		"--android-jar-pattern someotherdir/%/public/android.jar",
+	}, patterns)
+}
+
 func TestDroidstubsSandbox(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
 		genrule {
diff --git a/java/fuzz.go b/java/fuzz.go
index d0f369f..848d364 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"path/filepath"
 	"sort"
 	"strings"
 
@@ -26,6 +27,11 @@
 	"android/soong/fuzz"
 )
 
+const (
+	hostString   = "host"
+	targetString = "target"
+)
+
 type jniProperties struct {
 	// list of jni libs
 	Jni_libs []string
@@ -39,8 +45,10 @@
 }
 
 func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("java_fuzz_host", FuzzFactory)
-	ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
+	ctx.RegisterModuleType("java_fuzz", JavaFuzzFactory)
+	ctx.RegisterModuleType("java_fuzz_host", JavaFuzzHostFactory)
+	ctx.RegisterSingletonType("java_fuzz_host_packaging", javaFuzzHostPackagingFactory)
+	ctx.RegisterSingletonType("java_fuzz_device_packaging", javaFuzzDevicePackagingFactory)
 }
 
 type JavaFuzzLibrary struct {
@@ -55,11 +63,11 @@
 // sanitized for the given sanitizer or not.
 func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool {
 	// TODO: once b/231370928 is resolved, please uncomment the loop
-	// 	for _, s := range j.jniProperties.Sanitizers {
-	// 		if sanitizerName == s {
-	// 			return true
-	// 		}
-	// 	}
+	//     for _, s := range j.jniProperties.Sanitizers {
+	//         if sanitizerName == s {
+	//             return true
+	//         }
+	//     }
 	return false
 }
 
@@ -72,7 +80,6 @@
 		// this will be used by the ingestion pipeline to determine the version
 		// of jazzer to add to the fuzzer package
 		j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true)
-
 		for _, target := range mctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
 			mctx.AddFarVariationDependencies(sharedLibVariations, cc.JniFuzzLibTag, j.jniProperties.Jni_libs...)
@@ -91,7 +98,6 @@
 	if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
 		j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
 	}
-
 	if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
 		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
 		android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
@@ -101,7 +107,17 @@
 	ctx.VisitDirectDepsWithTag(cc.JniFuzzLibTag, func(dep android.Module) {
 		sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
 		if sharedLibInfo.SharedLibrary != nil {
-			libPath := android.PathForModuleOut(ctx, sharedLibInfo.SharedLibrary.Base())
+			// The .class jars are output in slightly different locations
+			// relative to the jni libs. Therefore, for consistency across
+			// host and device fuzzers of jni lib location, we save it in a
+			// native_libs directory.
+			var relPath string
+			if sharedLibInfo.Target.Arch.ArchType.Multilib == "lib64" {
+				relPath = filepath.Join("lib64", sharedLibInfo.SharedLibrary.Base())
+			} else {
+				relPath = filepath.Join("lib", sharedLibInfo.SharedLibrary.Base())
+			}
+			libPath := android.PathForModuleOut(ctx, relPath)
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
 				Input:  sharedLibInfo.SharedLibrary,
@@ -116,13 +132,12 @@
 	j.Library.GenerateAndroidBuildActions(ctx)
 }
 
-// java_fuzz builds and links sources into a `.jar` file for the host.
+// java_fuzz_host builds and links sources into a `.jar` file for the host.
 //
 // By default, a java_fuzz produces a `.jar` file containing `.class` files.
 // This jar is not suitable for installing on a device.
-func FuzzFactory() android.Module {
+func JavaFuzzHostFactory() android.Module {
 	module := &JavaFuzzLibrary{}
-
 	module.addHostProperties()
 	module.AddProperties(&module.jniProperties)
 	module.Module.properties.Installable = proptools.BoolPtr(true)
@@ -141,23 +156,54 @@
 		ctx.AppendProperties(&disableLinuxBionic)
 	})
 
-	module.initModuleAndImport(module)
-	android.InitSdkAwareModule(module)
-	InitJavaModuleMultiTargets(module, android.HostSupported)
+	InitJavaModuleMultiTargets(module, android.HostSupportedNoCross)
 	return module
 }
 
-// Responsible for generating rules that package fuzz targets into
-// their architecture & target/host specific zip file.
-type javaFuzzPackager struct {
+// java_fuzz builds and links sources into a `.jar` file for the device.
+// This generates .class files in a jar which can then be instrumented before
+// fuzzing in Android Runtime (ART: Android OS on emulator or device)
+func JavaFuzzFactory() android.Module {
+	module := &JavaFuzzLibrary{}
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.jniProperties)
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
+	module.Module.dexpreopter.isTest = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	InitJavaModuleMultiTargets(module, android.DeviceSupported)
+	return module
+}
+
+// Responsible for generating rules that package host fuzz targets into
+// a zip file.
+type javaFuzzHostPackager struct {
 	fuzz.FuzzPackager
 }
 
-func javaFuzzPackagingFactory() android.Singleton {
-	return &javaFuzzPackager{}
+// Responsible for generating rules that package device fuzz targets into
+// a zip file.
+type javaFuzzDevicePackager struct {
+	fuzz.FuzzPackager
 }
 
-func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+func javaFuzzHostPackagingFactory() android.Singleton {
+	return &javaFuzzHostPackager{}
+}
+
+func javaFuzzDevicePackagingFactory() android.Singleton {
+	return &javaFuzzDevicePackager{}
+}
+
+func (s *javaFuzzHostPackager) GenerateBuildActions(ctx android.SingletonContext) {
+	generateBuildActions(&s.FuzzPackager, hostString, ctx)
+}
+
+func (s *javaFuzzDevicePackager) GenerateBuildActions(ctx android.SingletonContext) {
+	generateBuildActions(&s.FuzzPackager, targetString, ctx)
+}
+
+func generateBuildActions(s *fuzz.FuzzPackager, hostOrTargetString string, ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination.
 	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
@@ -171,8 +217,14 @@
 			return
 		}
 
-		if javaFuzzModule.Target().HostCross {
-			return
+		if hostOrTargetString == hostString {
+			if !javaFuzzModule.Host() {
+				return
+			}
+		} else if hostOrTargetString == targetString {
+			if javaFuzzModule.Host() || javaFuzzModule.Target().HostCross {
+				return
+			}
 		}
 
 		fuzzModuleValidator := fuzz.FuzzModule{
@@ -185,12 +237,7 @@
 			return
 		}
 
-		hostOrTargetString := "target"
-		if javaFuzzModule.Host() {
-			hostOrTargetString = "host"
-		}
 		archString := javaFuzzModule.Arch().ArchType.String()
-
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
 		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
@@ -201,7 +248,7 @@
 		files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// Add .jar
-		files = append(files, fuzz.FileToZip{javaFuzzModule.outputFile, ""})
+		files = append(files, fuzz.FileToZip{javaFuzzModule.implementationJarFile, ""})
 
 		// Add jni .so files
 		for _, fPath := range javaFuzzModule.jniFilePaths {
@@ -217,12 +264,22 @@
 	s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
 }
 
-func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+func (s *javaFuzzHostPackager) MakeVars(ctx android.MakeVarsContext) {
 	packages := s.Packages.Strings()
 	sort.Strings(packages)
 
-	ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+	ctx.Strict("SOONG_JAVA_FUZZ_HOST_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
 
 	// Preallocate the slice of fuzz targets to minimize memory allocations.
-	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS")
+	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_HOST_TARGETS")
+}
+
+func (s *javaFuzzDevicePackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.Packages.Strings()
+	sort.Strings(packages)
+
+	ctx.Strict("SOONG_JAVA_FUZZ_DEVICE_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+
+	// Preallocate the slice of fuzz targets to minimize memory allocations.
+	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_DEVICE_TARGETS")
 }
diff --git a/java/java.go b/java/java.go
index d04e52a..5091d26 100644
--- a/java/java.go
+++ b/java/java.go
@@ -348,6 +348,7 @@
 	dataDeviceBinsTag       = dependencyTag{name: "dataDeviceBins"}
 	staticLibTag            = dependencyTag{name: "staticlib"}
 	libTag                  = dependencyTag{name: "javalib", runtimeLinked: true}
+	sdkLibTag               = dependencyTag{name: "sdklib", runtimeLinked: true}
 	java9LibTag             = dependencyTag{name: "java9lib", runtimeLinked: true}
 	pluginTag               = dependencyTag{name: "plugin", toolchain: true}
 	errorpronePluginTag     = dependencyTag{name: "errorprone-plugin", toolchain: true}
@@ -374,7 +375,7 @@
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == libTag
+	return depTag == libTag || depTag == sdkLibTag
 }
 
 func IsStaticLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -420,6 +421,7 @@
 	target         android.Target
 	coverageFile   android.OptionalPath
 	unstrippedFile android.Path
+	partition      string
 }
 
 func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {
@@ -427,7 +429,7 @@
 	if sdkDep.useModule {
 		ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 		ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
-		ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+		ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
 		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
 		}
@@ -1654,7 +1656,7 @@
 		if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
 			dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
 			switch tag {
-			case libTag:
+			case libTag, sdkLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
 				flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
 			case staticLibTag:
@@ -1664,7 +1666,7 @@
 			}
 		} else if dep, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
-			case libTag:
+			case libTag, sdkLibTag:
 				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			}
 		}
@@ -2178,7 +2180,7 @@
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
-	if depTag == libTag {
+	if IsLibDepTag(depTag) {
 		// Ok, propagate <uses-library> through non-static library dependencies.
 	} else if tag, ok := depTag.(usesLibraryDependencyTag); ok && tag.sdkVersion == dexpreopt.AnySdkVersion {
 		// Ok, propagate <uses-library> through non-compatibility <uses-library> dependencies.
diff --git a/java/java_test.go b/java/java_test.go
index 7f0cea7..d2373e3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -588,8 +588,8 @@
 	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
-	assertDeepEquals(t, "foo java sources incorrect",
-		[]string{"a.java"}, fooLibrary.compiledJavaSrcs.Strings())
+	assertDeepEquals(t, "foo unique sources incorrect",
+		[]string{"a.java"}, fooLibrary.uniqueSrcFiles.Strings())
 
 	assertDeepEquals(t, "foo java source jars incorrect",
 		[]string{".intermediates/stubs-source/android_common/stubs-source-stubs.srcjar"},
diff --git a/java/lint.go b/java/lint.go
index 931820d..fcd6d31 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -473,20 +473,23 @@
 
 	cmd.FlagWithOutput("--write-reference-baseline ", baseline)
 
-	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
+	cmd.Text("; EXITCODE=$?; ")
+
+	// The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
+	// export them out of the sandbox.  Do this before exiting so that the suggestions exit even after
+	// a fatal error.
+	cmd.BuiltTool("soong_zip").
+		FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
+		FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
+		FlagWithInput("-r ", srcsList)
+
+	cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi")
 
 	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
 
 	// The HTML output contains a date, remove it to make the output deterministic.
 	rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
 
-	// The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
-	// export them out of the sandbox.
-	rule.Command().BuiltTool("soong_zip").
-		FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
-		FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
-		FlagWithInput("-r ", srcsList)
-
 	rule.Build("lint", "lint")
 
 	l.outputs = lintOutputs{
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 01e7e6e..519a702 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -39,6 +39,7 @@
 # Downgrade existing errors to warnings
 --warning_check AppCompatResource                  # 55 occurences in 10 modules
 --warning_check AppLinkUrlError                    # 111 occurences in 53 modules
+--warning_check BinderGetCallingInMainThread
 --warning_check ByteOrderMark                      # 2 occurences in 2 modules
 --warning_check DuplicateActivity                  # 3 occurences in 3 modules
 --warning_check DuplicateDefinition                # 3623 occurences in 48 modules
@@ -91,6 +92,7 @@
 --warning_check StringFormatInvalid                # 148 occurences in 11 modules
 --warning_check StringFormatMatches                # 4800 occurences in 30 modules
 --warning_check UnknownId                          # 8 occurences in 7 modules
+--warning_check UnspecifiedImmutableFlag
 --warning_check ValidFragment                      # 12 occurences in 5 modules
 --warning_check ValidRestrictions                  # 5 occurences in 1 modules
 --warning_check WebViewLayout                      # 3 occurences in 1 modules
diff --git a/java/lint_test.go b/java/lint_test.go
index 456e6ba..62450d5 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -271,7 +271,7 @@
 				"a.java",
 			],
 			min_sdk_version: "29",
-			sdk_version: "module_current",
+			sdk_version: "XXX",
 			lint: {
 				strict_updatability_linting: true,
 			},
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 24f8253..f0de7a4 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -436,10 +436,10 @@
 	profile := bootImageProfileRule(ctx, imageConfig)
 
 	// Build boot image files for the android variants.
-	androidBootImageFilesByArch := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+	androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
 
 	// Zip the android variant boot image files up.
-	buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFilesByArch)
+	buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFiles.byArch)
 
 	// Build boot image files for the host variants. There are use directly by ART host side tests.
 	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 9449707..c6acd55 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -255,12 +255,11 @@
 	if p.properties.Extensions_dir != nil {
 		extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
 		for k, v := range getLatest(extensionApiFiles) {
-			if v.version > mctx.Config().PlatformBaseSdkExtensionVersion() {
-				if _, exists := latest[k]; !exists {
-					mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
-				}
-				latest[k] = v
+			if _, exists := latest[k]; !exists {
+				mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
 			}
+			// The extension version is always at least as new as the last sdk int version (potentially identical)
+			latest[k] = v
 		}
 	}
 
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index 75422ad..2b84353 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -61,7 +61,7 @@
 }
 
 func TestPrebuiltApis_WithExtensions(t *testing.T) {
-	runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) {
+	runTestWithBaseExtensionLevel := func(v int) (foo_input, bar_input, baz_input string) {
 		result := android.GroupFixturePreparers(
 			prepareForJavaTest,
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -69,7 +69,7 @@
 			}),
 			FixtureWithPrebuiltApisAndExtensions(map[string][]string{
 				"31":      {"foo"},
-				"32":      {"foo", "bar"},
+				"32":      {"foo", "bar", "baz"},
 				"current": {"foo", "bar"},
 			}, map[string][]string{
 				"1": {"foo"},
@@ -78,15 +78,24 @@
 		).RunTest(t)
 		foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
 		bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+		baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String()
 		return
 	}
-	// Here, the base extension level is 1, so extension level 2 is the latest
-	foo_input, bar_input := runTestWithBaseExtensionLevel(1)
-	android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
-	android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+	// Extension 2 is the latest for both foo and bar, finalized after the base extension version.
+	foo_input, bar_input, baz_input := runTestWithBaseExtensionLevel(1)
+	android.AssertStringEquals(t, "Expected latest foo = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+	android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
 
-	// Here, the base extension level is 2, so 2 is not later than 32.
-	foo_input, bar_input = runTestWithBaseExtensionLevel(2)
-	android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/foo.txt", foo_input)
-	android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/bar.txt", bar_input)
+	// Extension 2 is the latest for both foo and bar, finalized together with 32
+	foo_input, bar_input, baz_input = runTestWithBaseExtensionLevel(2)
+	android.AssertStringEquals(t, "Expected latest foo = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+	android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
+
+	// Extension 3 is the current extension, but it has not yet been finalized.
+	foo_input, bar_input, baz_input = runTestWithBaseExtensionLevel(3)
+	android.AssertStringEquals(t, "Expected latest foo = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+	android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
 }
diff --git a/java/resourceshrinker.go b/java/resourceshrinker.go
new file mode 100644
index 0000000..6d59601
--- /dev/null
+++ b/java/resourceshrinker.go
@@ -0,0 +1,43 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+var shrinkResources = pctx.AndroidStaticRule("shrinkResources",
+	blueprint.RuleParams{
+		Command:     `${config.ResourceShrinkerCmd} --output $out --input $in --raw_resources $raw_resources`,
+		CommandDeps: []string{"${config.ResourceShrinkerCmd}"},
+	}, "raw_resources")
+
+func ShrinkResources(ctx android.ModuleContext, apk android.Path, outputFile android.WritablePath) {
+	protoFile := android.PathForModuleOut(ctx, apk.Base()+".proto.apk")
+	aapt2Convert(ctx, protoFile, apk, "proto")
+	strictModeFile := android.PathForSource(ctx, "prebuilts/cmdline-tools/shrinker.xml")
+	protoOut := android.PathForModuleOut(ctx, apk.Base()+".proto.out.apk")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   shrinkResources,
+		Input:  protoFile,
+		Output: protoOut,
+		Args: map[string]string{
+			"raw_resources": strictModeFile.String(),
+		},
+	})
+	aapt2Convert(ctx, outputFile, protoOut, "binary")
+}
diff --git a/java/robolectric.go b/java/robolectric.go
index 71ffdb1..b6116ec 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -23,6 +23,7 @@
 	"android/soong/android"
 	"android/soong/java/config"
 	"android/soong/tradefed"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -166,7 +167,7 @@
 		instrumentedApp.implementationAndResourcesJar,
 	}
 
-	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+	handleLibDeps := func(dep android.Module) {
 		m := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
 		r.libs = append(r.libs, ctx.OtherModuleName(dep))
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
@@ -174,15 +175,22 @@
 		}
 	}
 
+	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+		handleLibDeps(dep)
+	}
+	for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) {
+		handleLibDeps(dep)
+	}
+
 	r.combinedJar = android.PathForModuleOut(ctx, "robolectric_combined", r.outputFile.Base())
 	TransformJarsToJar(ctx, r.combinedJar, "combine jars", combinedJarJars, android.OptionalPath{},
 		false, nil, nil)
 
 	// TODO: this could all be removed if tradefed was used as the test runner, it will find everything
 	// annotated as a test and run it.
-	for _, src := range r.compiledJavaSrcs {
+	for _, src := range r.uniqueSrcFiles {
 		s := src.Rel()
-		if !strings.HasSuffix(s, "Test.java") {
+		if !strings.HasSuffix(s, "Test.java") && !strings.HasSuffix(s, "Test.kt") {
 			continue
 		} else if strings.HasSuffix(s, "/BaseRobolectricTest.java") {
 			continue
diff --git a/java/rro.go b/java/rro.go
index 3a92b0c..9d0667c 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -142,7 +142,11 @@
 		aaptLinkFlags = append(aaptLinkFlags,
 			"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
 	}
-	r.aapt.buildActions(ctx, r, nil, nil, aaptLinkFlags...)
+	if r.overridableProperties.Category != nil {
+		aaptLinkFlags = append(aaptLinkFlags,
+			"--rename-overlay-category "+*r.overridableProperties.Category)
+	}
+	r.aapt.buildActions(ctx, r, nil, nil, false, aaptLinkFlags...)
 
 	// Sign the built package
 	_, _, certificates := collectAppDeps(ctx, r, false, false)
@@ -220,6 +224,9 @@
 
 	// the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
 	Target_package_name *string
+
+	// the rro category of this overlay. The category in the manifest file is used if one was not given.
+	Category *string
 }
 
 type OverrideRuntimeResourceOverlay struct {
diff --git a/java/rro_test.go b/java/rro_test.go
index 00ba5ba..8067a47 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -201,6 +201,7 @@
 			base: "foo_overlay",
 			package_name: "com.android.bar.overlay",
 			target_package_name: "com.android.bar",
+			category: "mycategory",
 		}
 		`)
 
@@ -212,6 +213,7 @@
 		targetVariant     string
 		packageFlag       string
 		targetPackageFlag string
+		categoryFlag      string
 	}{
 		{
 			variantName:       "android_common",
@@ -228,6 +230,7 @@
 			targetVariant:     "android_common_bar",
 			packageFlag:       "com.android.bar.overlay",
 			targetPackageFlag: "com.android.bar",
+			categoryFlag:      "mycategory",
 		},
 	}
 	for _, expected := range expectedVariants {
@@ -249,6 +252,7 @@
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-category", expected.categoryFlag)
 	}
 }
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 8f499b1..fad1df7 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -65,8 +65,27 @@
 	name string
 
 	// The api scope that this scope extends.
+	//
+	// This organizes the scopes into an extension hierarchy.
+	//
+	// If set this means that the API provided by this scope includes the API provided by the scope
+	// set in this field.
 	extends *apiScope
 
+	// The next api scope that a library that uses this scope can access.
+	//
+	// This organizes the scopes into an access hierarchy.
+	//
+	// If set this means that a library that can access this API can also access the API provided by
+	// the scope set in this field.
+	//
+	// A module that sets sdk_version: "<scope>_current" should have access to the <scope> API of
+	// every java_sdk_library that it depends on. If the library does not provide an API for <scope>
+	// then it will traverse up this access hierarchy to find an API that it does provide.
+	//
+	// If this is not set then it defaults to the scope set in extends.
+	canAccess *apiScope
+
 	// The legacy enabled status for a specific scope can be dependent on other
 	// properties that have been specified on the library so it is provided by
 	// a function that can determine the status by examining those properties.
@@ -107,7 +126,7 @@
 	// The scope specific prefix to add to the api file base of "current.txt" or "removed.txt".
 	apiFilePrefix string
 
-	// The scope specific prefix to add to the sdk library module name to construct a scope specific
+	// The scope specific suffix to add to the sdk library module name to construct a scope specific
 	// module name.
 	moduleSuffix string
 
@@ -193,6 +212,11 @@
 		}
 	}
 
+	// By default, a library that can access a scope can also access the scope it extends.
+	if scope.canAccess == nil {
+		scope.canAccess = scope.extends
+	}
+
 	// Escape any special characters in the arguments. This is needed because droidstubs
 	// passes these directly to the shell command.
 	scope.droidstubsArgs = proptools.ShellEscapeList(scopeSpecificArgs)
@@ -310,6 +334,14 @@
 	apiScopeSystemServer = initApiScope(&apiScope{
 		name:    "system-server",
 		extends: apiScopePublic,
+
+		// The system-server scope can access the module-lib scope.
+		//
+		// A module that provides a system-server API is appended to the standard bootclasspath that is
+		// used by the system server. So, it should be able to access module-lib APIs provided by
+		// libraries on the bootclasspath.
+		canAccess: apiScopeModuleLib,
+
 		// The system-server scope is disabled by default in legacy mode.
 		//
 		// Enabling this would break existing usages.
@@ -539,7 +571,7 @@
 	}
 
 	// TODO: determines whether to create HTML doc or not
-	//Html_doc *bool
+	// Html_doc *bool
 }
 
 // Paths to outputs from java_sdk_library and java_sdk_library_import.
@@ -926,7 +958,7 @@
 // If this does not support the requested api scope then find the closest available
 // scope it does support. Returns nil if no such scope is available.
 func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *scopePaths {
-	for s := scope; s != nil; s = s.extends {
+	for s := scope; s != nil; s = s.canAccess {
 		if paths := c.findScopePaths(s); paths != nil {
 			return paths
 		}
@@ -1354,7 +1386,7 @@
 	// Provide additional information for inclusion in an sdk's generated .info file.
 	additionalSdkInfo := map[string]interface{}{}
 	additionalSdkInfo["dist_stem"] = module.distStem()
-	baseModuleName := module.BaseModuleName()
+	baseModuleName := module.distStem()
 	scopes := map[string]interface{}{}
 	additionalSdkInfo["scopes"] = scopes
 	for scope, scopePaths := range module.scopePaths {
@@ -2236,8 +2268,9 @@
 		Sdk_version *string
 		Libs        []string
 		Jars        []string
-		Prefer      *bool
 		Compile_dex *bool
+
+		android.UserSuppliedPrebuiltProperties
 	}{}
 	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
 	props.Sdk_version = scopeProperties.Sdk_version
@@ -2247,7 +2280,7 @@
 	props.Jars = scopeProperties.Jars
 
 	// The imports are preferred if the java_sdk_library_import is preferred.
-	props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer())
+	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
 
 	// The imports need to be compiled to dex if the java_sdk_library_import requests it.
 	compileDex := module.properties.Compile_dex
@@ -2261,16 +2294,18 @@
 
 func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
 	props := struct {
-		Name   *string
-		Srcs   []string
-		Prefer *bool
+		Name *string
+		Srcs []string
+
+		android.UserSuppliedPrebuiltProperties
 	}{}
 	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope))
 	props.Srcs = scopeProperties.Stub_srcs
-	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props)
 
 	// The stubs source is preferred if the java_sdk_library_import is preferred.
-	props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer())
+	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
+
+	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props)
 }
 
 // Add the dependencies on the child module in the component deps mutator so that it
@@ -2904,6 +2939,18 @@
 type sdkLibrarySdkMemberProperties struct {
 	android.SdkMemberPropertiesBase
 
+	// Stem name for files in the sdk snapshot.
+	//
+	// This is used to construct the path names of various sdk library files in the sdk snapshot to
+	// make sure that they match the finalized versions of those files in prebuilts/sdk.
+	//
+	// This property is marked as keep so that it will be kept in all instances of this struct, will
+	// not be cleared but will be copied to common structs. That is needed because this field is used
+	// to construct many file names for other parts of this struct and so it needs to be present in
+	// all structs. If it was not marked as keep then it would be cleared in some structs and so would
+	// be unavailable for generating file names if there were other properties that were still set.
+	Stem string `sdk:"keep"`
+
 	// Scope to per scope properties.
 	Scopes map[*apiScope]*scopeProperties
 
@@ -2965,6 +3012,9 @@
 func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
 	sdk := variant.(*SdkLibrary)
 
+	// Copy the stem name for files in the sdk snapshot.
+	s.Stem = sdk.distStem()
+
 	s.Scopes = make(map[*apiScope]*scopeProperties)
 	for _, apiScope := range allApiScopes {
 		paths := sdk.findScopePaths(apiScope)
@@ -3017,6 +3067,8 @@
 		propertySet.AddProperty("permitted_packages", s.Permitted_packages)
 	}
 
+	stem := s.Stem
+
 	for _, apiScope := range allApiScopes {
 		if properties, ok := s.Scopes[apiScope]; ok {
 			scopeSet := propertySet.AddPropertySet(apiScope.propertyName)
@@ -3025,7 +3077,7 @@
 
 			var jars []string
 			for _, p := range properties.Jars {
-				dest := filepath.Join(scopeDir, ctx.Name()+"-stubs.jar")
+				dest := filepath.Join(scopeDir, stem+"-stubs.jar")
 				ctx.SnapshotBuilder().CopyToSnapshot(p, dest)
 				jars = append(jars, dest)
 			}
@@ -3033,31 +3085,31 @@
 
 			if ctx.SdkModuleContext().Config().IsEnvTrue("SOONG_SDK_SNAPSHOT_USE_SRCJAR") {
 				// Copy the stubs source jar into the snapshot zip as is.
-				srcJarSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".srcjar")
+				srcJarSnapshotPath := filepath.Join(scopeDir, stem+".srcjar")
 				ctx.SnapshotBuilder().CopyToSnapshot(properties.StubsSrcJar, srcJarSnapshotPath)
 				scopeSet.AddProperty("stub_srcs", []string{srcJarSnapshotPath})
 			} else {
 				// Merge the stubs source jar into the snapshot zip so that when it is unpacked
 				// the source files are also unpacked.
-				snapshotRelativeDir := filepath.Join(scopeDir, ctx.Name()+"_stub_sources")
+				snapshotRelativeDir := filepath.Join(scopeDir, stem+"_stub_sources")
 				ctx.SnapshotBuilder().UnzipToSnapshot(properties.StubsSrcJar, snapshotRelativeDir)
 				scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir})
 			}
 
 			if properties.CurrentApiFile != nil {
-				currentApiSnapshotPath := apiScope.snapshotRelativeCurrentApiTxtPath(ctx.Name())
+				currentApiSnapshotPath := apiScope.snapshotRelativeCurrentApiTxtPath(stem)
 				ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, currentApiSnapshotPath)
 				scopeSet.AddProperty("current_api", currentApiSnapshotPath)
 			}
 
 			if properties.RemovedApiFile != nil {
-				removedApiSnapshotPath := apiScope.snapshotRelativeRemovedApiTxtPath(ctx.Name())
+				removedApiSnapshotPath := apiScope.snapshotRelativeRemovedApiTxtPath(stem)
 				ctx.SnapshotBuilder().CopyToSnapshot(properties.RemovedApiFile, removedApiSnapshotPath)
 				scopeSet.AddProperty("removed_api", removedApiSnapshotPath)
 			}
 
 			if properties.AnnotationsZip != nil {
-				annotationsSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"_annotations.zip")
+				annotationsSnapshotPath := filepath.Join(scopeDir, stem+"_annotations.zip")
 				ctx.SnapshotBuilder().CopyToSnapshot(properties.AnnotationsZip, annotationsSnapshotPath)
 				scopeSet.AddProperty("annotations", annotationsSnapshotPath)
 			}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 805bc22..096bca8 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -699,6 +700,80 @@
 		`)
 }
 
+func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo-public", "foo-system", "foo-module-lib", "foo-system-server"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo-public",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library {
+			name: "foo-system",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library {
+			name: "foo-module-lib",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library {
+			name: "foo-system-server",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			system_server: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			libs: ["foo-public", "foo-system", "foo-module-lib", "foo-system-server"],
+			sdk_version: "system_server_current",
+		}
+		`)
+
+	stubsPath := func(name string, scope *apiScope) string {
+		name = scope.stubsLibraryModuleName(name)
+		return fmt.Sprintf("out/soong/.intermediates/%[1]s/android_common/turbine-combined/%[1]s.jar", name)
+	}
+
+	// The bar library should depend on the highest (where system server is highest and public is
+	// lowest) API scopes provided by each of the foo-* modules. The highest API scope provided by the
+	// foo-<x> module is <x>.
+	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
+	stubLibraries := []string{
+		stubsPath("foo-public", apiScopePublic),
+		stubsPath("foo-system", apiScopeSystem),
+		stubsPath("foo-module-lib", apiScopeModuleLib),
+		stubsPath("foo-system-server", apiScopeSystemServer),
+	}
+	expectedPattern := fmt.Sprintf(`^-classpath .*:\Q%s\E$`, strings.Join(stubLibraries, ":"))
+	if expected, actual := expectedPattern, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected pattern %q to match %#q", expected, actual)
+	}
+}
+
 func TestJavaSdkLibrary_MissingScope(t *testing.T) {
 	prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
@@ -875,11 +950,12 @@
 	})
 }
 
-func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
+func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer android.FixturePreparer) {
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
+		preparer,
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -893,11 +969,37 @@
 
 		java_sdk_library_import {
 			name: "sdklib",
-			prefer: true,
+			`+prefer+`
 			public: {
 				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
 			},
 		}
+
+		java_library {
+			name: "combined",
+			static_libs: [
+				"sdklib.stubs",
+			],
+			java_resources: [
+				":sdklib.stubs.source",
+				":sdklib{.public.api.txt}",
+				":sdklib{.public.removed-api.txt}",
+				":sdklib{.public.annotations.zip}",
+			],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+
+		java_library {
+			name: "public",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "current",
+		}
 		`)
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
@@ -913,9 +1015,48 @@
 	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
 		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
+		`prebuilt_sdklib.stubs.source`,
 		`sdklib.impl`,
 		`sdklib.xml`,
 	})
+
+	// Make sure that dependencies on child modules use the prebuilt when preferred.
+	CheckModuleDependencies(t, result.TestContext, "combined", "android_common", []string{
+		// Each use of :sdklib{...} adds a dependency onto prebuilt_sdklib.
+		`prebuilt_sdklib`,
+		`prebuilt_sdklib`,
+		`prebuilt_sdklib`,
+		`prebuilt_sdklib.stubs`,
+		`prebuilt_sdklib.stubs.source`,
+	})
+
+	// Make sure that dependencies on sdklib that resolve to one of the child libraries use the
+	// prebuilt library.
+	public := result.ModuleForTests("public", "android_common")
+	rule := public.Output("javac/public.jar")
+	inputs := rule.Implicits.Strings()
+	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/combined/sdklib.stubs.jar"
+	if !android.InList(expected, inputs) {
+		t.Errorf("expected %q to contain %q", inputs, expected)
+	}
+}
+
+func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
+	t.Run("prefer", func(t *testing.T) {
+		testJavaSdkLibraryImport_Preferred(t, "prefer: true,", android.NullFixturePreparer)
+	})
+
+	t.Run("use_source_config_var", func(t *testing.T) {
+		testJavaSdkLibraryImport_Preferred(t,
+			"use_source_config_var: {config_namespace: \"acme\", var_name: \"use_source\"},",
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.VendorVars = map[string]map[string]string{
+					"acme": {
+						"use_source": "false",
+					},
+				}
+			}))
+	})
 }
 
 func TestJavaSdkLibraryEnforce(t *testing.T) {
diff --git a/java/testing.go b/java/testing.go
index 1f41191..49430ee 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -393,6 +393,7 @@
 			aidl: {
 				export_include_dirs: ["framework/aidl"],
 			},
+			compile_dex: true,
 		}
 
 		android_app {
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 75154f9..61b17bf 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -42,7 +42,7 @@
 
 license_kind {
     name: "SPDX-license-identifier-0BSD",
-    conditions: ["unencumbered"],
+    conditions: ["permissive"],
     url: "https://spdx.org/licenses/0BSD",
 }
 
@@ -933,7 +933,7 @@
 
 license_kind {
     name: "SPDX-license-identifier-MIT-0",
-    conditions: ["notice"],
+    conditions: ["permissive"],
     url: "https://spdx.org/licenses/MIT-0.html",
 }
 
diff --git a/python/Android.bp b/python/Android.bp
index e49fa6a..99c02bd 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -27,3 +27,15 @@
     ],
     pluginFor: ["soong_build"],
 }
+
+// We're transitioning all of these flags to be true by default.
+// This is a defaults flag that can be used to easily add all of them to
+// certain modules.
+python_defaults {
+    name: "modern_python_path_defaults",
+    dont_add_top_level_directories_to_path: true,
+    dont_add_entrypoint_folder_to_path: true,
+    proto: {
+        respect_pkg_path: true,
+    },
+}
diff --git a/python/binary.go b/python/binary.go
index af29bb6..1f49a54 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -34,7 +34,7 @@
 }
 
 type bazelPythonBinaryAttributes struct {
-	Main           *string
+	Main           *bazel.Label
 	Srcs           bazel.LabelListAttribute
 	Deps           bazel.LabelListAttribute
 	Python_version *string
@@ -42,17 +42,6 @@
 }
 
 func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
-	var main *string
-	for _, propIntf := range m.GetProperties() {
-		if props, ok := propIntf.(*BinaryProperties); ok {
-			// main is optional.
-			if props.Main != nil {
-				main = props.Main
-				break
-			}
-		}
-	}
-
 	// 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
@@ -72,13 +61,24 @@
 
 	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
 	attrs := &bazelPythonBinaryAttributes{
-		Main:           main,
+		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",
@@ -116,6 +116,22 @@
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
 	Auto_gen_config *bool
+
+	// Currently, both the root of the zipfile and all the directories 1 level
+	// below that are added to the python path. When this flag is set to true,
+	// only the root of the zipfile will be added to the python path. This flag
+	// will be removed after all the python modules in the tree have been updated
+	// to support it. When using embedded_launcher: true, this is already the
+	// behavior. The default is currently false.
+	Dont_add_top_level_directories_to_path *bool
+
+	// Setting this to true will mimic Python 3.11+'s PYTHON_SAFE_PATH environment
+	// variable or -P flag, even on older python versions. This is a temporary
+	// flag while modules are changed to support it, eventually true will be the
+	// default and the flag will be removed. The default is currently false. It
+	// is only applicable when embedded_launcher is false, when embedded_launcher
+	// is true this is already implied.
+	Dont_add_entrypoint_folder_to_path *bool
 }
 
 type binaryDecorator struct {
@@ -128,10 +144,6 @@
 	IntermPathForModuleOut() android.OptionalPath
 }
 
-var (
-	StubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
-)
-
 func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
 	module := newModule(hod, android.MultilibFirst)
 	decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
@@ -180,9 +192,13 @@
 		})
 	}
 
+	addTopDirectoriesToPath := !proptools.BoolDefault(binary.binaryProperties.Dont_add_top_level_directories_to_path, true)
+	dontAddEntrypointFolderToPath := proptools.BoolDefault(binary.binaryProperties.Dont_add_entrypoint_folder_to_path, true)
+
 	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
 		binary.getHostInterpreterName(ctx, actualVersion),
-		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...),
+		addTopDirectoriesToPath, dontAddEntrypointFolderToPath)
 
 	return android.OptionalPathForPath(binFile)
 }
diff --git a/python/builder.go b/python/builder.go
index 7d7239c..f7f9a99 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -20,7 +20,6 @@
 	"strings"
 
 	"android/soong/android"
-
 	"github.com/google/blueprint"
 	_ "github.com/google/blueprint/bootstrap"
 )
@@ -44,13 +43,25 @@
 
 	hostPar = pctx.AndroidStaticRule("hostPar",
 		blueprint.RuleParams{
-			Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
+			Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' -e 's/ADD_TOP_DIRECTORIES_TO_PATH/$addTopDirectoriesToPath/g' build/soong/python/scripts/stub_template_host.txt > $out.main && ` +
 				`echo "#!/usr/bin/env $interp" >${out}.prefix &&` +
-				`$mergeParCmd -p --prefix ${out}.prefix -pm $stub $out $srcsZips && ` +
-				`chmod +x $out && (rm -f $stub; rm -f ${out}.prefix)`,
-			CommandDeps: []string{"$mergeParCmd"},
+				`$mergeParCmd -p --prefix ${out}.prefix -pm $out.main $out $srcsZips && ` +
+				`chmod +x $out && (rm -f $out.main; rm -f ${out}.prefix)`,
+			CommandDeps: []string{"$mergeParCmd", "build/soong/python/scripts/stub_template_host.txt"},
 		},
-		"interp", "main", "template", "stub", "srcsZips")
+		"interp", "main", "srcsZips", "addTopDirectoriesToPath")
+
+	hostParWithoutAddingEntrypointFolderToPath = pctx.AndroidStaticRule("hostParWithoutAddingEntrypointFolderToPath",
+		blueprint.RuleParams{
+			Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/__soong_entrypoint_redirector__.py/g' -e 's/ADD_TOP_DIRECTORIES_TO_PATH/$addTopDirectoriesToPath/g' build/soong/python/scripts/stub_template_host.txt > $out.main && ` +
+				"sed -e 's/ENTRY_POINT/$main/g' build/soong/python/scripts/main_non_embedded.py >`dirname $out`/__soong_entrypoint_redirector__.py && " +
+				"$parCmd -o $out.entrypoint_zip -C `dirname $out` -f `dirname $out`/__soong_entrypoint_redirector__.py && " +
+				`echo "#!/usr/bin/env $interp" >${out}.prefix &&` +
+				`$mergeParCmd -p --prefix ${out}.prefix -pm $out.main $out $srcsZips $out.entrypoint_zip && ` +
+				"chmod +x $out && (rm -f $out.main; rm -f ${out}.prefix; rm -f $out.entrypoint_zip; rm -f `dirname $out`/__soong_entrypoint_redirector__.py)",
+			CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/stub_template_host.txt", "build/soong/python/scripts/main_non_embedded.py"},
+		},
+		"interp", "main", "srcsZips", "addTopDirectoriesToPath")
 
 	embeddedPar = pctx.AndroidStaticRule("embeddedPar",
 		blueprint.RuleParams{
@@ -58,7 +69,7 @@
 				`sed 's/ENTRY_POINT/$main/' build/soong/python/scripts/main.py >$out.main &&` +
 				`$mergeParCmd -p -pm $out.main --prefix $launcher $out $srcsZips && ` +
 				`chmod +x $out && rm -rf $out.main`,
-			CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/main.py"},
+			CommandDeps: []string{"$mergeParCmd", "build/soong/python/scripts/main.py"},
 		},
 		"main", "srcsZips", "launcher")
 
@@ -81,7 +92,7 @@
 
 func registerBuildActionForParFile(ctx android.ModuleContext, embeddedLauncher bool,
 	launcherPath android.OptionalPath, interpreter, main, binName string,
-	srcsZips android.Paths) android.Path {
+	srcsZips android.Paths, addTopDirectoriesToPath bool, dontAddEntrypointFolderToPath bool) android.Path {
 
 	// .intermediate output path for bin executable.
 	binFile := android.PathForModuleOut(ctx, binName)
@@ -90,26 +101,37 @@
 	implicits := srcsZips
 
 	if !embeddedLauncher {
-		// the path of stub_template_host.txt from source tree.
-		template := android.PathForSource(ctx, StubTemplateHost)
-		implicits = append(implicits, template)
-
-		// intermediate output path for __main__.py
-		stub := android.PathForModuleOut(ctx, mainFileName).String()
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        hostPar,
-			Description: "host python archive",
-			Output:      binFile,
-			Implicits:   implicits,
-			Args: map[string]string{
-				"interp":   strings.Replace(interpreter, "/", `\/`, -1),
-				"main":     strings.Replace(main, "/", `\/`, -1),
-				"template": template.String(),
-				"stub":     stub,
-				"srcsZips": strings.Join(srcsZips.Strings(), " "),
-			},
-		})
+		addDirsString := "False"
+		if addTopDirectoriesToPath {
+			addDirsString = "True"
+		}
+		if dontAddEntrypointFolderToPath {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        hostParWithoutAddingEntrypointFolderToPath,
+				Description: "host python archive",
+				Output:      binFile,
+				Implicits:   implicits,
+				Args: map[string]string{
+					"interp":                  strings.Replace(interpreter, "/", `\/`, -1),
+					"main":                    strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
+					"srcsZips":                strings.Join(srcsZips.Strings(), " "),
+					"addTopDirectoriesToPath": addDirsString,
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        hostPar,
+				Description: "host python archive",
+				Output:      binFile,
+				Implicits:   implicits,
+				Args: map[string]string{
+					"interp":                  strings.Replace(interpreter, "/", `\/`, -1),
+					"main":                    strings.Replace(main, "/", `\/`, -1),
+					"srcsZips":                strings.Join(srcsZips.Strings(), " "),
+					"addTopDirectoriesToPath": addDirsString,
+				},
+			})
+		}
 	} else if launcherPath.Valid() {
 		// added launcherPath to the implicits Ninja dependencies.
 		implicits = append(implicits, launcherPath.Path())
diff --git a/python/defaults.go b/python/defaults.go
index dba23a7..3dc5bc4 100644
--- a/python/defaults.go
+++ b/python/defaults.go
@@ -19,7 +19,7 @@
 )
 
 func init() {
-	android.RegisterModuleType("python_defaults", defaultsFactory)
+	android.RegisterModuleType("python_defaults", DefaultsFactory)
 }
 
 type Defaults struct {
@@ -30,16 +30,13 @@
 func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 }
 
-func defaultsFactory() android.Module {
-	return DefaultsFactory()
-}
-
-func DefaultsFactory(props ...interface{}) android.Module {
+func DefaultsFactory() android.Module {
 	module := &Defaults{}
 
-	module.AddProperties(props...)
 	module.AddProperties(
 		&BaseProperties{},
+		&android.ProtoProperties{},
+		&BinaryProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/python/python.go b/python/python.go
index daf7c14..6f3a0ec 100644
--- a/python/python.go
+++ b/python/python.go
@@ -356,10 +356,6 @@
 	protoExt             = ".proto"
 	pyVersion2           = "PY2"
 	pyVersion3           = "PY3"
-	initFileName         = "__init__.py"
-	mainFileName         = "__main__.py"
-	entryPointFile       = "entry_point.txt"
-	parFileExt           = ".zip"
 	internalPath         = "internal"
 )
 
@@ -681,8 +677,7 @@
 		protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
 		protoFlags.OutTypeFlag = "--python_out"
 
-		// TODO(b/247578564): Change the default to true, and then eventually remove respect_pkg_path
-		protosRespectPkgPath := proptools.BoolDefault(p.properties.Proto.Respect_pkg_path, false)
+		protosRespectPkgPath := proptools.BoolDefault(p.properties.Proto.Respect_pkg_path, true)
 		pkgPathForProtos := pkgPath
 		if pkgPathForProtos != "" && protosRespectPkgPath {
 			pkgPathStagingDir := android.PathForModuleGen(ctx, "protos_staged_for_pkg_path")
diff --git a/python/python_test.go b/python/python_test.go
index f57f504..42a1ffb 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -300,8 +300,6 @@
 				filepath.Join("dir", "file2.py"):       nil,
 				filepath.Join("dir", "bin.py"):         nil,
 				filepath.Join("dir", "file4.py"):       nil,
-				StubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
-				MAIN_FILE = '%main%'`),
 			},
 			expectedBinaries: []pyModule{
 				{
diff --git a/python/scripts/main_non_embedded.py b/python/scripts/main_non_embedded.py
new file mode 100644
index 0000000..ffbaaa8
--- /dev/null
+++ b/python/scripts/main_non_embedded.py
@@ -0,0 +1,6 @@
+import runpy
+
+# The purpose of this file is to implement python 3.11+'s
+# PYTHON_SAFE_PATH / -P option on older python versions.
+
+runpy._run_module_as_main("ENTRY_POINT", alter_argv=False)
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index 23897b3..a0ddffe 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -1,7 +1,6 @@
 #!/usr/bin/env '%interpreter%'
 
 import os
-import re
 import tempfile
 import shutil
 import sys
@@ -15,56 +14,31 @@
 # Don't imply 'import site' on initialization
 PYTHON_ARG = '-S'
 
-def SearchPathEnv(name):
-  search_path = os.getenv('PATH', os.defpath).split(os.pathsep)
-  for directory in search_path:
-    if directory == '': continue
-    path = os.path.join(directory, name)
-    # Check if path is actual executable file.
-    if os.path.isfile(path) and os.access(path, os.X_OK):
-      return path
-  return None
-
-def FindPythonBinary():
-  if PYTHON_BINARY.startswith('/'):
-    # Case 1: Python interpreter is directly provided with absolute path.
-    return PYTHON_BINARY
-  else:
-    # Case 2: Find Python interpreter through environment variable: PATH.
-    return SearchPathEnv(PYTHON_BINARY)
-
-# Create the runfiles tree by extracting the zip file
-def ExtractRunfiles():
-  temp_dir = tempfile.mkdtemp("", "Soong.python_")
-  zf = zipfile.ZipFile(os.path.dirname(__file__))
-  zf.extractall(temp_dir)
-  return temp_dir
-
 def Main():
   args = sys.argv[1:]
 
-  new_env = {}
-  runfiles_path = None
-
+  runfiles_path = tempfile.mkdtemp(prefix="Soong.python_")
   try:
-    runfiles_path = ExtractRunfiles()
+    zf = zipfile.ZipFile(os.path.dirname(__file__))
+    zf.extractall(runfiles_path)
+    zf.close()
 
     # Add runfiles path to PYTHONPATH.
     python_path_entries = [runfiles_path]
 
-    # Add top dirs within runfiles path to PYTHONPATH.
-    top_entries = [os.path.join(runfiles_path, i) for i in os.listdir(runfiles_path)]
-    top_pkg_dirs = [i for i in top_entries if os.path.isdir(i)]
-    python_path_entries += top_pkg_dirs
+    if ADD_TOP_DIRECTORIES_TO_PATH:
+      # Add top dirs within runfiles path to PYTHONPATH.
+      top_entries = [os.path.join(runfiles_path, i) for i in os.listdir(runfiles_path)]
+      top_pkg_dirs = [i for i in top_entries if os.path.isdir(i)]
+      python_path_entries += top_pkg_dirs
 
+    new_python_path = ":".join(python_path_entries)
     old_python_path = os.environ.get(PYTHON_PATH)
-    separator = ':'
-    new_python_path = separator.join(python_path_entries)
 
-    # Copy old PYTHONPATH.
     if old_python_path:
-      new_python_path += separator + old_python_path
-    new_env[PYTHON_PATH] = new_python_path
+      os.environ.update({PYTHON_PATH: new_python_path + ":" + old_python_path})
+    else:
+      os.environ.update({PYTHON_PATH: new_python_path})
 
     # Now look for main python source file.
     main_filepath = os.path.join(runfiles_path, MAIN_FILE)
@@ -73,23 +47,14 @@
     assert os.access(main_filepath, os.R_OK), \
            'Cannot exec() %r: file not readable.' % main_filepath
 
-    python_program = FindPythonBinary()
-    if python_program is None:
-      raise AssertionError('Could not find python binary: ' + PYTHON_BINARY)
-    args = [python_program, PYTHON_ARG, main_filepath] + args
-
-    os.environ.update(new_env)
+    args = [PYTHON_BINARY, PYTHON_ARG, main_filepath] + args
 
     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)
-    retCode = subprocess.call(args, close_fds=False)
-    sys.exit(retCode)
-  except:
-    raise
+    sys.exit(subprocess.call(args, close_fds=False))
   finally:
-    if runfiles_path is not None:
-      shutil.rmtree(runfiles_path, True)
+    shutil.rmtree(runfiles_path, ignore_errors=True)
 
 if __name__ == '__main__':
   Main()
diff --git a/python/tests/dont_import_folder_of_entrypoint/Android.bp b/python/tests/dont_import_folder_of_entrypoint/Android.bp
new file mode 100644
index 0000000..ea5076e
--- /dev/null
+++ b/python/tests/dont_import_folder_of_entrypoint/Android.bp
@@ -0,0 +1,27 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test_host {
+    name: "py_dont_import_folder_of_entrypoint_test",
+    main: "mypkg/main.py",
+    srcs: [
+        "mypkg/main.py",
+        "mypkg/mymodule.py",
+    ],
+    defaults: ["modern_python_path_defaults"],
+}
+
+python_test_host {
+    name: "py_dont_import_folder_of_entrypoint_test_embedded_launcher",
+    main: "mypkg/main.py",
+    srcs: [
+        "mypkg/main.py",
+        "mypkg/mymodule.py",
+    ],
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
+}
diff --git a/python/tests/dont_import_folder_of_entrypoint/mypkg/main.py b/python/tests/dont_import_folder_of_entrypoint/mypkg/main.py
new file mode 100644
index 0000000..c6a36ed
--- /dev/null
+++ b/python/tests/dont_import_folder_of_entrypoint/mypkg/main.py
@@ -0,0 +1,15 @@
+import unittest
+import sys
+
+class TestProtoWithPkgPath(unittest.TestCase):
+
+    def test_cant_import_mymodule_directly(self):
+        with self.assertRaises(ImportError):
+            import mymodule
+
+    def test_can_import_mymodule_by_parent_package(self):
+        import mypkg.mymodule
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/tests/dont_import_folder_of_entrypoint/mypkg/mymodule.py b/python/tests/dont_import_folder_of_entrypoint/mypkg/mymodule.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/tests/dont_import_folder_of_entrypoint/mypkg/mymodule.py
diff --git a/python/tests/proto_pkg_path/Android.bp b/python/tests/proto_pkg_path/Android.bp
index 17afde2..ef79850 100644
--- a/python/tests/proto_pkg_path/Android.bp
+++ b/python/tests/proto_pkg_path/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 python_test_host {
     name: "py_proto_pkg_path_test",
     main: "main.py",
diff --git a/python/tests/top_level_dirs/Android.bp b/python/tests/top_level_dirs/Android.bp
new file mode 100644
index 0000000..fe13d4f
--- /dev/null
+++ b/python/tests/top_level_dirs/Android.bp
@@ -0,0 +1,13 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test_host {
+    name: "py_dont_add_top_level_dirs_test",
+    main: "main.py",
+    srcs: [
+        "main.py",
+        "mypkg/mymodule.py",
+    ],
+    dont_add_top_level_directories_to_path: true,
+}
diff --git a/python/tests/top_level_dirs/main.py b/python/tests/top_level_dirs/main.py
new file mode 100644
index 0000000..9f30bfa
--- /dev/null
+++ b/python/tests/top_level_dirs/main.py
@@ -0,0 +1,17 @@
+import unittest
+import sys
+
+print(sys.path, file=sys.stderr)
+
+class TestProtoWithPkgPath(unittest.TestCase):
+
+    def test_cant_import_mymodule_directly(self):
+        with self.assertRaises(ImportError):
+            import mymodule
+
+    def test_can_import_mymodule_by_parent_package(self):
+        import mypkg.mymodule
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/tests/top_level_dirs/mypkg/mymodule.py b/python/tests/top_level_dirs/mypkg/mymodule.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/tests/top_level_dirs/mypkg/mymodule.py
diff --git a/rust/binary.go b/rust/binary.go
index 41110f9..056888e 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -106,7 +106,7 @@
 		if static {
 			deps.CrtBegin = []string{"libc_musl_crtbegin_static"}
 		} else {
-			deps.CrtBegin = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}
+			deps.CrtBegin = []string{"libc_musl_crtbegin_dynamic"}
 		}
 		deps.CrtEnd = []string{"libc_musl_crtend"}
 	}
diff --git a/rust/compiler.go b/rust/compiler.go
index bf6a488..6055158 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -161,7 +161,7 @@
 	// This is primarily meant for rust_binary and rust_ffi modules where the default
 	// linkage of libstd might need to be overridden in some use cases. This should
 	// generally be avoided with other module types since it may cause collisions at
-	// linkage if all dependencies of the root binary module do not link against libstd\
+	// linkage if all dependencies of the root binary module do not link against libstd
 	// the same way.
 	Prefer_rlib *bool `android:"arch_variant"`
 
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index be73d69..79ea7a1 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -15,6 +15,7 @@
         "arm64_device.go",
         "global.go",
         "lints.go",
+        "riscv64_device.go",
         "toolchain.go",
         "darwin_host.go",
         "x86_linux_bionic_host.go",
diff --git a/rust/config/global.go b/rust/config/global.go
index e676837..81aec7e 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.63.0"
+	RustDefaultVersion = "1.64.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/config/riscv64_device.go b/rust/config/riscv64_device.go
new file mode 100644
index 0000000..d014dbf
--- /dev/null
+++ b/rust/config/riscv64_device.go
@@ -0,0 +1,91 @@
+// Copyright 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	Riscv64RustFlags            = []string{}
+	Riscv64ArchFeatureRustFlags = map[string][]string{"": {}}
+	Riscv64LinkFlags            = []string{}
+
+	Riscv64ArchVariantRustFlags = map[string][]string{"": {}}
+)
+
+func init() {
+	registerToolchainFactory(android.Android, android.Riscv64, Riscv64ToolchainFactory)
+
+	pctx.StaticVariable("Riscv64ToolchainRustFlags", strings.Join(Riscv64RustFlags, " "))
+	pctx.StaticVariable("Riscv64ToolchainLinkFlags", strings.Join(Riscv64LinkFlags, " "))
+
+	for variant, rustFlags := range Riscv64ArchVariantRustFlags {
+		pctx.StaticVariable("Riscv64"+variant+"VariantRustFlags",
+			strings.Join(rustFlags, " "))
+	}
+
+}
+
+type toolchainRiscv64 struct {
+	toolchain64Bit
+	toolchainRustFlags string
+}
+
+func (t *toolchainRiscv64) RustTriple() string {
+	return "riscv64-linux-android"
+}
+
+func (t *toolchainRiscv64) ToolchainLinkFlags() string {
+	// Prepend the lld flags from cc_config so we stay in sync with cc
+	return "${config.DeviceGlobalLinkFlags} ${cc_config.Riscv64Lldflags} ${config.Riscv64ToolchainLinkFlags}"
+}
+
+func (t *toolchainRiscv64) ToolchainRustFlags() string {
+	return t.toolchainRustFlags
+}
+
+func (t *toolchainRiscv64) RustFlags() string {
+	return "${config.Riscv64ToolchainRustFlags}"
+}
+
+func (t *toolchainRiscv64) Supported() bool {
+	return true
+}
+
+func (toolchainRiscv64) LibclangRuntimeLibraryArch() string {
+	return "riscv64"
+}
+
+func Riscv64ToolchainFactory(arch android.Arch) Toolchain {
+	archVariant := arch.ArchVariant
+
+	toolchainRustFlags := []string{
+		"${config.Riscv64ToolchainRustFlags}",
+		"${config.Riscv64" + archVariant + "VariantRustFlags}",
+	}
+
+	toolchainRustFlags = append(toolchainRustFlags, deviceGlobalRustFlags...)
+
+	for _, feature := range arch.ArchFeatures {
+		toolchainRustFlags = append(toolchainRustFlags, Riscv64ArchFeatureRustFlags[feature]...)
+	}
+
+	return &toolchainRiscv64{
+		toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+	}
+}
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 586095c..76cf21a 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -34,6 +34,7 @@
 	*binaryDecorator
 
 	fuzzPackagedModule fuzz.FuzzPackagedModule
+	sharedLibraries    android.Paths
 }
 
 var _ compiler = (*fuzzDecorator)(nil)
@@ -86,6 +87,15 @@
 		&fuzzer.fuzzPackagedModule.FuzzProperties)
 }
 
+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)
+
+	return out
+}
+
 func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
 	return RlibLinkage
 }
@@ -149,11 +159,8 @@
 		// The executable.
 		files = append(files, fuzz.FileToZip{rustModule.UnstrippedOutputFile(), ""})
 
-		// Grab the list of required shared libraries.
-		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, cc.UnstrippedOutputFile, cc.IsValidSharedDependency)
-
 		// Package shared libraries
-		files = append(files, cc.GetSharedLibsToZip(sharedLibraries, rustModule, &s.FuzzPackager, archString, "lib", &sharedLibraryInstalled)...)
+		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 {
diff --git a/rust/rust.go b/rust/rust.go
index 1517e62..28a300b 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1615,7 +1615,7 @@
 		}
 	}
 
-	if depTag == procMacroDepTag {
+	if depTag == procMacroDepTag || depTag == customBindgenDepTag {
 		return false
 	}
 
@@ -1664,6 +1664,10 @@
 	}
 }
 
+func (c *Module) Partition() string {
+	return ""
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/rust/sanitize.go b/rust/sanitize.go
index a3c5cb5..c68137e 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -189,16 +189,6 @@
 		}
 	}
 
-	// TODO:(b/178369775)
-	// For now sanitizing is only supported on devices
-	if ctx.Os() == android.Android && Bool(s.Fuzzer) {
-		sanitize.Properties.SanitizerEnabled = true
-	}
-
-	if ctx.Os() == android.Android && Bool(s.Address) {
-		sanitize.Properties.SanitizerEnabled = true
-	}
-
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
 	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
 		s.Hwaddress = nil
@@ -219,7 +209,9 @@
 		s.Memtag_heap = nil
 	}
 
-	if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap)) {
+	// TODO:(b/178369775)
+	// For now sanitizing is only supported on devices
+	if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap) || Bool(s.Fuzzer)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 }
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 4be0ace..1b64130 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -80,7 +81,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("com.android.art", "mybootclasspathfragment"),
 
-		java.FixtureConfigureBootJars("com.android.art:mybootlib"),
+		java.PrepareForBootImageConfigTest,
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
@@ -99,7 +100,7 @@
 			bootclasspath_fragment {
 				name: "mybootclasspathfragment",
 				image_name: "art",
-				contents: ["mybootlib"],
+				contents: ["core1", "core2"],
 				apex_available: ["com.android.art"],
 				hidden_api: {
 					split_packages: ["*"],
@@ -113,19 +114,32 @@
 			}
 
 			java_library {
-				name: "mybootlib",
+				name: "core1",
 				srcs: ["Test.java"],
 				system_modules: "none",
 				sdk_version: "none",
 				compile_dex: true,
 				apex_available: ["com.android.art"],
 			}
-		`),
+
+			java_library {
+				name: "core2",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+				apex_available: ["com.android.art"],
+			}
+`),
 	).RunTest(t)
 
 	// A preparer to update the test fixture used when processing an unpackage snapshot.
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
 
+	// Check that source on its own configures the bootImageConfig correctly.
+	java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/meta_lic")
+	java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+
 	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -136,7 +150,10 @@
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
     image_name: "art",
-    contents: ["mybootlib"],
+    contents: [
+        "core1",
+        "core2",
+    ],
     hidden_api: {
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
@@ -148,11 +165,19 @@
 }
 
 java_import {
-    name: "mybootlib",
+    name: "core1",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/core1.jar"],
+}
+
+java_import {
+    name: "core2",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.art"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/core2.jar"],
 }
 `),
 		checkAllCopyRules(`
@@ -162,31 +187,54 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar
 		`),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 
 		// Check the behavior of the snapshot without the source.
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
-			// Make sure that the boot jars package check rule includes the dex jar retrieved from the prebuilt apex.
-			checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/mybootlib.jar")
+			// 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")
+			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")
 		}),
 
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+
+		// Check the behavior of the snapshot when the source is preferred.
+		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/meta_lic")
+			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+		}),
+
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
+
+		// Check the behavior of the snapshot when it is preferred.
+		snapshotTestChecker(checkSnapshotPreferredWithSource, func(t *testing.T, result *android.TestResult) {
+			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/snapshot/prebuilt_mybootclasspathfragment/android_common_com.android.art/meta_lic")
+			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic")
+		}),
 	)
 
-	// Make sure that the boot jars package check rule includes the dex jar created from the source.
-	checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/mybootlib/android_common_apex10000/aligned/mybootlib.jar")
+	// 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")
 }
 
 // checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
 // package check rule.
-func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModule string) {
+func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModules ...string) {
+	t.Helper()
 	platformBcp := result.ModuleForTests("platform-bootclasspath", "android_common")
 	bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
 	command := bootJarsCheckRule.RuleParams.Command
-	expectedCommandArgs := " out/soong/host/linux-x86/bin/dexdump build/soong/scripts/check_boot_jars/package_allowed_list.txt " + expectedModule + " &&"
+	expectedCommandArgs := " build/soong/scripts/check_boot_jars/package_allowed_list.txt " + strings.Join(expectedModules, " ") + " &&"
 	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
 }
 
diff --git a/sdk/build_release.go b/sdk/build_release.go
index ac57a32..6bb05a3 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -100,6 +100,7 @@
 	// Add the dessert build releases from oldest to newest.
 	buildReleaseS = initBuildRelease("S")
 	buildReleaseT = initBuildRelease("Tiramisu")
+	buildReleaseU = initBuildRelease("UpsideDownCake")
 
 	// Add the current build release which is always treated as being more recent than any other
 	// build release, including those added in tests.
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index 13730cb..5bf57b5 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -42,7 +42,7 @@
 		android.AssertDeepEquals(t, "release", (*buildRelease)(nil), release)
 		// Uses a wildcard in the error message to allow for additional build releases to be added to
 		// the supported set without breaking this test.
-		android.FailIfNoMatchingErrors(t, `unknown release "A", expected one of \[S,Tiramisu,F1,F2,current\]`, []error{err})
+		android.FailIfNoMatchingErrors(t, `unknown release "A", expected one of \[S,Tiramisu,UpsideDownCake,F1,F2,current\]`, []error{err})
 	})
 }
 
@@ -60,7 +60,7 @@
 	t.Run("closed range", func(t *testing.T) {
 		set, err := parseBuildReleaseSet("S-F1")
 		android.AssertDeepEquals(t, "errors", nil, err)
-		android.AssertStringEquals(t, "set", "[S,Tiramisu,F1]", set.String())
+		android.AssertStringEquals(t, "set", "[S,Tiramisu,UpsideDownCake,F1]", set.String())
 	})
 	invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
 	t.Run("invalid release", func(t *testing.T) {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index d598834..51903ce3 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -889,6 +889,56 @@
 	)
 }
 
+func TestSnapshotWithJavaSdkLibrary_DistStem(t *testing.T) {
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib-foo"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib-foo",
+			apex_available: ["//apex_available:anyapex"],
+			srcs: ["Test.java"],
+			sdk_version: "current",
+			shared_library: false,
+			public: {
+				enabled: true,
+			},
+			dist_stem: "myjavalib",
+		}
+	`)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "myjavalib-foo",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib-foo.stubs/android_common/javac/myjavalib-foo.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib-foo.stubs.source/android_common/metalava/myjavalib-foo.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib-foo.stubs.source/android_common/metalava/myjavalib-foo.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+`),
+		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+		),
+	)
+}
+
 func TestSnapshotWithJavaSdkLibrary_UseSrcJar(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 8b8e1d7..2f9aee9 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -218,15 +218,16 @@
 }
 
 type testPropertiesStruct struct {
-	name        string
-	private     string
-	Public_Kept string `sdk:"keep"`
-	S_Common    string
-	S_Different string `android:"arch_variant"`
-	A_Common    []string
-	A_Different []string `android:"arch_variant"`
-	F_Common    *bool
-	F_Different *bool `android:"arch_variant"`
+	name          string
+	private       string
+	Public_Ignore string `sdk:"ignore"`
+	Public_Keep   string `sdk:"keep"`
+	S_Common      string
+	S_Different   string `android:"arch_variant"`
+	A_Common      []string
+	A_Different   []string `android:"arch_variant"`
+	F_Common      *bool
+	F_Different   *bool `android:"arch_variant"`
 	EmbeddedPropertiesStruct
 }
 
@@ -244,30 +245,32 @@
 	common := &testPropertiesStruct{name: "common"}
 	structs := []propertiesContainer{
 		&testPropertiesStruct{
-			name:        "struct-0",
-			private:     "common",
-			Public_Kept: "common",
-			S_Common:    "common",
-			S_Different: "upper",
-			A_Common:    []string{"first", "second"},
-			A_Different: []string{"alpha", "beta"},
-			F_Common:    proptools.BoolPtr(false),
-			F_Different: proptools.BoolPtr(false),
+			name:          "struct-0",
+			private:       "common",
+			Public_Ignore: "common",
+			Public_Keep:   "keep",
+			S_Common:      "common",
+			S_Different:   "upper",
+			A_Common:      []string{"first", "second"},
+			A_Different:   []string{"alpha", "beta"},
+			F_Common:      proptools.BoolPtr(false),
+			F_Different:   proptools.BoolPtr(false),
 			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
 				S_Embedded_Common:    "embedded_common",
 				S_Embedded_Different: "embedded_upper",
 			},
 		},
 		&testPropertiesStruct{
-			name:        "struct-1",
-			private:     "common",
-			Public_Kept: "common",
-			S_Common:    "common",
-			S_Different: "lower",
-			A_Common:    []string{"first", "second"},
-			A_Different: []string{"alpha", "delta"},
-			F_Common:    proptools.BoolPtr(false),
-			F_Different: proptools.BoolPtr(true),
+			name:          "struct-1",
+			private:       "common",
+			Public_Ignore: "common",
+			Public_Keep:   "keep",
+			S_Common:      "common",
+			S_Different:   "lower",
+			A_Common:      []string{"first", "second"},
+			A_Different:   []string{"alpha", "delta"},
+			F_Common:      proptools.BoolPtr(false),
+			F_Different:   proptools.BoolPtr(true),
 			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
 				S_Embedded_Common:    "embedded_common",
 				S_Embedded_Different: "embedded_lower",
@@ -282,15 +285,16 @@
 
 	android.AssertDeepEquals(t, "common properties not correct",
 		&testPropertiesStruct{
-			name:        "common",
-			private:     "",
-			Public_Kept: "",
-			S_Common:    "common",
-			S_Different: "",
-			A_Common:    []string{"first", "second"},
-			A_Different: []string(nil),
-			F_Common:    proptools.BoolPtr(false),
-			F_Different: nil,
+			name:          "common",
+			private:       "",
+			Public_Ignore: "",
+			Public_Keep:   "keep",
+			S_Common:      "common",
+			S_Different:   "",
+			A_Common:      []string{"first", "second"},
+			A_Different:   []string(nil),
+			F_Common:      proptools.BoolPtr(false),
+			F_Different:   nil,
 			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
 				S_Embedded_Common:    "embedded_common",
 				S_Embedded_Different: "",
@@ -300,15 +304,16 @@
 
 	android.AssertDeepEquals(t, "updated properties[0] not correct",
 		&testPropertiesStruct{
-			name:        "struct-0",
-			private:     "common",
-			Public_Kept: "common",
-			S_Common:    "",
-			S_Different: "upper",
-			A_Common:    nil,
-			A_Different: []string{"alpha", "beta"},
-			F_Common:    nil,
-			F_Different: proptools.BoolPtr(false),
+			name:          "struct-0",
+			private:       "common",
+			Public_Ignore: "common",
+			Public_Keep:   "keep",
+			S_Common:      "",
+			S_Different:   "upper",
+			A_Common:      nil,
+			A_Different:   []string{"alpha", "beta"},
+			F_Common:      nil,
+			F_Different:   proptools.BoolPtr(false),
 			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
 				S_Embedded_Common:    "",
 				S_Embedded_Different: "embedded_upper",
@@ -318,15 +323,16 @@
 
 	android.AssertDeepEquals(t, "updated properties[1] not correct",
 		&testPropertiesStruct{
-			name:        "struct-1",
-			private:     "common",
-			Public_Kept: "common",
-			S_Common:    "",
-			S_Different: "lower",
-			A_Common:    nil,
-			A_Different: []string{"alpha", "delta"},
-			F_Common:    nil,
-			F_Different: proptools.BoolPtr(true),
+			name:          "struct-1",
+			private:       "common",
+			Public_Ignore: "common",
+			Public_Keep:   "keep",
+			S_Common:      "",
+			S_Different:   "lower",
+			A_Common:      nil,
+			A_Different:   []string{"alpha", "delta"},
+			F_Common:      nil,
+			F_Different:   proptools.BoolPtr(true),
 			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
 				S_Embedded_Common:    "",
 				S_Embedded_Different: "embedded_lower",
diff --git a/sdk/update.go b/sdk/update.go
index 81f3672..92a13fa 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -2172,6 +2172,11 @@
 	// Retrieves the value on which common value optimization will be performed.
 	getter fieldAccessorFunc
 
+	// True if the field should never be cleared.
+	//
+	// This is set to true if and only if the field is annotated with `sdk:"keep"`.
+	keep bool
+
 	// The empty value for the field.
 	emptyValue reflect.Value
 
@@ -2217,8 +2222,8 @@
 			continue
 		}
 
-		// Ignore fields whose value should be kept.
-		if proptools.HasTag(field, "sdk", "keep") {
+		// Ignore fields tagged with sdk:"ignore".
+		if proptools.HasTag(field, "sdk", "ignore") {
 			continue
 		}
 
@@ -2236,6 +2241,8 @@
 			}
 		}
 
+		keep := proptools.HasTag(field, "sdk", "keep")
+
 		// Save a copy of the field index for use in the function.
 		fieldIndex := f
 
@@ -2275,6 +2282,7 @@
 				name,
 				filter,
 				fieldGetter,
+				keep,
 				reflect.Zero(field.Type),
 				proptools.HasTag(field, "android", "arch_variant"),
 			}
@@ -2394,11 +2402,13 @@
 		if commonValue != nil {
 			emptyValue := property.emptyValue
 			fieldGetter(commonStructValue).Set(*commonValue)
-			for i := 0; i < sliceValue.Len(); i++ {
-				container := sliceValue.Index(i).Interface().(propertiesContainer)
-				itemValue := reflect.ValueOf(container.optimizableProperties())
-				fieldValue := fieldGetter(itemValue)
-				fieldValue.Set(emptyValue)
+			if !property.keep {
+				for i := 0; i < sliceValue.Len(); i++ {
+					container := sliceValue.Index(i).Interface().(propertiesContainer)
+					itemValue := reflect.ValueOf(container.optimizableProperties())
+					fieldValue := fieldGetter(itemValue)
+					fieldValue.Set(emptyValue)
+				}
 			}
 		}
 
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 8e5dfe4..809ca3d 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -118,5 +118,6 @@
 	CrateName           string `json:",omitempty"`
 
 	// dependencies
-	Required []string `json:",omitempty"`
+	Required  []string `json:",omitempty"`
+	Overrides []string `json:",omitempty"`
 }
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index 6bc0165..61d131b 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -58,7 +58,7 @@
 ######################
 build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
 
-BAZEL_OUT="$(call_bazel info output_path)"
+BAZEL_OUT="$(call_bazel info --config=bp2build output_path)"
 
 export TARGET_PRODUCT="module_arm"
 call_bazel build --config=bp2build --config=ci --config=android \
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 78af54d..ad21d7d 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -48,6 +48,52 @@
 
 test_bp2build_null_build_with_globs
 
+function test_different_relative_outdir {
+  setup
+  create_mock_bazel
+
+  mkdir -p a
+  touch a/g.txt
+  cat > a/Android.bp <<'EOF'
+filegroup {
+    name: "g",
+    srcs: ["g.txt"],
+    bazel_module: {bp2build_available: true},
+  }
+EOF
+
+  # A directory under $MOCK_TOP
+  outdir=out2
+  trap "rm -rf $outdir" EXIT
+  # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
+  (export OUT_DIR=$outdir; run_soong bp2build && run_bazel build --config=bp2build //a:g)
+}
+
+test_different_relative_outdir
+
+function test_different_absolute_outdir {
+  setup
+  create_mock_bazel
+
+  mkdir -p a
+  touch a/g.txt
+  cat > a/Android.bp <<'EOF'
+filegroup {
+    name: "g",
+    srcs: ["g.txt"],
+    bazel_module: {bp2build_available: true},
+  }
+EOF
+
+  # A directory under /tmp/...
+  outdir=$(mktemp -t -d st.XXXXX)
+  trap 'rm -rf $outdir' EXIT
+  # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
+  (export OUT_DIR=$outdir; run_soong bp2build && run_bazel build --config=bp2build //a:g)
+}
+
+test_different_absolute_outdir
+
 function test_bp2build_generates_all_buildfiles {
   setup
   create_mock_bazel
@@ -114,7 +160,10 @@
   fi
 }
 
+_save_trap=$(trap -p EXIT)
+trap '[[ $? -ne 0 ]] && echo Are you running this locally? Try changing --sandbox_tmpfs_path to something other than /tmp/ in build/bazel/linux.bazelrc.' EXIT
 test_bp2build_generates_all_buildfiles
+eval ${_save_trap}
 
 function test_cc_correctness {
   setup
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index b408fd3..f6fffad 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -14,7 +14,9 @@
   setup
   create_mock_bazel
 
-  STANDALONE_BAZEL=true run_bazel info
+  run_soong bp2build
+
+  run_bazel info --config=bp2build
 }
 
 test_bazel_smoke
diff --git a/ui/build/build.go b/ui/build/build.go
index ff2d5f2..2022e50 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -112,10 +112,6 @@
 // checkBazelMode fails the build if there are conflicting arguments for which bazel
 // build mode to use.
 func checkBazelMode(ctx Context, config Config) {
-	if config.Environment().IsEnvTrue("USE_BAZEL_ANALYSIS") {
-		ctx.Fatalln("USE_BAZEL_ANALYSIS is deprecated. Unset USE_BAZEL_ANALYSIS.\n" +
-			"Use --bazel-mode-dev instead. For example: `m --bazel-mode-dev nothing`")
-	}
 	if config.bazelProdMode && config.bazelDevMode {
 		ctx.Fatalln("Conflicting bazel mode.\n" +
 			"Do not specify both --bazel-mode and --bazel-mode-dev")
@@ -249,41 +245,7 @@
 
 	SetupPath(ctx, config)
 
-	what := RunAll
-	if config.Checkbuild() {
-		what |= RunBuildTests
-	}
-	if config.SkipConfig() {
-		ctx.Verboseln("Skipping Config as requested")
-		what = what &^ RunProductConfig
-	}
-	if config.SkipKati() {
-		ctx.Verboseln("Skipping Kati as requested")
-		what = what &^ RunKati
-	}
-	if config.SkipKatiNinja() {
-		ctx.Verboseln("Skipping use of Kati ninja as requested")
-		what = what &^ RunKatiNinja
-	}
-	if config.SkipSoong() {
-		ctx.Verboseln("Skipping use of Soong as requested")
-		what = what &^ RunSoong
-	}
-
-	if config.SkipNinja() {
-		ctx.Verboseln("Skipping Ninja as requested")
-		what = what &^ RunNinja
-	}
-
-	if !config.SoongBuildInvocationNeeded() {
-		// This means that the output of soong_build is not needed and thus it would
-		// run unnecessarily. In addition, if this code wasn't there invocations
-		// with only special-cased target names like "m bp2build" would result in
-		// passing Ninja the empty target list and it would then build the default
-		// targets which is not what the user asked for.
-		what = what &^ RunNinja
-		what = what &^ RunKati
-	}
+	what := evaluateWhatToRun(config, ctx.Verboseln)
 
 	if config.StartGoma() {
 		startGoma(ctx, config)
@@ -359,6 +321,46 @@
 	}
 }
 
+func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int {
+	//evaluate what to run
+	what := RunAll
+	if config.Checkbuild() {
+		what |= RunBuildTests
+	}
+	if config.SkipConfig() {
+		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() {
+		verboseln("Skipping use of Soong as requested")
+		what = what &^ RunSoong
+	}
+
+	if config.SkipNinja() {
+		verboseln("Skipping Ninja as requested")
+		what = what &^ RunNinja
+	}
+
+	if !config.SoongBuildInvocationNeeded() {
+		// This means that the output of soong_build is not needed and thus it would
+		// run unnecessarily. In addition, if this code wasn't there invocations
+		// with only special-cased target names like "m bp2build" would result in
+		// passing Ninja the empty target list and it would then build the default
+		// targets which is not what the user asked for.
+		what = what &^ RunNinja
+		what = what &^ RunKati
+	}
+	return what
+}
+
 var distWaitGroup sync.WaitGroup
 
 // waitForDist waits for all backgrounded distGzipFile and distFile writes to finish
diff --git a/ui/build/config.go b/ui/build/config.go
index 14a99d0..f6f5b46 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -71,6 +71,7 @@
 	checkbuild      bool
 	dist            bool
 	jsonModuleGraph bool
+	apiBp2build     bool // Generate BUILD files for Soong modules that contribute APIs
 	bp2build        bool
 	queryview       bool
 	reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
@@ -387,13 +388,13 @@
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
-		if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
-			return java17Home
-		}
 		if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
 			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
 		}
-		return java11Home
+		if toolchain17, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN"); ok && toolchain17 != "true" {
+			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN is no longer supported. An OpenJDK 17 toolchain is now the global default.")
+		}
+		return java17Home
 	}()
 	absJavaHome := absPath(ctx, javaHome)
 
@@ -756,6 +757,8 @@
 			c.jsonModuleGraph = true
 		} else if arg == "bp2build" {
 			c.bp2build = true
+		} else if arg == "api_bp2build" {
+			c.apiBp2build = true
 		} else if arg == "queryview" {
 			c.queryview = true
 		} else if arg == "soong_docs" {
@@ -833,7 +836,7 @@
 		return true
 	}
 
-	if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() {
+	if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() && !c.ApiBp2build() {
 		// Command line was empty, the default Ninja target is built
 		return true
 	}
@@ -916,6 +919,10 @@
 	return shared.JoinPath(c.SoongOutDir(), "queryview.marker")
 }
 
+func (c *configImpl) ApiBp2buildMarkerFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "api_bp2build.marker")
+}
+
 func (c *configImpl) ModuleGraphFile() string {
 	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
 }
@@ -957,6 +964,10 @@
 	return c.bp2build
 }
 
+func (c *configImpl) ApiBp2build() bool {
+	return c.apiBp2build
+}
+
 func (c *configImpl) Queryview() bool {
 	return c.queryview
 }
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 6231e52..c3e52c6 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -94,6 +94,9 @@
 }
 
 func startRBE(ctx Context, config Config) {
+	if !config.GoogleProdCredsExist() && prodCredsAuthType(config) {
+		ctx.Fatalf("Unable to start RBE reproxy\nFAILED: Missing LOAS credentials.")
+	}
 	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 	defer ctx.EndTrace()
 
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 3ef77c7..28c6ec9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -45,6 +45,7 @@
 	bp2buildTag        = "bp2build"
 	jsonModuleGraphTag = "modulegraph"
 	queryviewTag       = "queryview"
+	apiBp2buildTag     = "api_bp2build"
 	soongDocsTag       = "soong_docs"
 
 	// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
@@ -237,6 +238,7 @@
 		config.NamedGlobFile(bp2buildTag),
 		config.NamedGlobFile(jsonModuleGraphTag),
 		config.NamedGlobFile(queryviewTag),
+		config.NamedGlobFile(apiBp2buildTag),
 		config.NamedGlobFile(soongDocsTag),
 	}
 }
@@ -307,6 +309,19 @@
 		fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
 	)
 
+	// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
+	// The final workspace will be generated in out/soong/api_bp2build
+	apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build")
+	apiBp2buildInvocation := primaryBuilderInvocation(
+		config,
+		apiBp2buildTag,
+		config.ApiBp2buildMarkerFile(),
+		[]string{
+			"--bazel_api_bp2build_dir", apiBp2buildDir,
+		},
+		fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
+	)
+
 	soongDocsInvocation := primaryBuilderInvocation(
 		config,
 		soongDocsTag,
@@ -345,6 +360,7 @@
 			bp2buildInvocation,
 			jsonModuleGraphInvocation,
 			queryviewInvocation,
+			apiBp2buildInvocation,
 			soongDocsInvocation},
 	}
 
@@ -381,11 +397,14 @@
 	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
 	// For Bazel mixed builds.
 	soongBuildEnv.Set("BAZEL_PATH", "./tools/bazel")
-	soongBuildEnv.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
+	// Bazel's HOME var is set to an output subdirectory which doesn't exist. This
+	// prevents Bazel from file I/O in the actual user HOME directory.
+	soongBuildEnv.Set("BAZEL_HOME", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazelhome")))
 	soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
 	soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
 	soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
 	soongBuildEnv.Set("LOG_DIR", config.LogsDir())
+	soongBuildEnv.Set("BAZEL_DEPS_FILE", filepath.Join(os.Getenv("TOP"), config.OutDir(), "tools", "bazel.list"))
 
 	// For Soong bootstrapping tests
 	if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
@@ -415,6 +434,10 @@
 			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
 		}
 
+		if config.ApiBp2build() {
+			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
+		}
+
 		if config.SoongDocs() {
 			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
 		}
@@ -478,6 +501,10 @@
 		targets = append(targets, config.QueryviewMarkerFile())
 	}
 
+	if config.ApiBp2build() {
+		targets = append(targets, config.ApiBp2buildMarkerFile())
+	}
+
 	if config.SoongDocs() {
 		targets = append(targets, config.SoongDocsHtml())
 	}
diff --git a/xml/Android.bp b/xml/Android.bp
index 1542930..d4753de 100644
--- a/xml/Android.bp
+++ b/xml/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "blueprint-pathtools",
         "soong",
+        "soong-bp2build",
         "soong-android",
         "soong-etc",
     ],
@@ -18,6 +19,7 @@
     ],
     testSrcs: [
         "xml_test.go",
+        "xml_conversion_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/xml/xml.go b/xml/xml.go
index c281078..8c0c072 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/etc"
 
 	"github.com/google/blueprint"
@@ -67,6 +68,8 @@
 }
 
 type prebuiltEtcXml struct {
+	android.BazelModuleBase
+
 	etc.PrebuiltEtc
 
 	properties prebuiltEtcXmlProperties
@@ -129,5 +132,40 @@
 	etc.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
 	// This module is device-only
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitBazelModule(module)
 	return module
 }
+
+type bazelPrebuiltEtcXmlAttributes struct {
+	Src               bazel.LabelAttribute
+	Filename          bazel.LabelAttribute
+	Dir               string
+	Installable       bazel.BoolAttribute
+	Filename_from_src bazel.BoolAttribute
+	Schema            *string
+}
+
+func (p *prebuiltEtcXml) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	baseAttrs := p.PrebuiltEtc.Bp2buildHelper(ctx)
+
+	var schema *string
+	if p.properties.Schema != nil {
+		schema = p.properties.Schema
+	}
+
+	attrs := &bazelPrebuiltEtcXmlAttributes{
+		Src:               baseAttrs.Src,
+		Filename:          baseAttrs.Filename,
+		Dir:               baseAttrs.Dir,
+		Installable:       baseAttrs.Installable,
+		Filename_from_src: baseAttrs.Filename_from_src,
+		Schema:            schema,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "prebuilt_xml",
+		Bzl_load_location: "//build/bazel/rules/prebuilt_xml.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: p.Name()}, attrs)
+}
diff --git a/xml/xml_conversion_test.go b/xml/xml_conversion_test.go
new file mode 100644
index 0000000..6606ddc
--- /dev/null
+++ b/xml/xml_conversion_test.go
@@ -0,0 +1,129 @@
+// Copyright 2022 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 xml
+
+import (
+	"android/soong/android"
+	"android/soong/bp2build"
+
+	"testing"
+)
+
+func runXmlPrebuiltEtcTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "prebuilt_etc_xml"
+	(&tc).ModuleTypeUnderTestFactory = PrebuiltEtcXmlFactory
+	bp2build.RunBp2BuildTestCase(t, registerXmlModuleTypes, tc)
+}
+
+func registerXmlModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestXmlPrebuiltEtcSimple(t *testing.T) {
+	runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+		Description: "prebuilt_etc_xml - simple example",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc_xml {
+    name: "foo",
+    src: "fooSrc",
+    filename: "fooFileName",
+    sub_dir: "fooDir",
+    schema: "foo.dtd",
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+				"src":      `"fooSrc"`,
+				"filename": `"fooFileName"`,
+				"dir":      `"etc/fooDir"`,
+				"schema":   `"foo.dtd"`,
+			})}})
+}
+
+func TestXmlPrebuiltEtcFilenameFromSrc(t *testing.T) {
+	runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+		Description: "prebuilt_etc_xml - filenameFromSrc True  ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc_xml {
+    name: "foo",
+    src: "fooSrc",
+    filename_from_src: true,
+    sub_dir: "fooDir",
+    schema: "foo.dtd",
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+				"src":      `"fooSrc"`,
+				"filename": `"fooSrc"`,
+				"dir":      `"etc/fooDir"`,
+				"schema":   `"foo.dtd"`,
+			})}})
+}
+
+func TestXmlPrebuiltEtcFilenameAndFilenameFromSrc(t *testing.T) {
+	runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+		Description: "prebuilt_etc_xml - filename provided and filenameFromSrc True  ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc_xml {
+    name: "foo",
+    src: "fooSrc",
+    filename: "fooFileName",
+    filename_from_src: true,
+    sub_dir: "fooDir",
+    schema: "foo.dtd",
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+				"src":      `"fooSrc"`,
+				"filename": `"fooFileName"`,
+				"dir":      `"etc/fooDir"`,
+				"schema":   `"foo.dtd"`,
+			})}})
+}
+
+func TestXmlPrebuiltEtcFileNameFromSrcMultipleSrcs(t *testing.T) {
+	runXmlPrebuiltEtcTestCase(t, bp2build.Bp2buildTestCase{
+		Description: "prebuilt_etc - filename_from_src is true but there are multiple srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc_xml {
+    name: "foo",
+    filename_from_src: true,
+    arch: {
+        arm: {
+            src: "barSrc",
+        },
+        arm64: {
+            src: "bazSrc",
+        },
+    }
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTarget("prebuilt_xml", "foo", bp2build.AttrNameToString{
+				"filename_from_src": `True`,
+				"dir":               `"etc"`,
+				"src": `select({
+        "//build/bazel/platforms/arch:arm": "barSrc",
+        "//build/bazel/platforms/arch:arm64": "bazSrc",
+        "//conditions:default": None,
+    })`,
+			})}})
+}
diff --git a/zip/cmd/BUILD.bazel b/zip/cmd/BUILD.bazel
new file mode 100644
index 0000000..e04a1e1
--- /dev/null
+++ b/zip/cmd/BUILD.bazel
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO(b/194644518): Switch to the source version when Bazel can build go
+# binaries.
+alias(
+    name = "soong_zip",
+    actual = "//prebuilts/build-tools:linux-x86/bin/soong_zip",
+)