Merge "Revert "Delete SyspropMutator""
diff --git a/README.md b/README.md
index caffd3d..87d6948 100644
--- a/README.md
+++ b/README.md
@@ -13,8 +13,7 @@
 By design, Android.bp files are very simple.  There are no conditionals or
 control flow statements - any complexity is handled in build logic written in
 Go.  The syntax and semantics of Android.bp files are intentionally similar
-to [Bazel BUILD files](https://www.bazel.io/versions/master/docs/be/overview.html)
-when possible.
+to [Bazel BUILD files](https://bazel.build/concepts/build-files) when possible.
 
 ### Modules
 
@@ -596,14 +595,14 @@
 start the build with `SOONG_DELVE=<listen addr>` in the environment.
 For example:
 ```bash
-SOONG_DELVE=:5006 m nothing
+SOONG_DELVE=5006 m nothing
 ```
 
 To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE`
 variable:
 
 ```
-SOONG_UI_DELVE=:5006 m nothing
+SOONG_UI_DELVE=5006 m nothing
 ```
 
 
diff --git a/android/Android.bp b/android/Android.bp
index f58a472..e0ad58f 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -38,7 +38,9 @@
         "bazel_paths.go",
         "buildinfo_prop.go",
         "config.go",
+        "test_config.go",
         "config_bp2build.go",
+        "configured_jars.go",
         "csuite_config.go",
         "deapexer.go",
         "defaults.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index a5799a5..d4609f4 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -37,8 +37,8 @@
 
 var (
 	Bp2buildDefaultConfig = Bp2BuildConfig{
-		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
 		"art/libartbase":                        Bp2BuildDefaultTrueRecursively,
+		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
 		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
 		"art/libnativebridge":                   Bp2BuildDefaultTrueRecursively,
 		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
@@ -46,19 +46,16 @@
 		"bionic":                                Bp2BuildDefaultTrueRecursively,
 		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
 
-		"build/bazel/examples/apex/minimal":           Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/soong_config_variables": Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/python":                 Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/gensrcs":                Bp2BuildDefaultTrueRecursively,
-		"build/make/target/product/security":          Bp2BuildDefaultTrue,
-		"build/make/tools/signapk":                    Bp2BuildDefaultTrue,
-		"build/make/tools/zipalign":                   Bp2BuildDefaultTrueRecursively,
-		"build/soong":                                 Bp2BuildDefaultTrue,
-		"build/soong/cc/libbuildversion":              Bp2BuildDefaultTrue, // Skip tests subdir
-		"build/soong/cc/ndkstubgen":                   Bp2BuildDefaultTrue,
-		"build/soong/cc/symbolfile":                   Bp2BuildDefaultTrue,
-		"build/soong/linkerconfig":                    Bp2BuildDefaultTrueRecursively,
-		"build/soong/scripts":                         Bp2BuildDefaultTrueRecursively,
+		"build/bazel":                        Bp2BuildDefaultTrueRecursively,
+		"build/make/target/product/security": Bp2BuildDefaultTrue,
+		"build/make/tools/signapk":           Bp2BuildDefaultTrue,
+		"build/make/tools/zipalign":          Bp2BuildDefaultTrueRecursively,
+		"build/soong":                        Bp2BuildDefaultTrue,
+		"build/soong/cc/libbuildversion":     Bp2BuildDefaultTrue, // Skip tests subdir
+		"build/soong/cc/ndkstubgen":          Bp2BuildDefaultTrue,
+		"build/soong/cc/symbolfile":          Bp2BuildDefaultTrue,
+		"build/soong/linkerconfig":           Bp2BuildDefaultTrueRecursively,
+		"build/soong/scripts":                Bp2BuildDefaultTrueRecursively,
 
 		"cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
 		"development/apps/DevelopmentSettings":        Bp2BuildDefaultTrue,
@@ -122,7 +119,10 @@
 		"external/icu":                           Bp2BuildDefaultTrueRecursively,
 		"external/icu/android_icu4j":             Bp2BuildDefaultFalse, // java rules incomplete
 		"external/icu/icu4j":                     Bp2BuildDefaultFalse, // java rules incomplete
+		"external/jacoco":                        Bp2BuildDefaultTrueRecursively,
 		"external/jarjar":                        Bp2BuildDefaultTrueRecursively,
+		"external/javassist":                     Bp2BuildDefaultTrueRecursively,
+		"external/javaparser":                    Bp2BuildDefaultTrueRecursively,
 		"external/javapoet":                      Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                  Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                       Bp2BuildDefaultTrueRecursively,
@@ -137,10 +137,13 @@
 		"external/libjpeg-turbo":                 Bp2BuildDefaultTrueRecursively,
 		"external/libmpeg2":                      Bp2BuildDefaultTrueRecursively,
 		"external/libpng":                        Bp2BuildDefaultTrueRecursively,
+		"external/libvpx":                        Bp2BuildDefaultTrueRecursively,
+		"external/libyuv":                        Bp2BuildDefaultTrueRecursively,
 		"external/lz4/lib":                       Bp2BuildDefaultTrue,
 		"external/lzma/C":                        Bp2BuildDefaultTrueRecursively,
 		"external/mdnsresponder":                 Bp2BuildDefaultTrueRecursively,
 		"external/minijail":                      Bp2BuildDefaultTrueRecursively,
+		"external/openscreen":                    Bp2BuildDefaultTrueRecursively,
 		"external/pcre":                          Bp2BuildDefaultTrueRecursively,
 		"external/protobuf":                      Bp2BuildDefaultTrueRecursively,
 		"external/python/six":                    Bp2BuildDefaultTrueRecursively,
@@ -148,14 +151,18 @@
 		"external/scudo":                         Bp2BuildDefaultTrueRecursively,
 		"external/selinux/libselinux":            Bp2BuildDefaultTrueRecursively,
 		"external/selinux/libsepol":              Bp2BuildDefaultTrueRecursively,
+		"external/speex":                         Bp2BuildDefaultTrueRecursively,
 		"external/toybox":                        Bp2BuildDefaultTrueRecursively,
 		"external/zlib":                          Bp2BuildDefaultTrueRecursively,
 		"external/zopfli":                        Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                          Bp2BuildDefaultTrueRecursively,
 
+		"frameworks/av":                                      Bp2BuildDefaultTrue,
 		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/liberror":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
+		"frameworks/base/services/tests/servicestests/aidl":  Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
 		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
@@ -169,7 +176,39 @@
 		"frameworks/native/opengl/tests/testViewport":        Bp2BuildDefaultTrue,
 		"frameworks/proto_logging/stats/stats_log_api_gen":   Bp2BuildDefaultTrueRecursively,
 
-		"libnativehelper":                                  Bp2BuildDefaultTrueRecursively,
+		"hardware/interfaces":                          Bp2BuildDefaultTrue,
+		"hardware/interfaces/common/aidl":              Bp2BuildDefaultTrue,
+		"hardware/interfaces/configstore/1.0":          Bp2BuildDefaultTrue,
+		"hardware/interfaces/configstore/1.1":          Bp2BuildDefaultTrue,
+		"hardware/interfaces/configstore/utils":        Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/aidl":  Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/2.0":   Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/3.0":   Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/4.0":   Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/bufferqueue/1.0": Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/bufferqueue/2.0": Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/1.0":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/1.1":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/1.2":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/common/aidl":     Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/2.0":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/2.1":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/3.0":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/mapper/4.0":      Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/1.0":                Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/bufferpool/2.0":     Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/c2/1.0":             Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/c2/1.1":             Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/c2/1.2":             Bp2BuildDefaultTrue,
+		"hardware/interfaces/media/omx/1.0":            Bp2BuildDefaultTrue,
+		"hardware/interfaces/neuralnetworks/1.0":       Bp2BuildDefaultTrue,
+		"hardware/interfaces/neuralnetworks/1.1":       Bp2BuildDefaultTrue,
+		"hardware/interfaces/neuralnetworks/1.2":       Bp2BuildDefaultTrue,
+		"hardware/interfaces/neuralnetworks/1.3":       Bp2BuildDefaultTrue,
+		"hardware/interfaces/neuralnetworks/aidl":      Bp2BuildDefaultTrue,
+
+		"libnativehelper": Bp2BuildDefaultTrueRecursively,
+
 		"packages/apps/DevCamera":                          Bp2BuildDefaultTrue,
 		"packages/apps/HTMLViewer":                         Bp2BuildDefaultTrue,
 		"packages/apps/Protips":                            Bp2BuildDefaultTrue,
@@ -183,9 +222,11 @@
 		"packages/modules/adb/pairing_connection":          Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/proto":                       Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/tls":                         Bp2BuildDefaultTrueRecursively,
-		"packages/providers/MediaProvider/tools/dialogs":   Bp2BuildDefaultTrue,
+		"packages/providers/MediaProvider/tools/dialogs":   Bp2BuildDefaultFalse, // TODO(b/242834374)
 		"packages/screensavers/Basic":                      Bp2BuildDefaultTrue,
-		"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultTrue,
+		"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
+
+		"platform_testing/tests/example": Bp2BuildDefaultTrueRecursively,
 
 		"prebuilts/clang/host/linux-x86":           Bp2BuildDefaultTrueRecursively,
 		"prebuilts/runtime/mainline/platform/sdk":  Bp2BuildDefaultTrueRecursively,
@@ -208,6 +249,7 @@
 		"system/core/libprocessgroup/cgrouprc":                   Bp2BuildDefaultTrue,
 		"system/core/libprocessgroup/cgrouprc_format":            Bp2BuildDefaultTrue,
 		"system/core/libsystem":                                  Bp2BuildDefaultTrueRecursively,
+		"system/core/libsysutils":                                Bp2BuildDefaultTrueRecursively,
 		"system/core/libutils":                                   Bp2BuildDefaultTrueRecursively,
 		"system/core/libvndksupport":                             Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser":     Bp2BuildDefaultTrueRecursively,
@@ -215,21 +257,32 @@
 		"system/libartpalette":                                   Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                                         Bp2BuildDefaultTrueRecursively,
 		"system/libfmq":                                          Bp2BuildDefaultTrue,
+		"system/libhidl/libhidlmemory":                           Bp2BuildDefaultTrue,
+		"system/libhidl/transport":                               Bp2BuildDefaultTrue,
+		"system/libhidl/transport/allocator/1.0":                 Bp2BuildDefaultTrue,
 		"system/libhidl/transport/base/1.0":                      Bp2BuildDefaultTrue,
 		"system/libhidl/transport/manager/1.0":                   Bp2BuildDefaultTrue,
 		"system/libhidl/transport/manager/1.1":                   Bp2BuildDefaultTrue,
 		"system/libhidl/transport/manager/1.2":                   Bp2BuildDefaultTrue,
+		"system/libhidl/transport/memory/1.0":                    Bp2BuildDefaultTrue,
+		"system/libhidl/transport/memory/token/1.0":              Bp2BuildDefaultTrue,
+		"system/libhidl/transport/safe_union/1.0":                Bp2BuildDefaultTrue,
+		"system/libhidl/transport/token/1.0":                     Bp2BuildDefaultTrue,
+		"system/libhidl/transport/token/1.0/utils":               Bp2BuildDefaultTrue,
 		"system/libhwbinder":                                     Bp2BuildDefaultTrueRecursively,
 		"system/libprocinfo":                                     Bp2BuildDefaultTrue,
 		"system/libziparchive":                                   Bp2BuildDefaultTrueRecursively,
-		"system/logging/liblog":                                  Bp2BuildDefaultTrueRecursively,
+		"system/logging":                                         Bp2BuildDefaultTrueRecursively,
 		"system/media/audio":                                     Bp2BuildDefaultTrueRecursively,
+		"system/media/audio_utils":                               Bp2BuildDefaultTrueRecursively,
 		"system/memory/libion":                                   Bp2BuildDefaultTrueRecursively,
 		"system/memory/libmemunreachable":                        Bp2BuildDefaultTrueRecursively,
 		"system/sepolicy/apex":                                   Bp2BuildDefaultTrueRecursively,
+		"system/testing/gtest_extras":                            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":                                   Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":                            Bp2BuildDefaultTrueRecursively,
 		"system/tools/sysprop":                                   Bp2BuildDefaultTrue,
+		"system/tools/aidl/build/tests_bp2build":                 Bp2BuildDefaultTrue,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
 		"tools/apksig": Bp2BuildDefaultTrue,
@@ -241,34 +294,22 @@
 		// This is actually build/bazel/build.BAZEL symlinked to ./BUILD
 		".":/*recursive = */ false,
 
-		// build/bazel/examples/apex/... BUILD files should be generated, so
-		// build/bazel is not recursive. Instead list each subdirectory under
-		// build/bazel explicitly.
-		"build/bazel":/* recursive = */ false,
-		"build/bazel/ci/dist":/* recursive = */ false,
-		"build/bazel/examples/android_app":/* recursive = */ true,
-		"build/bazel/examples/java":/* recursive = */ true,
-		"build/bazel/examples/partitions":/* recursive = */ true,
-		"build/bazel/bazel_skylib":/* recursive = */ true,
-		"build/bazel/rules":/* recursive = */ true,
-		"build/bazel/rules_cc":/* recursive = */ true,
-		"build/bazel/scripts":/* recursive = */ true,
-		"build/bazel/tests":/* recursive = */ true,
-		"build/bazel/platforms":/* recursive = */ true,
-		"build/bazel/product_config":/* recursive = */ true,
-		"build/bazel/product_variables":/* recursive = */ true,
-		"build/bazel/vendor/google":/* recursive = */ true,
+		"build/bazel":/* recursive = */ true,
 		"build/bazel_common_rules":/* recursive = */ true,
 		// 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,
 
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
+		"external/bazelbuild-kotlin-rules":/* recursive = */ true,
 		"external/bazel-skylib":/* recursive = */ true,
 		"external/guava":/* recursive = */ true,
 		"external/jsr305":/* recursive = */ true,
+		"external/protobuf":/* recursive = */ false,
+		"frameworks/base/tools/codegen":/* recursive = */ true,
 		"frameworks/ex/common":/* recursive = */ true,
 
 		"packages/apps/Music":/* recursive = */ true,
@@ -276,6 +317,7 @@
 
 		"prebuilts/bazel":/* recursive = */ true,
 		"prebuilts/bundletool":/* recursive = */ true,
+		"prebuilts/clang/host/linux-x86":/* recursive = */ false,
 		"prebuilts/gcc":/* recursive = */ true,
 		"prebuilts/build-tools":/* recursive = */ true,
 		"prebuilts/jdk/jdk11":/* recursive = */ false,
@@ -290,6 +332,7 @@
 		"code_coverage.policy",
 		"code_coverage.policy.other",
 		"codec2_soft_exports",
+		"codecs_g711dec",
 		"com.android.media.swcodec-androidManifest",
 		"com.android.media.swcodec-ld.config.txt",
 		"com.android.media.swcodec-mediaswcodec.rc",
@@ -301,12 +344,16 @@
 		"flatbuffer_headers",
 		"gemmlowp_headers",
 		"gl_headers",
+		"libaidlcommonsupport",
 		"libandroid_runtime_lazy",
 		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
-		"libaudioutils_fixedfft",
+		"libbinder",
+		"libbinder_device_interface_sources",
+		"libbinder_aidl",
 		"libbinder_headers",
 		"libbinder_headers_platform_shared",
+		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
 		"libbufferhub_headers",
 		"libcodec2",
@@ -316,6 +363,10 @@
 		"libdvr_headers",
 		"libgsm",
 		"libgui_bufferqueue_sources",
+		"libgrallocusage",
+		"libgralloctypes",
+		"libnativewindow",
+		"libgraphicsenv",
 		"libhardware",
 		"libhardware_headers",
 		"libincfs_headers",
@@ -326,16 +377,31 @@
 		"libpdx_headers",
 		"libprocpartition",
 		"libruy_static",
+		"libandroidio",
+		"libandroidio_srcs",
 		"libserviceutils",
+		"libstagefright_amrnbenc",
+		"libstagefright_amrnbdec",
+		"libstagefright_amrwbdec",
+		"libstagefright_amrwbenc",
+		"libstagefright_amrnb_common",
 		"libstagefright_enc_common",
+		"libstagefright_flacdec",
+		"libstagefright_foundation",
 		"libstagefright_foundation_headers",
 		"libstagefright_headers",
+		"libstagefright_m4vh263dec",
+		"libstagefright_m4vh263enc",
+		"libstagefright_mp3dec",
+		"libstagefright_mp3dec_headers",
 		"libsurfaceflinger_headers",
 		"libsync",
 		"libtextclassifier_hash_headers",
 		"libtextclassifier_hash_static",
 		"libtflite_kernel_utils",
 		"libtinyxml2",
+		"libgui_aidl",
+		"libui",
 		"libui-types",
 		"libui_headers",
 		"libvorbisidec",
@@ -343,11 +409,22 @@
 		"media_plugin_headers",
 		"mediaswcodec.policy",
 		"mediaswcodec.xml",
+		"neuralnetworks_types",
+		"neuralnetworks_utils_hal_common",
+		"neuralnetworks_utils_hal_1_0",
+		"neuralnetworks_utils_hal_1_1",
+		"neuralnetworks_utils_hal_1_2",
+		"neuralnetworks_utils_hal_1_3",
+		"libneuralnetworks_common",
 		"philox_random",
 		"philox_random_headers",
 		"server_configurable_flags",
 		"tensorflow_headers",
 
+		"libgui_headers",
+		"libstagefright_bufferpool@2.0",
+		"libstagefright_bufferpool@2.0.1",
+
 		// fastboot
 		"bootimg_headers",
 		"fastboot",
@@ -390,25 +467,53 @@
 		//system/libhidl
 		// needed by cc_hidl_library
 		"libhidlbase",
+
+		//frameworks/native
+		"framework_native_aidl_binder",
+		"framework_native_aidl_gui",
+
+		//frameworks/native/libs/input
+		"inputconstants_aidl",
+
+		// needed for aidl_interface's ndk backend
+		"libbinder_ndk",
+
+		"libusb",
+
+		// needed by liblogd
+		"ILogcatManagerService_aidl",
+		"libincremental_aidl-cpp",
+		"incremental_aidl",
+
+		//frameworks/native/cmds/cmd
+		"libcmd",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
 		"linker_config",
 		"java_import",
 		"java_import_host",
+		"sysprop_library",
+		"aidl_interface_headers",
 	}
 
 	Bp2buildModuleDoNotConvertList = []string{
 		// cc bugs
-		"libactivitymanager_aidl",                   // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
-		"gen-kotlin-build-file.py",                  // TODO(b/198619163) module has same name as source
+		"libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
+
+		// TODO(b/198619163) module has same name as source
+		"logtagd.rc",
+
 		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
-		"linkerconfig", "mdnsd", // TODO(b/202876379): has arch-variant static_executable
+
+		// TODO(b/202876379): has arch-variant static_executable
+		"linkerconfig",
+		"mdnsd",
+		"libcutils_test_static",
+		"KernelLibcutilsTest",
+
 		"linker",                 // TODO(b/228316882): cc_binary uses link_crt
-		"libdebuggerd",           // TODO(b/228314770): support product variable-specific header_libs
 		"versioner",              // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
-		"libspeexresampler",      // TODO(b/231995978): Filter out unknown cflags
-		"libvpx",                 // TODO(b/240756936): Arm neon variant not supported
 		"art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers
 		"apexer_test",            // Requires aapt2
 		"apexer_test_host_tools",
@@ -461,7 +566,6 @@
 
 		// unconverted deps
 		"CarHTMLViewer",                                              // depends on unconverted modules android.car-stubs, car-ui-lib
-		"abb",                                                        // depends on unconverted modules: libcmd, libbinder
 		"adb",                                                        // depends on unconverted modules: AdbWinApi, libandroidfw, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
 		"android_icu4j_srcgen",                                       // depends on unconverted modules: currysrc
 		"android_icu4j_srcgen_binary",                                // depends on unconverted modules: android_icu4j_srcgen, currysrc
@@ -474,7 +578,7 @@
 		"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
 		"host-libprotobuf-java-nano",                                 // b/220869005, depends on libprotobuf-java-nano
-		"libadb_host",                                                // depends on unconverted modules: AdbWinApi, libopenscreen-discovery, libopenscreen-platform-impl, libusb
+		"jacoco-stubs",                                               // b/245767077, depends on droidstubs
 		"libapexutil",                                                // depends on unconverted modules: apex-info-list-tinyxml
 		"libart",                                                     // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
 		"libart-runtime-gtest",                                       // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libartbase-art-gtest
@@ -483,10 +587,9 @@
 		"libartbased-art-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
 		"libartd",                                                    // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
 		"libartd-runtime-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest
+		"libdebuggerd",                                               // depends on unconverted module: libdexfile
 		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
 		"libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
-		"libdexfile",                                              // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
-		"libdexfile_static",                                       // depends on unconverted modules: libartbase, libdexfile
 		"libdexfiled",                                             // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
 		"libfastdeploy_host",                                      // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
 		"libgmock_main_ndk",                                       // depends on unconverted modules: libgtest_ndk_c++
@@ -494,7 +597,7 @@
 		"libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libnativetesthelper_jni, libgmock_ndk
 		"libnativetesthelper_jni",   // depends on unconverted modules: libgtest_ndk_c++
 		"libprotobuf-java-nano",     // b/220869005, depends on non-public_current SDK
-		"libstatslog",               // depends on unconverted modules: libstatspull, statsd-aidl-ndk, libbinder_ndk
+		"libstatslog",               // depends on unconverted modules: libstatspull, statsd-aidl-ndk
 		"libstatslog_art",           // depends on unconverted modules: statslog_art.cpp, statslog_art.h
 		"linker_reloc_bench_main",   // depends on unconverted modules: liblinker_reloc_bench_*
 		"pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
@@ -508,12 +611,462 @@
 		"truth-host-prebuilt", // depends on unconverted modules: truth-prebuilt
 		"truth-prebuilt",      // depends on unconverted modules: asm-7.0, guava
 
-		// b/215723302; awaiting tz{data,_version} to then rename targets conflicting with srcs
-		"tzdata",
-		"tz_version",
-
 		// '//bionic/libc:libc_bp2build_cc_library_static' is duplicated in the 'deps' attribute of rule
 		"toybox-static",
+
+		// cc_test related.
+		// Failing host cc_tests
+		"memunreachable_unit_test",
+		"libprocinfo_test",
+		"ziparchive-tests",
+		"gtest_isolated_tests",
+		"libunwindstack_unit_test",
+		"task_profiles_test",
+		"power_tests", // failing test on server, but not on host
+
+		// reflect: call of reflect.Value.NumField on interface Value
+		// affects all cc_tests that depend on art_defaults
+		"libnativebridge-tests",
+		"libnativeloader_test",
+		"art_libnativebridge_cts_tests",
+		"art_standalone_libdexfile_external_tests",
+		"art_standalone_libdexfile_support_tests",
+		"libnativebridge-lazy-tests",
+		"libnativebridge-test-case",
+		"libnativebridge2-test-case",
+		"libnativebridge3-test-case",
+		"libnativebridge6-test-case",
+		"libnativebridge6prezygotefork",
+
+		// cc_test with unconverted deps, or are device-only (and not verified to pass yet)
+		"AMRWBEncTest",
+		"AmrnbDecoderTest",     // depends on unconverted modules: libaudioutils, libsndfile
+		"AmrnbEncoderTest",     // depends on unconverted modules: libaudioutils, libsndfile
+		"AmrwbDecoderTest",     // depends on unconverted modules: libsndfile, libaudioutils
+		"AmrwbEncoderTest",     // depends on unconverted modules: libaudioutils, libsndfile
+		"Mp3DecoderTest",       // depends on unconverted modules: libsndfile, libaudioutils
+		"Mpeg4H263DecoderTest", // depends on unconverted modules: libstagefright_foundation
+		"Mpeg4H263EncoderTest",
+		"avcdec",
+		"avcenc",
+		"bionic-benchmarks-tests",
+		"bionic-fortify-runtime-asan-test",
+		"bionic-stress-tests",
+		"bionic-unit-tests",
+		"bionic-unit-tests-glibc",
+		"bionic-unit-tests-static",
+		"boringssl_crypto_test",
+		"boringssl_ssl_test",
+		"cfi_test_helper",
+		"cfi_test_helper2",
+		"cintltst32",
+		"cintltst64",
+		"compare",
+		"cpuid",
+		"debuggerd_test", // depends on unconverted modules: libdebuggerd
+		"elftls_dlopen_ie_error_helper",
+		"exec_linker_helper",
+		"fastdeploy_test", // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb
+		"fdtrack_test",
+		"google-benchmark-test",
+		"googletest-param-test-test_ndk", // depends on unconverted modules: libgtest_ndk_c++
+		"gtest-typed-test_test",
+		"gtest-typed-test_test_ndk", // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++
+		"gtest_ndk_tests",           // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++
+		"gtest_ndk_tests_no_main",   // depends on unconverted modules: libgtest_ndk_c++
+		"gtest_prod_test_ndk",       // depends on unconverted modules: libgtest_ndk_c++, libgtest_main_ndk_c++
+		"gtest_tests",
+		"gtest_tests_no_main",
+		"gwp_asan_unittest",
+		"half_test",
+		"hashcombine_test",
+		"hevcdec",
+		"hevcenc",
+		"hwbinderThroughputTest", // depends on unconverted modules: android.hardware.tests.libhwbinder@1.0-impl.test, android.hardware.tests.libhwbinder@1.0
+		"i444tonv12_eg",
+		"icu4c_sample_break",
+		"intltest32",
+		"intltest64",
+		"ion-unit-tests",
+		"jemalloc5_integrationtests",
+		"jemalloc5_unittests",
+		"ld_config_test_helper",
+		"ld_preload_test_helper",
+		"libBionicCtsGtestMain", // depends on unconverted modules: libgtest_isolated
+		"libBionicLoaderTests",  // depends on unconverted modules: libmeminfo
+		"libapexutil_tests",     // depends on unconverted modules: apex-info-list-tinyxml, libapexutil
+		"libavservices_minijail_unittest",
+		"libcutils_sockets_test",
+		"libexpectedutils_test",
+		"libhwbinder_latency",
+		"liblog-host-test", // failing tests
+		"libminijail_test",
+		"libminijail_unittest_gtest",
+		"libpackagelistparser_test",
+		"libprotobuf_vendor_suffix_test",
+		"libstagefright_amrnbdec_test", // depends on unconverted modules: libsndfile, libaudioutils
+		"libstagefright_amrnbenc_test",
+		"libstagefright_amrwbdec_test", // depends on unconverted modules: libsndfile, libaudioutils
+		"libstagefright_m4vh263enc_test",
+		"libstagefright_mp3dec_test", // depends on unconverted modules: libsndfile, libaudioutils
+		"libstatssocket_test",
+		"libvndksupport-tests",
+		"libyuv_unittest",
+		"linker-unit-tests",
+		"malloc_debug_system_tests",
+		"malloc_debug_unit_tests",
+		"malloc_hooks_system_tests",
+		"mat_test",
+		"mathtest",
+		"memunreachable_binder_test", // depends on unconverted modules: libbinder
+		"memunreachable_test",
+		"metadata_tests",
+		"minijail0_cli_unittest_gtest",
+		"mpeg2dec",
+		"mvcdec",
+		"ns_hidden_child_helper",
+		"pngtest",
+		"preinit_getauxval_test_helper",
+		"preinit_syscall_test_helper",
+		"psnr",
+		"quat_test",
+		"rappor-tests", // depends on unconverted modules: jsr305, guava
+		"scudo_unit_tests",
+		"stats-log-api-gen-test", // depends on unconverted modules: libstats_proto_host
+		"syscall_filter_unittest_gtest",
+		"sysprop_test", // depends on unconverted modules: libcom.android.sysprop.tests
+		"thread_exit_cb_helper",
+		"tls_properties_helper",
+		"ulp",
+		"vec_test",
+		"yuvconstants",
+		"yuvconvert",
+		"zipalign_tests",
+
+		// cc_test_library
+		"clang_diagnostic_tests",
+		"exec_linker_helper_lib",
+		"fortify_disabled_for_tidy",
+		"ld_config_test_helper_lib1",
+		"ld_config_test_helper_lib2",
+		"ld_config_test_helper_lib3",
+		"ld_preload_test_helper_lib1",
+		"ld_preload_test_helper_lib2",
+		"libBionicElfTlsLoaderTests",
+		"libBionicElfTlsTests",
+		"libBionicElfTlsTests",
+		"libBionicFramePointerTests",
+		"libBionicFramePointerTests",
+		"libBionicStandardTests",
+		"libBionicStandardTests",
+		"libBionicTests",
+		"libart-broken",
+		"libatest_simple_zip",
+		"libcfi-test",
+		"libcfi-test-bad",
+		"libcrash_test",
+		// "libcrypto_fuzz_unsafe",
+		"libdl_preempt_test_1",
+		"libdl_preempt_test_2",
+		"libdl_test_df_1_global",
+		"libdlext_test",
+		"libdlext_test_different_soname",
+		"libdlext_test_fd",
+		"libdlext_test_norelro",
+		"libdlext_test_recursive",
+		"libdlext_test_zip",
+		"libfortify1-new-tests-clang",
+		"libfortify1-new-tests-clang",
+		"libfortify1-tests-clang",
+		"libfortify1-tests-clang",
+		"libfortify2-new-tests-clang",
+		"libfortify2-new-tests-clang",
+		"libfortify2-tests-clang",
+		"libfortify2-tests-clang",
+		"libgnu-hash-table-library",
+		"libicutest_static",
+		"liblinker_reloc_bench_000",
+		"liblinker_reloc_bench_001",
+		"liblinker_reloc_bench_002",
+		"liblinker_reloc_bench_003",
+		"liblinker_reloc_bench_004",
+		"liblinker_reloc_bench_005",
+		"liblinker_reloc_bench_006",
+		"liblinker_reloc_bench_007",
+		"liblinker_reloc_bench_008",
+		"liblinker_reloc_bench_009",
+		"liblinker_reloc_bench_010",
+		"liblinker_reloc_bench_011",
+		"liblinker_reloc_bench_012",
+		"liblinker_reloc_bench_013",
+		"liblinker_reloc_bench_014",
+		"liblinker_reloc_bench_015",
+		"liblinker_reloc_bench_016",
+		"liblinker_reloc_bench_017",
+		"liblinker_reloc_bench_018",
+		"liblinker_reloc_bench_019",
+		"liblinker_reloc_bench_020",
+		"liblinker_reloc_bench_021",
+		"liblinker_reloc_bench_022",
+		"liblinker_reloc_bench_023",
+		"liblinker_reloc_bench_024",
+		"liblinker_reloc_bench_025",
+		"liblinker_reloc_bench_026",
+		"liblinker_reloc_bench_027",
+		"liblinker_reloc_bench_028",
+		"liblinker_reloc_bench_029",
+		"liblinker_reloc_bench_030",
+		"liblinker_reloc_bench_031",
+		"liblinker_reloc_bench_032",
+		"liblinker_reloc_bench_033",
+		"liblinker_reloc_bench_034",
+		"liblinker_reloc_bench_035",
+		"liblinker_reloc_bench_036",
+		"liblinker_reloc_bench_037",
+		"liblinker_reloc_bench_038",
+		"liblinker_reloc_bench_039",
+		"liblinker_reloc_bench_040",
+		"liblinker_reloc_bench_041",
+		"liblinker_reloc_bench_042",
+		"liblinker_reloc_bench_043",
+		"liblinker_reloc_bench_044",
+		"liblinker_reloc_bench_045",
+		"liblinker_reloc_bench_046",
+		"liblinker_reloc_bench_047",
+		"liblinker_reloc_bench_048",
+		"liblinker_reloc_bench_049",
+		"liblinker_reloc_bench_050",
+		"liblinker_reloc_bench_051",
+		"liblinker_reloc_bench_052",
+		"liblinker_reloc_bench_053",
+		"liblinker_reloc_bench_054",
+		"liblinker_reloc_bench_055",
+		"liblinker_reloc_bench_056",
+		"liblinker_reloc_bench_057",
+		"liblinker_reloc_bench_058",
+		"liblinker_reloc_bench_059",
+		"liblinker_reloc_bench_060",
+		"liblinker_reloc_bench_061",
+		"liblinker_reloc_bench_062",
+		"liblinker_reloc_bench_063",
+		"liblinker_reloc_bench_064",
+		"liblinker_reloc_bench_065",
+		"liblinker_reloc_bench_066",
+		"liblinker_reloc_bench_067",
+		"liblinker_reloc_bench_068",
+		"liblinker_reloc_bench_069",
+		"liblinker_reloc_bench_070",
+		"liblinker_reloc_bench_071",
+		"liblinker_reloc_bench_072",
+		"liblinker_reloc_bench_073",
+		"liblinker_reloc_bench_074",
+		"liblinker_reloc_bench_075",
+		"liblinker_reloc_bench_076",
+		"liblinker_reloc_bench_077",
+		"liblinker_reloc_bench_078",
+		"liblinker_reloc_bench_079",
+		"liblinker_reloc_bench_080",
+		"liblinker_reloc_bench_081",
+		"liblinker_reloc_bench_082",
+		"liblinker_reloc_bench_083",
+		"liblinker_reloc_bench_084",
+		"liblinker_reloc_bench_085",
+		"liblinker_reloc_bench_086",
+		"liblinker_reloc_bench_087",
+		"liblinker_reloc_bench_088",
+		"liblinker_reloc_bench_089",
+		"liblinker_reloc_bench_090",
+		"liblinker_reloc_bench_091",
+		"liblinker_reloc_bench_092",
+		"liblinker_reloc_bench_093",
+		"liblinker_reloc_bench_094",
+		"liblinker_reloc_bench_095",
+		"liblinker_reloc_bench_096",
+		"liblinker_reloc_bench_097",
+		"liblinker_reloc_bench_098",
+		"liblinker_reloc_bench_099",
+		"liblinker_reloc_bench_100",
+		"liblinker_reloc_bench_101",
+		"liblinker_reloc_bench_102",
+		"liblinker_reloc_bench_103",
+		"liblinker_reloc_bench_104",
+		"liblinker_reloc_bench_105",
+		"liblinker_reloc_bench_106",
+		"liblinker_reloc_bench_107",
+		"liblinker_reloc_bench_108",
+		"liblinker_reloc_bench_109",
+		"liblinker_reloc_bench_110",
+		"liblinker_reloc_bench_111",
+		"liblinker_reloc_bench_112",
+		"liblinker_reloc_bench_113",
+		"liblinker_reloc_bench_114",
+		"liblinker_reloc_bench_115",
+		"liblinker_reloc_bench_116",
+		"liblinker_reloc_bench_117",
+		"liblinker_reloc_bench_118",
+		"liblinker_reloc_bench_119",
+		"liblinker_reloc_bench_120",
+		"liblinker_reloc_bench_121",
+		"liblinker_reloc_bench_122",
+		"liblinker_reloc_bench_123",
+		"liblinker_reloc_bench_124",
+		"liblinker_reloc_bench_125",
+		"liblinker_reloc_bench_126",
+		"liblinker_reloc_bench_127",
+		"liblinker_reloc_bench_128",
+		"liblinker_reloc_bench_129",
+		"liblinker_reloc_bench_130",
+		"liblinker_reloc_bench_131",
+		"liblinker_reloc_bench_132",
+		"liblinker_reloc_bench_133",
+		"liblinker_reloc_bench_134",
+		"liblinker_reloc_bench_135",
+		"liblinker_reloc_bench_136",
+		"liblinker_reloc_bench_137",
+		"liblinker_reloc_bench_138",
+		"liblinker_reloc_bench_139",
+		"liblinker_reloc_bench_140",
+		"liblinker_reloc_bench_141",
+		"liblinker_reloc_bench_142",
+		"liblinker_reloc_bench_143",
+		"liblinker_reloc_bench_144",
+		"liblinker_reloc_bench_145",
+		"liblinker_reloc_bench_146",
+		"liblinker_reloc_bench_147",
+		"liblinker_reloc_bench_148",
+		"liblinker_reloc_bench_149",
+		"liblinker_reloc_bench_150",
+		"liblinker_reloc_bench_151",
+		"liblinker_reloc_bench_152",
+		"liblinker_reloc_bench_153",
+		"liblinker_reloc_bench_154",
+		"liblinker_reloc_bench_155",
+		"liblinker_reloc_bench_156",
+		"liblinker_reloc_bench_157",
+		"liblinker_reloc_bench_158",
+		"liblinker_reloc_bench_159",
+		"liblinker_reloc_bench_160",
+		"liblinker_reloc_bench_161",
+		"liblinker_reloc_bench_162",
+		"liblinker_reloc_bench_163",
+		"liblinker_reloc_bench_164",
+		"liblinker_reloc_bench_165",
+		"liblinker_reloc_bench_166",
+		"liblinker_reloc_bench_167",
+		"liblinker_reloc_bench_168",
+		"libns_hidden_child_app",
+		"libns_hidden_child_global",
+		"libns_hidden_child_internal",
+		"libns_hidden_child_public",
+		"libnstest_dlopened",
+		"libnstest_ns_a_public1",
+		"libnstest_ns_a_public1_internal",
+		"libnstest_ns_b_public2",
+		"libnstest_ns_b_public3",
+		"libnstest_private",
+		"libnstest_private_external",
+		"libnstest_public",
+		"libnstest_public_internal",
+		"libnstest_root",
+		"libnstest_root_not_isolated",
+		"librelocations-ANDROID_REL",
+		"librelocations-ANDROID_RELR",
+		"librelocations-RELR",
+		"librelocations-fat",
+		"libsegment_gap_inner",
+		"libsegment_gap_outer",
+		// "libssl_fuzz_unsafe",
+		"libstatssocket_private",
+		"libsysv-hash-table-library",
+		"libtest_atexit",
+		"libtest_check_order_dlsym",
+		"libtest_check_order_dlsym_1_left",
+		"libtest_check_order_dlsym_2_right",
+		"libtest_check_order_dlsym_3_c",
+		"libtest_check_order_dlsym_a",
+		"libtest_check_order_dlsym_b",
+		"libtest_check_order_dlsym_d",
+		"libtest_check_order_reloc_root",
+		"libtest_check_order_reloc_root_1",
+		"libtest_check_order_reloc_root_2",
+		"libtest_check_order_reloc_siblings",
+		"libtest_check_order_reloc_siblings_1",
+		"libtest_check_order_reloc_siblings_2",
+		"libtest_check_order_reloc_siblings_3",
+		"libtest_check_order_reloc_siblings_a",
+		"libtest_check_order_reloc_siblings_b",
+		"libtest_check_order_reloc_siblings_c",
+		"libtest_check_order_reloc_siblings_c_1",
+		"libtest_check_order_reloc_siblings_c_2",
+		"libtest_check_order_reloc_siblings_d",
+		"libtest_check_order_reloc_siblings_e",
+		"libtest_check_order_reloc_siblings_f",
+		"libtest_check_rtld_next_from_library",
+		"libtest_dlopen_df_1_global",
+		"libtest_dlopen_from_ctor",
+		"libtest_dlopen_from_ctor_main",
+		"libtest_dlopen_weak_undefined_func",
+		"libtest_dlsym_df_1_global",
+		"libtest_dlsym_from_this",
+		"libtest_dlsym_from_this_child",
+		"libtest_dlsym_from_this_grandchild",
+		"libtest_dlsym_weak_func",
+		"libtest_dt_runpath_a",
+		"libtest_dt_runpath_b",
+		"libtest_dt_runpath_c",
+		"libtest_dt_runpath_d",
+		"libtest_dt_runpath_d_zip",
+		"libtest_dt_runpath_x",
+		"libtest_dt_runpath_y",
+		"libtest_elftls_dynamic",
+		"libtest_elftls_dynamic_filler_1",
+		"libtest_elftls_dynamic_filler_2",
+		"libtest_elftls_dynamic_filler_3",
+		"libtest_elftls_shared_var",
+		"libtest_elftls_shared_var_ie",
+		"libtest_elftls_tprel",
+		"libtest_empty",
+		"libtest_ifunc",
+		"libtest_ifunc_variable",
+		"libtest_ifunc_variable_impl",
+		"libtest_indirect_thread_local_dtor",
+		"libtest_init_fini_order_child",
+		"libtest_init_fini_order_grand_child",
+		"libtest_init_fini_order_root",
+		"libtest_init_fini_order_root2",
+		"libtest_missing_symbol",
+		"libtest_missing_symbol_child_private",
+		"libtest_missing_symbol_child_public",
+		"libtest_missing_symbol_root",
+		"libtest_nodelete_1",
+		"libtest_nodelete_2",
+		"libtest_nodelete_dt_flags_1",
+		"libtest_pthread_atfork",
+		"libtest_relo_check_dt_needed_order",
+		"libtest_relo_check_dt_needed_order_1",
+		"libtest_relo_check_dt_needed_order_2",
+		"libtest_simple",
+		"libtest_thread_local_dtor",
+		"libtest_thread_local_dtor2",
+		"libtest_two_parents_child",
+		"libtest_two_parents_parent1",
+		"libtest_two_parents_parent2",
+		"libtest_versioned_lib",
+		"libtest_versioned_libv1",
+		"libtest_versioned_libv2",
+		"libtest_versioned_otherlib",
+		"libtest_versioned_otherlib_empty",
+		"libtest_versioned_uselibv1",
+		"libtest_versioned_uselibv2",
+		"libtest_versioned_uselibv2_other",
+		"libtest_versioned_uselibv3_other",
+		"libtest_with_dependency",
+		"libtest_with_dependency_loop",
+		"libtest_with_dependency_loop_a",
+		"libtest_with_dependency_loop_b",
+		"libtest_with_dependency_loop_b_tmp",
+		"libtest_with_dependency_loop_c",
+		"libtestshared",
 	}
 
 	Bp2buildCcLibraryStaticOnlyList = []string{}
@@ -535,30 +1088,8 @@
 		"libadb_pairing_connection_static",
 		"libadb_pairing_server", "libadb_pairing_server_static",
 
-		// TODO(b/204811222) support suffix in cc_binary
-		"acvp_modulewrapper",
-		"android.hardware.media.c2@1.0-service-v4l2",
-		"app_process",
-		"bar_test",
-		"bench_cxa_atexit",
-		"bench_noop",
-		"bench_noop_nostl",
-		"bench_noop_static",
-		"boringssl_self_test",
-		"boringssl_self_test_vendor",
-		"bssl",
-		"cavp",
-		"crash_dump",
+		// TODO(b/240563612) Needing `stem` selection support for cc_binary
 		"crasher",
-		"libcxx_test_template",
-		"linker",
-		"memory_replay",
-		"native_bridge_guest_linker",
-		"native_bridge_stub_library_defaults",
-		"noop",
-		"simpleperf_ndk",
-		"toybox-static",
-		"zlib_bench",
 
 		// java_import[_host] issues
 		// tradefed prebuilts depend on libprotobuf
@@ -574,5 +1105,19 @@
 		"prebuilt_android-support-annotations-nodeps",
 		"prebuilt_android-arch-paging-common-nodeps",
 		"prebuilt_android-arch-room-common-nodeps",
+		// TODO(b/217750501) exclude_dirs property not supported
+		"prebuilt_kotlin-reflect",
+		"prebuilt_kotlin-stdlib",
+		"prebuilt_kotlin-stdlib-jdk7",
+		"prebuilt_kotlin-stdlib-jdk8",
+		"prebuilt_kotlin-test",
+		// TODO(b/217750501) exclude_files property not supported
+		"prebuilt_platform-robolectric-4.4-prebuilt",
+		"prebuilt_platform-robolectric-4.5.1-prebuilt",
+		"prebuilt_currysrc_org.eclipse",
+	}
+
+	ProdMixedBuildsEnabledList = []string{
+		"com.android.adbd",
 	}
 )
diff --git a/android/apex.go b/android/apex.go
index c53ceb3..00b7241 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -454,6 +454,8 @@
 	}
 	return InList(what, apex_available) ||
 		(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
+		(what == "com.android.btservices" && InList("com.android.bluetooth", apex_available)) || // TODO b/243054261
+		(what == "com.android.bluetooth" && InList("com.android.btservices", apex_available)) || // TODO b/243054261
 		(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available))
 }
 
@@ -710,8 +712,8 @@
 
 // NewApexContents creates and initializes an ApexContents that is suitable
 // for use with an apex module.
-// * contents is a map from a module name to information about its membership within
-//   the apex.
+//   - contents is a map from a module name to information about its membership within
+//     the apex.
 func NewApexContents(contents map[string]ApexMembership) *ApexContents {
 	return &ApexContents{
 		contents: contents,
diff --git a/android/api_levels.go b/android/api_levels.go
index da50b19..bf7b317 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -307,24 +307,25 @@
 func getFinalCodenamesMap(config Config) map[string]int {
 	return config.Once(finalCodenamesMapKey, func() interface{} {
 		apiLevelsMap := map[string]int{
-			"G":     9,
-			"I":     14,
-			"J":     16,
-			"J-MR1": 17,
-			"J-MR2": 18,
-			"K":     19,
-			"L":     21,
-			"L-MR1": 22,
-			"M":     23,
-			"N":     24,
-			"N-MR1": 25,
-			"O":     26,
-			"O-MR1": 27,
-			"P":     28,
-			"Q":     29,
-			"R":     30,
-			"S":     31,
-			"S-V2":  32,
+			"G":        9,
+			"I":        14,
+			"J":        16,
+			"J-MR1":    17,
+			"J-MR2":    18,
+			"K":        19,
+			"L":        21,
+			"L-MR1":    22,
+			"M":        23,
+			"N":        24,
+			"N-MR1":    25,
+			"O":        26,
+			"O-MR1":    27,
+			"P":        28,
+			"Q":        29,
+			"R":        30,
+			"S":        31,
+			"S-V2":     32,
+			"Tiramisu": 33,
 		}
 
 		// TODO: Differentiate "current" and "future".
@@ -351,24 +352,25 @@
 func GetApiLevelsMap(config Config) map[string]int {
 	return config.Once(apiLevelsMapKey, func() interface{} {
 		apiLevelsMap := map[string]int{
-			"G":     9,
-			"I":     14,
-			"J":     16,
-			"J-MR1": 17,
-			"J-MR2": 18,
-			"K":     19,
-			"L":     21,
-			"L-MR1": 22,
-			"M":     23,
-			"N":     24,
-			"N-MR1": 25,
-			"O":     26,
-			"O-MR1": 27,
-			"P":     28,
-			"Q":     29,
-			"R":     30,
-			"S":     31,
-			"S-V2":  32,
+			"G":        9,
+			"I":        14,
+			"J":        16,
+			"J-MR1":    17,
+			"J-MR2":    18,
+			"K":        19,
+			"L":        21,
+			"L-MR1":    22,
+			"M":        23,
+			"N":        24,
+			"N-MR1":    25,
+			"O":        26,
+			"O-MR1":    27,
+			"P":        28,
+			"Q":        29,
+			"R":        30,
+			"S":        31,
+			"S-V2":     32,
+			"Tiramisu": 33,
 		}
 		for i, codename := range config.PlatformVersionActiveCodenames() {
 			apiLevelsMap[codename] = previewAPILevelBase + i
diff --git a/android/arch.go b/android/arch.go
index 6acc8ad..9bc9d89 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"reflect"
 	"runtime"
+	"sort"
 	"strings"
 
 	"android/soong/bazel"
@@ -393,54 +394,6 @@
 	}
 }
 
-func registerBp2buildArchPathDepsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("bp2build-arch-pathdeps", bp2buildArchPathDepsMutator).Parallel()
-}
-
-// add dependencies for architecture specific properties tagged with `android:"path"`
-func bp2buildArchPathDepsMutator(ctx BottomUpMutatorContext) {
-	var module Module
-	module = ctx.Module()
-
-	m := module.base()
-	if !m.ArchSpecific() {
-		return
-	}
-
-	// addPathDepsForProps does not descend into sub structs, so we need to descend into the
-	// arch-specific properties ourselves
-	var properties []interface{}
-	for _, archProperties := range m.archProperties {
-		for _, archProps := range archProperties {
-			archPropValues := reflect.ValueOf(archProps).Elem()
-			// there are three "arch" variations, descend into each
-			for _, variant := range []string{"Arch", "Multilib", "Target"} {
-				// The properties are an interface, get the value (a pointer) that it points to
-				archProps := archPropValues.FieldByName(variant).Elem()
-				if archProps.IsNil() {
-					continue
-				}
-				// And then a pointer to a struct
-				archProps = archProps.Elem()
-				for i := 0; i < archProps.NumField(); i += 1 {
-					f := archProps.Field(i)
-					// If the value of the field is a struct (as opposed to a pointer to a struct) then step
-					// into the BlueprintEmbed field.
-					if f.Kind() == reflect.Struct {
-						f = f.FieldByName("BlueprintEmbed")
-					}
-					if f.IsZero() {
-						continue
-					}
-					props := f.Interface().(interface{})
-					properties = append(properties, props)
-				}
-			}
-		}
-	}
-	addPathDepsForProps(ctx, properties)
-}
-
 // osMutator splits an arch-specific module into a variant for each OS that is enabled for the
 // module.  It uses the HostOrDevice value passed to InitAndroidArchModule and the
 // device_supported and host_supported properties to determine which OsTypes are enabled for this
@@ -572,26 +525,29 @@
 // archMutator splits a module into a variant for each Target requested by the module.  Target selection
 // for a module is in three levels, OsClass, multilib, and then Target.
 // OsClass selection is determined by:
-//    - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
-//      whether the module type can compile for host, device or both.
-//    - The host_supported and device_supported properties on the module.
+//   - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
+//     whether the module type can compile for host, device or both.
+//   - The host_supported and device_supported properties on the module.
+//
 // If host is supported for the module, the Host and HostCross OsClasses are selected.  If device is supported
 // for the module, the Device OsClass is selected.
 // Within each selected OsClass, the multilib selection is determined by:
-//    - The compile_multilib property if it set (which may be overridden by target.android.compile_multilib or
-//      target.host.compile_multilib).
-//    - The default multilib passed to InitAndroidArchModule if compile_multilib was not set.
+//   - The compile_multilib property if it set (which may be overridden by target.android.compile_multilib or
+//     target.host.compile_multilib).
+//   - The default multilib passed to InitAndroidArchModule if compile_multilib was not set.
+//
 // Valid multilib values include:
-//    "both": compile for all Targets supported by the OsClass (generally x86_64 and x86, or arm64 and arm).
-//    "first": compile for only a single preferred Target supported by the OsClass.  This is generally x86_64 or arm64,
-//        but may be arm for a 32-bit only build.
-//    "32": compile for only a single 32-bit Target supported by the OsClass.
-//    "64": compile for only a single 64-bit Target supported by the OsClass.
-//    "common": compile a for a single Target that will work on all Targets supported by the OsClass (for example Java).
-//    "common_first": compile a for a Target that will work on all Targets supported by the OsClass
-//        (same as "common"), plus a second Target for the preferred Target supported by the OsClass
-//        (same as "first").  This is used for java_binary that produces a common .jar and a wrapper
-//        executable script.
+//
+//	"both": compile for all Targets supported by the OsClass (generally x86_64 and x86, or arm64 and arm).
+//	"first": compile for only a single preferred Target supported by the OsClass.  This is generally x86_64 or arm64,
+//	    but may be arm for a 32-bit only build.
+//	"32": compile for only a single 32-bit Target supported by the OsClass.
+//	"64": compile for only a single 64-bit Target supported by the OsClass.
+//	"common": compile a for a single Target that will work on all Targets supported by the OsClass (for example Java).
+//	"common_first": compile a for a Target that will work on all Targets supported by the OsClass
+//	    (same as "common"), plus a second Target for the preferred Target supported by the OsClass
+//	    (same as "first").  This is used for java_binary that produces a common .jar and a wrapper
+//	    executable script.
 //
 // Once the list of Targets is determined, the module is split into a variant for each Target.
 //
@@ -664,6 +620,12 @@
 		mctx.ModuleErrorf("%s", err.Error())
 	}
 
+	// If there are no supported targets disable the module.
+	if len(targets) == 0 {
+		base.Disable()
+		return
+	}
+
 	// If the module is using extraMultilib, decode the extraMultilib selection into
 	// a separate list of Targets.
 	var multiTargets []Target
@@ -672,6 +634,7 @@
 		if err != nil {
 			mctx.ModuleErrorf("%s", err.Error())
 		}
+		multiTargets = filterHostCross(multiTargets, targets[0].HostCross)
 	}
 
 	// Recovery is always the primary architecture, filter out any other architectures.
@@ -808,6 +771,18 @@
 	return targets
 }
 
+// filterHostCross takes a list of Targets and a hostCross value, and returns a modified list
+// that contains only Targets that have the specified HostCross.
+func filterHostCross(targets []Target, hostCross bool) []Target {
+	for i := 0; i < len(targets); i++ {
+		if targets[i].HostCross != hostCross {
+			targets = append(targets[:i], targets[i+1:]...)
+			i--
+		}
+	}
+	return targets
+}
+
 // archPropRoot is a struct type used as the top level of the arch-specific properties.  It
 // contains the "arch", "multilib", and "target" property structs.  It is used to split up the
 // property structs to limit how much is allocated when a single arch-specific property group is
@@ -998,19 +973,13 @@
 		if string(field.Tag) != `android:"`+strings.Join(values, ",")+`"` {
 			panic(fmt.Errorf("unexpected tag format %q", field.Tag))
 		}
-		// don't delete path tag as it is needed for bp2build
 		// these tags don't need to be present in the runtime generated struct type.
-		values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend"})
-		if len(values) > 0 && values[0] != "path" {
+		values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path"})
+		if len(values) > 0 {
 			panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
-		} else if len(values) == 1 {
-			// FIXME(b/200678898): This assumes that the only tag type when there's
-			// `android:"arch_variant"` is `android` itself and thus clobbers others
-			field.Tag = reflect.StructTag(`android:"` + strings.Join(values, ",") + `"`)
-		} else {
-			field.Tag = ``
 		}
 
+		field.Tag = ``
 		return true, field
 	}
 	return false, field
@@ -1269,11 +1238,13 @@
 
 // Returns the struct containing the properties specific to the given
 // architecture type. These look like this in Blueprint files:
-// arch: {
-//     arm64: {
-//         key: value,
-//     },
-// },
+//
+//	arch: {
+//	    arm64: {
+//	        key: value,
+//	    },
+//	},
+//
 // This struct will also contain sub-structs containing to the architecture/CPU
 // variants and features that themselves contain properties specific to those.
 func getArchTypeStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) (reflect.Value, bool) {
@@ -1285,11 +1256,12 @@
 
 // Returns the struct containing the properties specific to a given multilib
 // value. These look like this in the Blueprint file:
-// multilib: {
-//     lib32: {
-//         key: value,
-//     },
-// },
+//
+//	multilib: {
+//	    lib32: {
+//	        key: value,
+//	    },
+//	},
 func getMultilibStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) (reflect.Value, bool) {
 	archPropValues := reflect.ValueOf(archProperties).Elem()
 	multilibProp := archPropValues.FieldByName("Multilib").Elem()
@@ -1843,20 +1815,23 @@
 }
 
 // FirstTarget takes a list of Targets and a list of multilib values and returns a list of Targets
-// that contains zero or one Target for each OsType, selecting the one that matches the earliest
-// filter.
+// that contains zero or one Target for each OsType and HostCross, selecting the one that matches
+// the earliest filter.
 func FirstTarget(targets []Target, filters ...string) []Target {
 	// find the first target from each OS
 	var ret []Target
-	hasHost := false
-	set := make(map[OsType]bool)
+	type osHostCross struct {
+		os        OsType
+		hostCross bool
+	}
+	set := make(map[osHostCross]bool)
 
 	for _, filter := range filters {
 		buildTargets := filterMultilibTargets(targets, filter)
 		for _, t := range buildTargets {
-			if _, found := set[t.Os]; !found {
-				hasHost = hasHost || (t.Os.Class == Host)
-				set[t.Os] = true
+			key := osHostCross{t.Os, t.HostCross}
+			if _, found := set[key]; !found {
+				set[key] = true
 				ret = append(ret, t)
 			}
 		}
@@ -2064,9 +2039,10 @@
 // arch-variant properties correspond to the values of the properties of the 'propertySet' struct
 // that are specific to that axis/configuration. Each axis is independent, containing
 // non-overlapping configs that correspond to the various "arch-variant" support, at this time:
-//    arches (including multilib)
-//    oses
-//    arch+os combinations
+//
+//	arches (including multilib)
+//	oses
+//	arch+os combinations
 //
 // For example, passing a struct { Foo bool, Bar string } will return an interface{} that can be
 // type asserted back into the same struct, containing the config-specific property value specified
@@ -2109,13 +2085,22 @@
 	// For each arch type (x86, arm64, etc.)
 	for _, arch := range ArchTypeList() {
 		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
-		// Iterate over ever shard and extract a struct with the same type as the
+		// Iterate over every shard and extract a struct with the same type as the
 		// input one that contains the data specific to that arch.
 		propertyStructs := make([]reflect.Value, 0)
+		archFeaturePropertyStructs := make(map[string][]reflect.Value, 0)
 		for _, archProperty := range archProperties {
 			archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch)
 			if ok {
 				propertyStructs = append(propertyStructs, archTypeStruct)
+
+				// For each feature this arch supports (arm: neon, x86: ssse3, sse4, ...)
+				for _, feature := range archFeatures[arch] {
+					prefix := "arch." + arch.Name + "." + feature
+					if featureProperties, ok := getChildPropertyStruct(ctx, archTypeStruct, feature, prefix); ok {
+						archFeaturePropertyStructs[feature] = append(archFeaturePropertyStructs[feature], featureProperties)
+					}
+				}
 			}
 			multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch)
 			if ok {
@@ -2123,10 +2108,31 @@
 			}
 		}
 
-		// Create a new instance of the requested property set
-		value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+		archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, propertySet)
 
-		archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, value)
+		// In soong, if multiple features match the current configuration, they're
+		// all used. In bazel, we have to have unambiguous select() statements, so
+		// we can't have two features that are both active in the same select().
+		// One alternative is to split out each feature into a separate select(),
+		// but then it's difficult to support exclude_srcs, which may need to
+		// exclude things from the regular arch select() statement if a certain
+		// feature is active. Instead, keep the features in the same select
+		// statement as the arches, but emit the power set of all possible
+		// combinations of features, so that bazel can match the most precise one.
+		allFeatures := make([]string, 0, len(archFeaturePropertyStructs))
+		for feature := range archFeaturePropertyStructs {
+			allFeatures = append(allFeatures, feature)
+		}
+		for _, features := range bazel.PowerSetWithoutEmptySet(allFeatures) {
+			sort.Strings(features)
+			propsForCurrentFeatureSet := make([]reflect.Value, 0)
+			propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, propertyStructs...)
+			for _, feature := range features {
+				propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, archFeaturePropertyStructs[feature]...)
+			}
+			archToProp[arch.Name+"-"+strings.Join(features, "-")] =
+				mergeStructs(ctx, propsForCurrentFeatureSet, propertySet)
+		}
 	}
 	axisToProps[bazel.ArchConfigurationAxis] = archToProp
 
@@ -2219,17 +2225,21 @@
 
 // Returns a struct matching the propertySet interface, containing properties specific to the targetName
 // For example, given these arguments:
-//    propertySet = BaseCompilerProperties
-//    targetName = "android_arm"
+//
+//	propertySet = BaseCompilerProperties
+//	targetName = "android_arm"
+//
 // And given this Android.bp fragment:
-//    target:
-//       android_arm: {
-//          srcs: ["foo.c"],
-//       }
-//       android_arm64: {
-//          srcs: ["bar.c"],
-//      }
-//    }
+//
+//	target:
+//	   android_arm: {
+//	      srcs: ["foo.c"],
+//	   }
+//	   android_arm64: {
+//	      srcs: ["bar.c"],
+//	  }
+//	}
+//
 // This would return a BaseCompilerProperties with BaseCompilerProperties.Srcs = ["foo.c"]
 func getTargetStructs(ctx ArchVariantContext, archProperties []interface{}, targetName string) []reflect.Value {
 	var propertyStructs []reflect.Value
diff --git a/android/arch_test.go b/android/arch_test.go
index dd0b115..6814f8a 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -66,9 +66,9 @@
 			}{},
 			out: &struct {
 				A *string
-				B *string `android:"path"`
-				C *string `android:"path"`
-				D *string `android:"path"`
+				B *string
+				C *string
+				D *string
 			}{},
 			filtered: true,
 		},
@@ -259,6 +259,27 @@
 	}
 }
 
+func (m *archTestMultiTargetsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *archTestMultiTargetsModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+}
+
+func archTestMultiTargetsModuleFactory() Module {
+	m := &archTestMultiTargetsModule{}
+	m.AddProperties(&m.props)
+	InitAndroidMultiTargetsArchModule(m, HostAndDeviceSupported, MultilibCommon)
+	return m
+}
+
+type archTestMultiTargetsModule struct {
+	ModuleBase
+	props struct {
+		Deps []string
+	}
+}
+
 func (m *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 }
 
@@ -277,19 +298,27 @@
 	PrepareForTestWithArchMutator,
 	FixtureRegisterWithContext(func(ctx RegistrationContext) {
 		ctx.RegisterModuleType("module", archTestModuleFactory)
+		ctx.RegisterModuleType("multi_targets_module", archTestMultiTargetsModuleFactory)
 	}),
 )
 
 func TestArchMutator(t *testing.T) {
 	var buildOSVariants []string
+	var buildOS64Variants []string
 	var buildOS32Variants []string
+	var buildOSCommonVariant string
+
 	switch runtime.GOOS {
 	case "linux":
 		buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"}
+		buildOS64Variants = []string{"linux_glibc_x86_64"}
 		buildOS32Variants = []string{"linux_glibc_x86"}
+		buildOSCommonVariant = "linux_glibc_common"
 	case "darwin":
 		buildOSVariants = []string{"darwin_x86_64"}
+		buildOS64Variants = []string{"darwin_x86_64"}
 		buildOS32Variants = nil
+		buildOSCommonVariant = "darwin_common"
 	}
 
 	bp := `
@@ -312,24 +341,46 @@
 			host_supported: true,
 			compile_multilib: "32",
 		}
+
+		module {
+			name: "first",
+			host_supported: true,
+			compile_multilib: "first",
+		}
+
+		multi_targets_module {
+			name: "multi_targets",
+			host_supported: true,
+		}
 	`
 
 	testCases := []struct {
-		name        string
-		preparer    FixturePreparer
-		fooVariants []string
-		barVariants []string
-		bazVariants []string
-		quxVariants []string
+		name          string
+		preparer      FixturePreparer
+		fooVariants   []string
+		barVariants   []string
+		bazVariants   []string
+		quxVariants   []string
+		firstVariants []string
+
+		multiTargetVariants    []string
+		multiTargetVariantsMap map[string][]string
+
+		goOS string
 	}{
 		{
-			name:        "normal",
-			preparer:    nil,
-			fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
-			barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
-			bazVariants: nil,
-			quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
-		},
+			name:                "normal",
+			preparer:            nil,
+			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			barVariants:         append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
+			bazVariants:         nil,
+			quxVariants:         append(buildOS32Variants, "android_arm_armv7-a-neon"),
+			firstVariants:       append(buildOS64Variants, "android_arm64_armv8-a"),
+			multiTargetVariants: []string{buildOSCommonVariant, "android_common"},
+			multiTargetVariantsMap: map[string][]string{
+				buildOSCommonVariant: buildOS64Variants,
+				"android_common":     {"android_arm64_armv8-a"},
+			}},
 		{
 			name: "host-only",
 			preparer: FixtureModifyConfig(func(config Config) {
@@ -337,10 +388,33 @@
 				config.BuildOSCommonTarget = Target{}
 				config.Targets[Android] = nil
 			}),
-			fooVariants: nil,
-			barVariants: buildOSVariants,
-			bazVariants: nil,
-			quxVariants: buildOS32Variants,
+			fooVariants:         nil,
+			barVariants:         buildOSVariants,
+			bazVariants:         nil,
+			quxVariants:         buildOS32Variants,
+			firstVariants:       buildOS64Variants,
+			multiTargetVariants: []string{buildOSCommonVariant},
+			multiTargetVariantsMap: map[string][]string{
+				buildOSCommonVariant: buildOS64Variants,
+			},
+		},
+		{
+			name: "same arch host and host cross",
+			preparer: FixtureModifyConfig(func(config Config) {
+				modifyTestConfigForMusl(config)
+				modifyTestConfigForMuslArm64HostCross(config)
+			}),
+			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			barVariants:         []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			bazVariants:         nil,
+			quxVariants:         []string{"linux_musl_x86", "android_arm_armv7-a-neon"},
+			firstVariants:       []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"},
+			multiTargetVariants: []string{"linux_musl_common", "android_common"},
+			multiTargetVariantsMap: map[string][]string{
+				"linux_musl_common": {"linux_musl_x86_64"},
+				"android_common":    {"android_arm64_armv8-a"},
+			},
+			goOS: "linux",
 		},
 	}
 
@@ -356,8 +430,21 @@
 		return ret
 	}
 
+	moduleMultiTargets := func(ctx *TestContext, name string, variant string) []string {
+		var ret []string
+		targets := ctx.ModuleForTests(name, variant).Module().MultiTargets()
+		for _, t := range targets {
+			ret = append(ret, t.String())
+		}
+		return ret
+	}
+
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			if tt.goOS != runtime.GOOS {
+				t.Skipf("requries runtime.GOOS %s", tt.goOS)
+			}
+
 			result := GroupFixturePreparers(
 				prepareForArchTest,
 				// Test specific preparer
@@ -381,6 +468,20 @@
 			if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !reflect.DeepEqual(w, g) {
 				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
 			}
+			if g, w := enabledVariants(ctx, "first"), tt.firstVariants; !reflect.DeepEqual(w, g) {
+				t.Errorf("want first variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			if g, w := enabledVariants(ctx, "multi_targets"), tt.multiTargetVariants; !reflect.DeepEqual(w, g) {
+				t.Fatalf("want multi_target variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			for _, variant := range tt.multiTargetVariants {
+				targets := moduleMultiTargets(ctx, "multi_targets", variant)
+				if g, w := targets, tt.multiTargetVariantsMap[variant]; !reflect.DeepEqual(w, g) {
+					t.Errorf("want ctx.MultiTarget() for %q:\n%q\ngot:\n%q\n", variant, w, g)
+				}
+			}
 		})
 	}
 }
diff --git a/android/bazel.go b/android/bazel.go
index 183a2f3..eb6aca4 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -17,9 +17,6 @@
 import (
 	"bufio"
 	"errors"
-	"fmt"
-	"io/ioutil"
-	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -35,6 +32,12 @@
 	Bp2BuildTopLevel = "."
 )
 
+// Bp2buildAidlLibrary describes a filegroup module that are converted to aidl_library
+type Bp2buildAidlLibrary interface {
+	ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool
+	GetAidlLibraryLabel(ctx BazelConversionPathContext) string
+}
+
 type BazelConversionStatus struct {
 	// Information about _all_ bp2build targets generated by this module. Multiple targets are
 	// supported as Soong handles some things within a single target that we may choose to split into
@@ -72,7 +75,7 @@
 
 // Properties contains common module properties for Bazel migration purposes.
 type properties struct {
-	// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
+	// In "Bazel mixed build" mode, this represents the Bazel target replacing
 	// this Soong module.
 	Bazel_module bazelModuleProperties
 }
@@ -111,7 +114,6 @@
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
 	ShouldConvertWithBp2build(ctx BazelConversionContext) bool
 	shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool
-	GetBazelBuildFileContents(c Config, path, name string) (string, error)
 	ConvertWithBp2build(ctx TopDownMutatorContext)
 
 	// namespacedVariableProps is a map from a soong config variable namespace
@@ -205,7 +207,7 @@
 	return "" // no label for unconverted module
 }
 
-type bp2BuildConversionAllowlist struct {
+type Bp2BuildConversionAllowlist struct {
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	defaultConfig allowlists.Bp2BuildConfig
 
@@ -213,46 +215,47 @@
 	// in the synthetic Bazel workspace.
 	keepExistingBuildFile map[string]bool
 
-	// Per-module allowlist to always opt modules in of both bp2build and mixed builds.
-	// These modules are usually in directories with many other modules that are not ready for
-	// conversion.
+	// Per-module allowlist to always opt modules into both bp2build and Bazel Dev Mode mixed
+	// builds. These modules are usually in directories with many other modules that are not ready
+	// for conversion.
 	//
 	// A module can either be in this list or its directory allowlisted entirely
 	// in bp2buildDefaultConfig, but not both at the same time.
 	moduleAlwaysConvert map[string]bool
 
-	// Per-module-type allowlist to always opt modules in to both bp2build and mixed builds
-	// when they have the same type as one listed.
+	// Per-module-type allowlist to always opt modules in to both bp2build and
+	// Bazel Dev Mode mixed builds when they have the same type as one listed.
 	moduleTypeAlwaysConvert map[string]bool
 
-	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
+	// Per-module denylist to always opt modules out of bp2build conversion.
 	moduleDoNotConvert map[string]bool
 
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
 	ccLibraryStaticOnly map[string]bool
-
-	// Per-module denylist to opt modules out of mixed builds. Such modules will
-	// still be generated via bp2build.
-	mixedBuildsDisabled map[string]bool
 }
 
-// NewBp2BuildAllowlist creates a new, empty bp2BuildConversionAllowlist
+// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
+// generate a static version of itself based on the current global configuration.
+func (a Bp2BuildConversionAllowlist) GenerateCcLibraryStaticOnly(moduleName string) bool {
+	return a.ccLibraryStaticOnly[moduleName]
+}
+
+// NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
 // which can be populated using builder pattern Set* methods
-func NewBp2BuildAllowlist() bp2BuildConversionAllowlist {
-	return bp2BuildConversionAllowlist{
+func NewBp2BuildAllowlist() Bp2BuildConversionAllowlist {
+	return Bp2BuildConversionAllowlist{
 		allowlists.Bp2BuildConfig{},
 		map[string]bool{},
 		map[string]bool{},
 		map[string]bool{},
 		map[string]bool{},
 		map[string]bool{},
-		map[string]bool{},
 	}
 }
 
 // SetDefaultConfig copies the entries from defaultConfig into the allowlist
-func (a bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist {
 	if a.defaultConfig == nil {
 		a.defaultConfig = allowlists.Bp2BuildConfig{}
 	}
@@ -264,7 +267,7 @@
 }
 
 // SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
-func (a bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist {
 	if a.keepExistingBuildFile == nil {
 		a.keepExistingBuildFile = map[string]bool{}
 	}
@@ -276,7 +279,7 @@
 }
 
 // SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist {
 	if a.moduleAlwaysConvert == nil {
 		a.moduleAlwaysConvert = map[string]bool{}
 	}
@@ -288,7 +291,7 @@
 }
 
 // SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist {
 	if a.moduleTypeAlwaysConvert == nil {
 		a.moduleTypeAlwaysConvert = map[string]bool{}
 	}
@@ -300,7 +303,7 @@
 }
 
 // SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist {
 	if a.moduleDoNotConvert == nil {
 		a.moduleDoNotConvert = map[string]bool{}
 	}
@@ -312,7 +315,7 @@
 }
 
 // SetCcLibraryStaticOnlyList copies the entries from ccLibraryStaticOnly into the allowlist
-func (a bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) Bp2BuildConversionAllowlist {
 	if a.ccLibraryStaticOnly == nil {
 		a.ccLibraryStaticOnly = map[string]bool{}
 	}
@@ -323,52 +326,15 @@
 	return a
 }
 
-// SetMixedBuildsDisabledList copies the entries from mixedBuildsDisabled into the allowlist
-func (a bp2BuildConversionAllowlist) SetMixedBuildsDisabledList(mixedBuildsDisabled []string) bp2BuildConversionAllowlist {
-	if a.mixedBuildsDisabled == nil {
-		a.mixedBuildsDisabled = map[string]bool{}
-	}
-	for _, m := range mixedBuildsDisabled {
-		a.mixedBuildsDisabled[m] = true
-	}
-
-	return a
-}
-
-var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
-var bp2buildAllowlist OncePer
-
-func getBp2BuildAllowList() bp2BuildConversionAllowlist {
-	return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
-		return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
-			SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
-			SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
-			SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
-			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
-			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList).
-			SetMixedBuildsDisabledList(allowlists.MixedBuildsDisabledList)
-	}).(bp2BuildConversionAllowlist)
-}
-
-// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
-// generate a static version of itself based on the current global configuration.
-func GenerateCcLibraryStaticOnly(moduleName string) bool {
-	return getBp2BuildAllowList().ccLibraryStaticOnly[moduleName]
-}
-
 // ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
 // added to the build symlink forest based on the current global configuration.
-func ShouldKeepExistingBuildFileForDir(dir string) bool {
-	return shouldKeepExistingBuildFileForDir(getBp2BuildAllowList(), dir)
-}
-
-func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, dir string) bool {
-	if _, ok := allowlist.keepExistingBuildFile[dir]; ok {
+func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
+	if _, ok := a.keepExistingBuildFile[dir]; ok {
 		// Exact dir match
 		return true
 	}
 	// Check if subtree match
-	for prefix, recursive := range allowlist.keepExistingBuildFile {
+	for prefix, recursive := range a.keepExistingBuildFile {
 		if recursive {
 			if strings.HasPrefix(dir, prefix+"/") {
 				return true
@@ -379,6 +345,20 @@
 	return false
 }
 
+var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
+var bp2buildAllowlist OncePer
+
+func GetBp2BuildAllowList() Bp2BuildConversionAllowlist {
+	return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
+		return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
+			SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
+			SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
+			SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
+			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
+			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList)
+	}).(Bp2BuildConversionAllowlist)
+}
+
 // MixedBuildsEnabled returns true if a module is ready to be replaced by a
 // converted or handcrafted Bazel target. As a side effect, calling this
 // method will also log whether this module is mixed build enabled for
@@ -399,20 +379,10 @@
 	if !ctx.Module().Enabled() {
 		return false
 	}
-	if !ctx.Config().BazelContext.BazelEnabled() {
-		return false
-	}
 	if !convertedToBazel(ctx, ctx.Module()) {
 		return false
 	}
-
-	if GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
-		// Don't use partially-converted cc_library targets in mixed builds,
-		// since mixed builds would generally rely on both static and shared
-		// variants of a cc_library.
-		return false
-	}
-	return !getBp2BuildAllowList().mixedBuildsDisabled[ctx.Module().Name()]
+	return ctx.Config().BazelContext.BazelAllowlisted(ctx.Module().Name())
 }
 
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -453,7 +423,7 @@
 	}
 
 	moduleName := module.Name()
-	allowlist := ctx.Config().bp2buildPackageConfig
+	allowlist := ctx.Config().Bp2buildPackageConfig
 	moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
 	moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
 	allowlistConvert := moduleNameAllowed || moduleTypeAllowed
@@ -469,14 +439,6 @@
 		return false
 	}
 
-	if allowlistConvert && shouldKeepExistingBuildFileForDir(allowlist, packagePath) {
-		if moduleNameAllowed {
-			ctx.ModuleErrorf("A module cannot be in a directory listed in keepExistingBuildFile"+
-				" and also be in moduleAlwaysConvert. Directory: '%s'", packagePath)
-			return false
-		}
-	}
-
 	// This is a tristate value: true, false, or unset.
 	if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
 		if moduleNameAllowed {
@@ -532,28 +494,6 @@
 	return false, packagePath
 }
 
-// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
-// an error if there are errors reading the file.
-// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
-// something more targeted based on the rule type and target.
-func (b *BazelModuleBase) GetBazelBuildFileContents(c Config, path, name string) (string, error) {
-	if !strings.Contains(b.HandcraftedLabel(), path) {
-		return "", fmt.Errorf("%q not found in bazel_module.label %q", path, b.HandcraftedLabel())
-	}
-	name = filepath.Join(path, name)
-	f, err := c.fs.Open(name)
-	if err != nil {
-		return "", err
-	}
-	defer f.Close()
-
-	data, err := ioutil.ReadAll(f)
-	if err != nil {
-		return "", err
-	}
-	return string(data[:]), nil
-}
-
 func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) {
 	ctx.TopDown("bp2build_conversion", convertWithBp2build).Parallel()
 }
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 5804a46..3e490dd 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,8 +27,10 @@
 	"strings"
 	"sync"
 
+	"android/soong/android/allowlists"
 	"android/soong/bazel/cquery"
 	"android/soong/shared"
+
 	"github.com/google/blueprint"
 
 	"android/soong/bazel"
@@ -54,7 +56,7 @@
 }
 
 func RegisterMixedBuildsMutator(ctx RegistrationContext) {
-	ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
 		ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
 	})
 }
@@ -141,8 +143,11 @@
 	// queued in the BazelContext.
 	InvokeBazel(config Config) error
 
-	// Returns true if bazel is enabled for the given configuration.
-	BazelEnabled() bool
+	// Returns true if Bazel handling is enabled for the module with the given name.
+	// Note that this only implies "bazel mixed build" allowlisting. The caller
+	// should independently verify the module is eligible for Bazel handling
+	// (for example, that it is MixedBuildBuildable).
+	BazelAllowlisted(moduleName string) bool
 
 	// Returns the bazel output base (the root directory for all bazel intermediate outputs).
 	OutputBase() string
@@ -182,6 +187,17 @@
 
 	// Depsets which should be used for Bazel's build statements.
 	depsets []bazel.AqueryDepset
+
+	// Per-module allowlist/denylist functionality to control whether analysis of
+	// modules are handled by Bazel. For modules which do not have a Bazel definition
+	// (or do not sufficiently support bazel handling via MixedBuildBuildable),
+	// this allowlist will have no effect, even if the module is explicitly allowlisted here.
+	// Per-module denylist to opt modules out of bazel handling.
+	bazelDisabledModules map[string]bool
+	// Per-module allowlist to opt modules in to bazel handling.
+	bazelEnabledModules map[string]bool
+	// If true, modules are bazel-enabled by default, unless present in bazelDisabledModules.
+	modulesDefaultToBazel bool
 }
 
 var _ BazelContext = &bazelContext{}
@@ -228,7 +244,7 @@
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) BazelEnabled() bool {
+func (m MockBazelContext) BazelAllowlisted(moduleName string) bool {
 	return true
 }
 
@@ -314,7 +330,7 @@
 	return ""
 }
 
-func (n noopBazelContext) BazelEnabled() bool {
+func (n noopBazelContext) BazelAllowlisted(moduleName string) bool {
 	return false
 }
 
@@ -327,9 +343,30 @@
 }
 
 func NewBazelContext(c *config) (BazelContext, error) {
-	// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
-	// are production ready.
-	if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
+	var modulesDefaultToBazel bool
+	disabledModules := map[string]bool{}
+	enabledModules := map[string]bool{}
+
+	switch c.BuildMode {
+	case BazelProdMode:
+		modulesDefaultToBazel = false
+
+		for _, enabledProdModule := range allowlists.ProdMixedBuildsEnabledList {
+			enabledModules[enabledProdModule] = true
+		}
+	case BazelDevMode:
+		modulesDefaultToBazel = true
+
+		// Don't use partially-converted cc_library targets in mixed builds,
+		// since mixed builds would generally rely on both static and shared
+		// variants of a cc_library.
+		for staticOnlyModule, _ := range GetBp2BuildAllowList().ccLibraryStaticOnly {
+			disabledModules[staticOnlyModule] = true
+		}
+		for _, disabledDevModule := range allowlists.MixedBuildsDisabledList {
+			disabledModules[disabledDevModule] = true
+		}
+	default:
 		return noopBazelContext{}, nil
 	}
 
@@ -337,10 +374,14 @@
 	if err != nil {
 		return nil, err
 	}
+
 	return &bazelContext{
-		bazelRunner: &builtinBazelRunner{},
-		paths:       p,
-		requests:    make(map[cqueryKey]bool),
+		bazelRunner:           &builtinBazelRunner{},
+		paths:                 p,
+		requests:              make(map[cqueryKey]bool),
+		modulesDefaultToBazel: modulesDefaultToBazel,
+		bazelEnabledModules:   enabledModules,
+		bazelDisabledModules:  disabledModules,
 	}, nil
 }
 
@@ -385,8 +426,14 @@
 	return p.metricsDir
 }
 
-func (context *bazelContext) BazelEnabled() bool {
-	return true
+func (context *bazelContext) BazelAllowlisted(moduleName string) bool {
+	if context.bazelDisabledModules[moduleName] {
+		return false
+	}
+	if context.bazelEnabledModules[moduleName] {
+		return true
+	}
+	return context.modulesDefaultToBazel
 }
 
 func pwdPrefix() string {
@@ -647,6 +694,31 @@
 	formatString := `
 # This file is generated by soong_build. Do not edit.
 
+# a drop-in replacement for json.encode(), not available in cquery environment
+# TODO(cparsons): bring json module in and remove this function
+def json_encode(input):
+  # Avoiding recursion by limiting
+  #  - a dict to contain anything except a dict
+  #  - a list to contain only primitives
+  def encode_primitive(p):
+    t = type(p)
+    if t == "string" or t == "int":
+      return repr(p)
+    fail("unsupported value '%%s' of type '%%s'" %% (p, type(p)))
+
+  def encode_list(list):
+    return "[%%s]" %% ", ".join([encode_primitive(item) for item in list])
+
+  def encode_list_or_primitive(v):
+    return encode_list(v) if type(v) == "list" else encode_primitive(v)
+
+  if type(input) == "dict":
+    # TODO(juu): the result is read line by line so can't use '\n' yet
+    kv_pairs = [("%%s: %%s" %% (encode_primitive(k), encode_list_or_primitive(v))) for (k, v) in input.items()]
+    return "{ %%s }" %% ", ".join(kv_pairs)
+  else:
+    return encode_list_or_primitive(input)
+
 # Label Map Section
 %s
 
@@ -680,15 +752,6 @@
     fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
     return "UNKNOWN"
 
-def json_for_file(key, file):
-    return '"' + key + '":"' + file.path + '"'
-
-def json_for_files(key, files):
-    return '"' + key + '":[' + ",".join(['"' + f.path + '"' for f in files]) + ']'
-
-def json_for_labels(key, ll):
-    return '"' + key + '":[' + ",".join(['"' + str(x) + '"' for x in ll]) + ']'
-
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
@@ -766,9 +829,9 @@
 	cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
 		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
 	if err != nil {
-		_ = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
+		return err
 	}
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666); err != nil {
 		return err
 	}
 
@@ -850,7 +913,7 @@
 
 func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
 	// bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
-	if !ctx.Config().BazelContext.BazelEnabled() {
+	if !ctx.Config().IsMixedBuildsEnabled() {
 		return
 	}
 
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index c030aa8..bbdae96 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -221,9 +221,13 @@
 // Transform a path (if necessary) to acknowledge package boundaries
 //
 // e.g. something like
-//   async_safe/include/async_safe/CHECK.h
+//
+//	async_safe/include/async_safe/CHECK.h
+//
 // might become
-//   //bionic/libc/async_safe:include/async_safe/CHECK.h
+//
+//	//bionic/libc/async_safe:include/async_safe/CHECK.h
+//
 // if the "async_safe" directory is actually a package and not just a directory.
 //
 // In particular, paths that extend into packages are transformed into absolute labels beginning with //.
@@ -303,20 +307,21 @@
 // directory and Bazel target labels, excluding those included in the excludes argument (which
 // should already be expanded to resolve references to Soong-modules). Valid elements of paths
 // include:
-// * filepath, relative to local module directory, resolves as a filepath relative to the local
-//   source directory
-// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
-//    module directory. Because Soong does not have a concept of crossing package boundaries, the
-//    glob as computed by Soong may contain paths that cross package-boundaries that would be
-//    unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
-//    (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
-//    than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
-// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//    or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
-//    the local module directory, it will be returned relative to the current package (e.g.
-//    ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
-//    "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
-//    will panic.
+//   - filepath, relative to local module directory, resolves as a filepath relative to the local
+//     source directory
+//   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//     module directory. Because Soong does not have a concept of crossing package boundaries, the
+//     glob as computed by Soong may contain paths that cross package-boundaries that would be
+//     unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
+//     (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
+//     than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
+//   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//     or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
+//     the local module directory, it will be returned relative to the current package (e.g.
+//     ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
+//     "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
+//     will panic.
+//
 // Properties passed as the paths or excludes argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on other modules will have already been handled by the
 // path_deps mutator.
diff --git a/android/bazel_test.go b/android/bazel_test.go
index e14649e..b578cca 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
@@ -158,7 +158,7 @@
 
 type TestBazelConversionContext struct {
 	omc       bazel.OtherModuleTestContext
-	allowlist bp2BuildConversionAllowlist
+	allowlist Bp2BuildConversionAllowlist
 	errors    []string
 }
 
@@ -183,7 +183,7 @@
 func (bcc *TestBazelConversionContext) Config() Config {
 	return Config{
 		&config{
-			bp2buildPackageConfig: bcc.allowlist,
+			Bp2buildPackageConfig: bcc.allowlist,
 		},
 	}
 }
@@ -202,7 +202,7 @@
 		shouldConvert  bool
 		expectedErrors []string
 		module         TestBazelModule
-		allowlist      bp2BuildConversionAllowlist
+		allowlist      Bp2BuildConversionAllowlist
 	}{
 		{
 			description:   "allowlist enables module",
@@ -215,7 +215,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -233,7 +233,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -254,7 +254,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -264,27 +264,6 @@
 			},
 		},
 		{
-			description:    "module in allowlist and existing BUILD file",
-			shouldConvert:  false,
-			expectedErrors: []string{"A module cannot be in a directory listed in keepExistingBuildFile and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
-			module: TestBazelModule{
-				TestModuleInfo: bazel.TestModuleInfo{
-					ModuleName: "foo",
-					Typ:        "rule1",
-					Dir:        "existing/build/dir",
-				},
-				BazelModuleBase: bazelableBazelModuleBase,
-			},
-			allowlist: bp2BuildConversionAllowlist{
-				moduleAlwaysConvert: map[string]bool{
-					"foo": true,
-				},
-				keepExistingBuildFile: map[string]bool{
-					"existing/build/dir": true,
-				},
-			},
-		},
-		{
 			description:    "module allowlist and enabled directory",
 			shouldConvert:  false,
 			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
@@ -296,7 +275,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -317,7 +296,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -344,7 +323,7 @@
 					},
 				},
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -389,7 +368,7 @@
 }
 
 func TestBp2buildAllowList(t *testing.T) {
-	allowlist := getBp2BuildAllowList()
+	allowlist := GetBp2BuildAllowList()
 	for k, v := range allowlists.Bp2buildDefaultConfig {
 		if allowlist.defaultConfig[k] != v {
 			t.Errorf("bp2build default config of %s: expected: %v, got: %v", k, v, allowlist.defaultConfig[k])
@@ -415,9 +394,4 @@
 			t.Errorf("bp2build cc library static only of %s: expected: true, got: %v", k, allowlist.ccLibraryStaticOnly[k])
 		}
 	}
-	for _, k := range allowlists.MixedBuildsDisabledList {
-		if !allowlist.mixedBuildsDisabled[k] {
-			t.Errorf("bp2build mix build disabled of %s: expected: true, got: %v", k, allowlist.mixedBuildsDisabled[k])
-		}
-	}
 }
diff --git a/android/config.go b/android/config.go
index 84c17de..745410f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -20,7 +20,6 @@
 import (
 	"bytes"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -69,6 +68,37 @@
 	*config
 }
 
+type SoongBuildMode int
+
+// Build modes that soong_build can run as.
+const (
+	// Don't use bazel at all during module analysis.
+	AnalysisNoBazel SoongBuildMode = iota
+
+	// Bp2build mode: Generate BUILD files from blueprint files and exit.
+	Bp2build
+
+	// Generate BUILD files which faithfully represent the dependency graph of
+	// blueprint modules. Individual BUILD targets will not, however, faitfhully
+	// express build semantics.
+	GenerateQueryView
+
+	// Create a JSON representation of the module graph and exit.
+	GenerateModuleGraph
+
+	// Generate a documentation file for module type definitions and exit.
+	GenerateDocFile
+
+	// Use bazel during analysis of many allowlisted build modules. The allowlist
+	// is considered a "developer mode" allowlist, as some modules may be
+	// allowlisted on an experimental basis.
+	BazelDevMode
+
+	// Use bazel during analysis of build modules from an allowlist carefully
+	// curated by the build team to be proven stable.
+	BazelProdMode
+)
+
 // SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
@@ -158,8 +188,8 @@
 	fs         pathtools.FileSystem
 	mockBpList string
 
-	runningAsBp2Build              bool
-	bp2buildPackageConfig          bp2BuildConversionAllowlist
+	BuildMode                      SoongBuildMode
+	Bp2buildPackageConfig          Bp2BuildConversionAllowlist
 	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
@@ -172,6 +202,12 @@
 
 	OncePer
 
+	// These fields are only used for metrics collection. A module should be added
+	// to these maps only if its implementation supports Bazel handling in mixed
+	// builds. A module being in the "enabled" list indicates that there is a
+	// variant of that module for which bazel-handling actually took place.
+	// A module being in the "disabled" list indicates that there is a variant of
+	// that module for which bazel-handling was denied.
 	mixedBuildsLock           sync.Mutex
 	mixedBuildEnabledModules  map[string]struct{}
 	mixedBuildDisabledModules map[string]struct{}
@@ -345,126 +381,9 @@
 	}
 }
 
-// TestConfig returns a Config object for testing.
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
-	envCopy := make(map[string]string)
-	for k, v := range env {
-		envCopy[k] = v
-	}
-
-	// Copy the real PATH value to the test environment, it's needed by
-	// NonHermeticHostSystemTool() used in x86_darwin_host.go
-	envCopy["PATH"] = os.Getenv("PATH")
-
-	config := &config{
-		productVariables: productVariables{
-			DeviceName:                          stringPtr("test_device"),
-			DeviceProduct:                       stringPtr("test_product"),
-			Platform_sdk_version:                intPtr(30),
-			Platform_sdk_codename:               stringPtr("S"),
-			Platform_base_sdk_extension_version: intPtr(1),
-			Platform_version_active_codenames:   []string{"S", "Tiramisu"},
-			DeviceSystemSdkVersions:             []string{"14", "15"},
-			Platform_systemsdk_versions:         []string{"29", "30"},
-			AAPTConfig:                          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
-			AAPTPreferredConfig:                 stringPtr("xhdpi"),
-			AAPTCharacteristics:                 stringPtr("nosdcard"),
-			AAPTPrebuiltDPI:                     []string{"xhdpi", "xxhdpi"},
-			UncompressPrivAppDex:                boolPtr(true),
-			ShippingApiLevel:                    stringPtr("30"),
-		},
-
-		outDir:       buildDir,
-		soongOutDir:  filepath.Join(buildDir, "soong"),
-		captureBuild: true,
-		env:          envCopy,
-
-		// Set testAllowNonExistentPaths so that test contexts don't need to specify every path
-		// passed to PathForSource or PathForModuleSrc.
-		TestAllowNonExistentPaths: true,
-
-		BazelContext:              noopBazelContext{},
-		mixedBuildDisabledModules: make(map[string]struct{}),
-		mixedBuildEnabledModules:  make(map[string]struct{}),
-	}
-	config.deviceConfig = &deviceConfig{
-		config: config,
-	}
-	config.TestProductVariables = &config.productVariables
-
-	config.mockFileSystem(bp, fs)
-
-	determineBuildOS(config)
-
-	return Config{config}
-}
-
-func modifyTestConfigToSupportArchMutator(testConfig Config) {
-	config := testConfig.config
-
-	config.Targets = map[OsType][]Target{
-		Android: []Target{
-			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
-			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
-		},
-		config.BuildOS: []Target{
-			{config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
-			{config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
-		},
-	}
-
-	if runtime.GOOS == "darwin" {
-		config.Targets[config.BuildOS] = config.Targets[config.BuildOS][:1]
-	}
-
-	config.BuildOSTarget = config.Targets[config.BuildOS][0]
-	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
-	config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
-	config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
-	config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
-	config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
-	config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
-	config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
-}
-
-func modifyTestConfigForMusl(config Config) {
-	delete(config.Targets, config.BuildOS)
-	config.productVariables.HostMusl = boolPtr(true)
-	determineBuildOS(config.config)
-	config.Targets[config.BuildOS] = []Target{
-		{config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
-		{config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
-	}
-
-	config.BuildOSTarget = config.Targets[config.BuildOS][0]
-	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
-}
-
-// TestArchConfig returns a Config object suitable for using for tests that
-// need to run the arch mutator.
-func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
-	testConfig := TestConfig(buildDir, env, bp, fs)
-	modifyTestConfigToSupportArchMutator(testConfig)
-	return testConfig
-}
-
-// ConfigForAdditionalRun is a config object which is "reset" for another
-// bootstrap run. Only per-run data is reset. Data which needs to persist across
-// multiple runs in the same program execution is carried over (such as Bazel
-// context or environment deps).
-func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.moduleListFile, c.runGoTests, c.outDir, c.soongOutDir, c.env)
-	if err != nil {
-		return Config{}, err
-	}
-	newConfig.BazelContext = c.BazelContext
-	newConfig.envDeps = c.envDeps
-	return newConfig, nil
-}
-
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(moduleListFile string, runGoTests bool, outDir, soongOutDir string, availableEnv map[string]string) (Config, error) {
+func NewConfig(moduleListFile string, buildMode SoongBuildMode, runGoTests bool, outDir, soongOutDir string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
@@ -561,8 +480,9 @@
 		config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	}
 
+	config.BuildMode = buildMode
 	config.BazelContext, err = NewBazelContext(config)
-	config.bp2buildPackageConfig = getBp2BuildAllowList()
+	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
 
 	return Config{config}, err
 }
@@ -597,6 +517,12 @@
 	c.mockBpList = blueprint.MockModuleListFile
 }
 
+// Returns true if "Bazel builds" is enabled. In this mode, part of build
+// analysis is handled by Bazel.
+func (c *config) IsMixedBuildsEnabled() bool {
+	return c.BuildMode == BazelProdMode || c.BuildMode == BazelDevMode
+}
+
 func (c *config) SetAllowMissingDependencies() {
 	c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
 }
@@ -743,8 +669,7 @@
 // DeviceProduct returns the current product target. There could be multiple of
 // these per device type.
 //
-// NOTE: Do not base conditional logic on this value. It may break product
-//       inheritance.
+// NOTE: Do not base conditional logic on this value. It may break product inheritance.
 func (c *config) DeviceProduct() string {
 	return *c.productVariables.DeviceProduct
 }
@@ -806,7 +731,7 @@
 }
 
 func (c *config) MinSupportedSdkVersion() ApiLevel {
-	return uncheckedFinalApiLevel(19)
+	return uncheckedFinalApiLevel(21)
 }
 
 func (c *config) FinalApiLevels() []ApiLevel {
@@ -915,6 +840,15 @@
 	return PathForSource(ctx, filepath.Dir(defaultCert))
 }
 
+// Certificate for the NetworkStack sepolicy context
+func (c *config) MainlineSepolicyDevCertificatesDir(ctx ModuleContext) SourcePath {
+	cert := String(c.productVariables.MainlineSepolicyDevCertificates)
+	if cert != "" {
+		return PathForSource(ctx, cert)
+	}
+	return c.DefaultAppCertificateDir(ctx)
+}
+
 // AllowMissingDependencies configures Blueprint/Soong to not fail when modules
 // are configured to depend on non-existent modules. Note that this does not
 // affect missing input dependencies at the Ninja level.
@@ -1685,6 +1619,10 @@
 	return uncheckedFinalApiLevel(apiLevel)
 }
 
+func (c *deviceConfig) BuildBrokenClangProperty() bool {
+	return c.config.productVariables.BuildBrokenClangProperty
+}
+
 func (c *deviceConfig) BuildBrokenEnforceSyspropOwner() bool {
 	return c.config.productVariables.BuildBrokenEnforceSyspropOwner
 }
@@ -1737,316 +1675,6 @@
 	return c.productVariables.IgnorePrefer32OnDevice
 }
 
-// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
-// Such lists are used in the build system for things like bootclasspath jars or system server jars.
-// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
-// module name. The pairs come from Make product variables as a list of colon-separated strings.
-//
-// Examples:
-//   - "com.android.art:core-oj"
-//   - "platform:framework"
-//   - "system_ext:foo"
-//
-type ConfiguredJarList struct {
-	// A list of apex components, which can be an apex name,
-	// or special names like "platform" or "system_ext".
-	apexes []string
-
-	// A list of jar module name components.
-	jars []string
-}
-
-// Len returns the length of the list of jars.
-func (l *ConfiguredJarList) Len() int {
-	return len(l.jars)
-}
-
-// Jar returns the idx-th jar component of (apex, jar) pairs.
-func (l *ConfiguredJarList) Jar(idx int) string {
-	return l.jars[idx]
-}
-
-// Apex returns the idx-th apex component of (apex, jar) pairs.
-func (l *ConfiguredJarList) Apex(idx int) string {
-	return l.apexes[idx]
-}
-
-// ContainsJar returns true if the (apex, jar) pairs contains a pair with the
-// given jar module name.
-func (l *ConfiguredJarList) ContainsJar(jar string) bool {
-	return InList(jar, l.jars)
-}
-
-// If the list contains the given (apex, jar) pair.
-func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
-	for i := 0; i < l.Len(); i++ {
-		if apex == l.apexes[i] && jar == l.jars[i] {
-			return true
-		}
-	}
-	return false
-}
-
-// ApexOfJar returns the apex component of the first pair with the given jar name on the list, or
-// an empty string if not found.
-func (l *ConfiguredJarList) ApexOfJar(jar string) string {
-	if idx := IndexList(jar, l.jars); idx != -1 {
-		return l.Apex(IndexList(jar, l.jars))
-	}
-	return ""
-}
-
-// IndexOfJar returns the first pair with the given jar name on the list, or -1
-// if not found.
-func (l *ConfiguredJarList) IndexOfJar(jar string) int {
-	return IndexList(jar, l.jars)
-}
-
-func copyAndAppend(list []string, item string) []string {
-	// Create the result list to be 1 longer than the input.
-	result := make([]string, len(list)+1)
-
-	// Copy the whole input list into the result.
-	count := copy(result, list)
-
-	// Insert the extra item at the end.
-	result[count] = item
-
-	return result
-}
-
-// Append an (apex, jar) pair to the list.
-func (l *ConfiguredJarList) Append(apex string, jar string) ConfiguredJarList {
-	// Create a copy of the backing arrays before appending to avoid sharing backing
-	// arrays that are mutated across instances.
-	apexes := copyAndAppend(l.apexes, apex)
-	jars := copyAndAppend(l.jars, jar)
-
-	return ConfiguredJarList{apexes, jars}
-}
-
-// Append a list of (apex, jar) pairs to the list.
-func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
-	apexes := make([]string, 0, l.Len()+other.Len())
-	jars := make([]string, 0, l.Len()+other.Len())
-
-	apexes = append(apexes, l.apexes...)
-	jars = append(jars, l.jars...)
-
-	apexes = append(apexes, other.apexes...)
-	jars = append(jars, other.jars...)
-
-	return ConfiguredJarList{apexes, jars}
-}
-
-// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
-func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
-	apexes := make([]string, 0, l.Len())
-	jars := make([]string, 0, l.Len())
-
-	for i, jar := range l.jars {
-		apex := l.apexes[i]
-		if !list.containsApexJarPair(apex, jar) {
-			apexes = append(apexes, apex)
-			jars = append(jars, jar)
-		}
-	}
-
-	return ConfiguredJarList{apexes, jars}
-}
-
-// Filter keeps the entries if a jar appears in the given list of jars to keep. Returns a new list
-// and any remaining jars that are not on this list.
-func (l *ConfiguredJarList) Filter(jarsToKeep []string) (ConfiguredJarList, []string) {
-	var apexes []string
-	var jars []string
-
-	for i, jar := range l.jars {
-		if InList(jar, jarsToKeep) {
-			apexes = append(apexes, l.apexes[i])
-			jars = append(jars, jar)
-		}
-	}
-
-	return ConfiguredJarList{apexes, jars}, RemoveListFromList(jarsToKeep, jars)
-}
-
-// CopyOfJars returns a copy of the list of strings containing jar module name
-// components.
-func (l *ConfiguredJarList) CopyOfJars() []string {
-	return CopyOf(l.jars)
-}
-
-// CopyOfApexJarPairs returns a copy of the list of strings with colon-separated
-// (apex, jar) pairs.
-func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
-	pairs := make([]string, 0, l.Len())
-
-	for i, jar := range l.jars {
-		apex := l.apexes[i]
-		pairs = append(pairs, apex+":"+jar)
-	}
-
-	return pairs
-}
-
-// BuildPaths returns a list of build paths based on the given directory prefix.
-func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
-	paths := make(WritablePaths, l.Len())
-	for i, jar := range l.jars {
-		paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
-	}
-	return paths
-}
-
-// BuildPathsByModule returns a map from module name to build paths based on the given directory
-// prefix.
-func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
-	paths := map[string]WritablePath{}
-	for _, jar := range l.jars {
-		paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
-	}
-	return paths
-}
-
-// UnmarshalJSON converts JSON configuration from raw bytes into a
-// ConfiguredJarList structure.
-func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
-	// Try and unmarshal into a []string each item of which contains a pair
-	// <apex>:<jar>.
-	var list []string
-	err := json.Unmarshal(b, &list)
-	if err != nil {
-		// Did not work so return
-		return err
-	}
-
-	apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
-	if err != nil {
-		return err
-	}
-	l.apexes = apexes
-	l.jars = jars
-	return nil
-}
-
-func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) {
-	if len(l.apexes) != len(l.jars) {
-		return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars))
-	}
-
-	list := make([]string, 0, len(l.apexes))
-
-	for i := 0; i < len(l.apexes); i++ {
-		list = append(list, l.apexes[i]+":"+l.jars[i])
-	}
-
-	return json.Marshal(list)
-}
-
-// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
-//
-// TODO(b/139391334): hard coded until we find a good way to query the stem of a
-// module before any other mutators are run.
-func ModuleStem(module string) string {
-	if module == "framework-minus-apex" {
-		return "framework"
-	}
-	return module
-}
-
-// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
-// based on the operating system.
-func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
-	paths := make([]string, l.Len())
-	for i, jar := range l.jars {
-		apex := l.apexes[i]
-		name := ModuleStem(jar) + ".jar"
-
-		var subdir string
-		if apex == "platform" {
-			subdir = "system/framework"
-		} else if apex == "system_ext" {
-			subdir = "system_ext/framework"
-		} else {
-			subdir = filepath.Join("apex", apex, "javalib")
-		}
-
-		if ostype.Class == Host {
-			paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
-		} else {
-			paths[i] = filepath.Join("/", subdir, name)
-		}
-	}
-	return paths
-}
-
-func (l *ConfiguredJarList) String() string {
-	var pairs []string
-	for i := 0; i < l.Len(); i++ {
-		pairs = append(pairs, l.apexes[i]+":"+l.jars[i])
-	}
-	return strings.Join(pairs, ",")
-}
-
-func splitListOfPairsIntoPairOfLists(list []string) ([]string, []string, error) {
-	// Now we need to populate this list by splitting each item in the slice of
-	// pairs and appending them to the appropriate list of apexes or jars.
-	apexes := make([]string, len(list))
-	jars := make([]string, len(list))
-
-	for i, apexjar := range list {
-		apex, jar, err := splitConfiguredJarPair(apexjar)
-		if err != nil {
-			return nil, nil, err
-		}
-		apexes[i] = apex
-		jars[i] = jar
-	}
-
-	return apexes, jars, nil
-}
-
-// Expected format for apexJarValue = <apex name>:<jar name>
-func splitConfiguredJarPair(str string) (string, string, error) {
-	pair := strings.SplitN(str, ":", 2)
-	if len(pair) == 2 {
-		apex := pair[0]
-		jar := pair[1]
-		if apex == "" {
-			return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
-		}
-		return apex, jar, nil
-	} else {
-		return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
-	}
-}
-
-// CreateTestConfiguredJarList is a function to create ConfiguredJarList for tests.
-func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
-	// Create the ConfiguredJarList in as similar way as it is created at runtime by marshalling to
-	// a json list of strings and then unmarshalling into a ConfiguredJarList instance.
-	b, err := json.Marshal(list)
-	if err != nil {
-		panic(err)
-	}
-
-	var jarList ConfiguredJarList
-	err = json.Unmarshal(b, &jarList)
-	if err != nil {
-		panic(err)
-	}
-
-	return jarList
-}
-
-// EmptyConfiguredJarList returns an empty jar list.
-func EmptyConfiguredJarList() ConfiguredJarList {
-	return ConfiguredJarList{}
-}
-
-var earlyBootJarsKey = NewOnceKey("earlyBootJars")
-
 func (c *config) BootJars() []string {
 	return c.Once(earlyBootJarsKey, func() interface{} {
 		list := c.productVariables.BootJars.CopyOfJars()
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
index 748be62..d6b2bcf 100644
--- a/android/config_bp2build.go
+++ b/android/config_bp2build.go
@@ -240,10 +240,12 @@
 // ExportedVariableReferenceDictVariables is a mapping from variable names to a
 // dictionary which references previously defined variables. This is used to
 // create a Starlark output such as:
-// 		string_var1 = "string1
-// 		var_ref_dict_var1 = {
-// 			"key1": string_var1
-// 		}
+//
+//	string_var1 = "string1
+//	var_ref_dict_var1 = {
+//		"key1": string_var1
+//	}
+//
 // This type of variable collection must be expanded last so that it recognizes
 // previously defined variables.
 type ExportedVariableReferenceDictVariables map[string]map[string]string
diff --git a/android/configured_jars.go b/android/configured_jars.go
new file mode 100644
index 0000000..53fef05
--- /dev/null
+++ b/android/configured_jars.go
@@ -0,0 +1,314 @@
+// 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 android
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"path/filepath"
+	"strings"
+)
+
+// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
+// Such lists are used in the build system for things like bootclasspath jars or system server jars.
+// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
+// module name. The pairs come from Make product variables as a list of colon-separated strings.
+//
+// Examples:
+//   - "com.android.art:core-oj"
+//   - "platform:framework"
+//   - "system_ext:foo"
+type ConfiguredJarList struct {
+	// A list of apex components, which can be an apex name,
+	// or special names like "platform" or "system_ext".
+	apexes []string
+
+	// A list of jar module name components.
+	jars []string
+}
+
+// Len returns the length of the list of jars.
+func (l *ConfiguredJarList) Len() int {
+	return len(l.jars)
+}
+
+// Jar returns the idx-th jar component of (apex, jar) pairs.
+func (l *ConfiguredJarList) Jar(idx int) string {
+	return l.jars[idx]
+}
+
+// Apex returns the idx-th apex component of (apex, jar) pairs.
+func (l *ConfiguredJarList) Apex(idx int) string {
+	return l.apexes[idx]
+}
+
+// ContainsJar returns true if the (apex, jar) pairs contains a pair with the
+// given jar module name.
+func (l *ConfiguredJarList) ContainsJar(jar string) bool {
+	return InList(jar, l.jars)
+}
+
+// If the list contains the given (apex, jar) pair.
+func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
+	for i := 0; i < l.Len(); i++ {
+		if apex == l.apexes[i] && jar == l.jars[i] {
+			return true
+		}
+	}
+	return false
+}
+
+// ApexOfJar returns the apex component of the first pair with the given jar name on the list, or
+// an empty string if not found.
+func (l *ConfiguredJarList) ApexOfJar(jar string) string {
+	if idx := IndexList(jar, l.jars); idx != -1 {
+		return l.Apex(IndexList(jar, l.jars))
+	}
+	return ""
+}
+
+// IndexOfJar returns the first pair with the given jar name on the list, or -1
+// if not found.
+func (l *ConfiguredJarList) IndexOfJar(jar string) int {
+	return IndexList(jar, l.jars)
+}
+
+func copyAndAppend(list []string, item string) []string {
+	// Create the result list to be 1 longer than the input.
+	result := make([]string, len(list)+1)
+
+	// Copy the whole input list into the result.
+	count := copy(result, list)
+
+	// Insert the extra item at the end.
+	result[count] = item
+
+	return result
+}
+
+// Append an (apex, jar) pair to the list.
+func (l *ConfiguredJarList) Append(apex string, jar string) ConfiguredJarList {
+	// Create a copy of the backing arrays before appending to avoid sharing backing
+	// arrays that are mutated across instances.
+	apexes := copyAndAppend(l.apexes, apex)
+	jars := copyAndAppend(l.jars, jar)
+
+	return ConfiguredJarList{apexes, jars}
+}
+
+// Append a list of (apex, jar) pairs to the list.
+func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
+	apexes := make([]string, 0, l.Len()+other.Len())
+	jars := make([]string, 0, l.Len()+other.Len())
+
+	apexes = append(apexes, l.apexes...)
+	jars = append(jars, l.jars...)
+
+	apexes = append(apexes, other.apexes...)
+	jars = append(jars, other.jars...)
+
+	return ConfiguredJarList{apexes, jars}
+}
+
+// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
+func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
+	apexes := make([]string, 0, l.Len())
+	jars := make([]string, 0, l.Len())
+
+	for i, jar := range l.jars {
+		apex := l.apexes[i]
+		if !list.containsApexJarPair(apex, jar) {
+			apexes = append(apexes, apex)
+			jars = append(jars, jar)
+		}
+	}
+
+	return ConfiguredJarList{apexes, jars}
+}
+
+// Filter keeps the entries if a jar appears in the given list of jars to keep. Returns a new list
+// and any remaining jars that are not on this list.
+func (l *ConfiguredJarList) Filter(jarsToKeep []string) (ConfiguredJarList, []string) {
+	var apexes []string
+	var jars []string
+
+	for i, jar := range l.jars {
+		if InList(jar, jarsToKeep) {
+			apexes = append(apexes, l.apexes[i])
+			jars = append(jars, jar)
+		}
+	}
+
+	return ConfiguredJarList{apexes, jars}, RemoveListFromList(jarsToKeep, jars)
+}
+
+// CopyOfJars returns a copy of the list of strings containing jar module name
+// components.
+func (l *ConfiguredJarList) CopyOfJars() []string {
+	return CopyOf(l.jars)
+}
+
+// CopyOfApexJarPairs returns a copy of the list of strings with colon-separated
+// (apex, jar) pairs.
+func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
+	pairs := make([]string, 0, l.Len())
+
+	for i, jar := range l.jars {
+		apex := l.apexes[i]
+		pairs = append(pairs, apex+":"+jar)
+	}
+
+	return pairs
+}
+
+// BuildPaths returns a list of build paths based on the given directory prefix.
+func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
+	paths := make(WritablePaths, l.Len())
+	for i, jar := range l.jars {
+		paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
+	}
+	return paths
+}
+
+// BuildPathsByModule returns a map from module name to build paths based on the given directory
+// prefix.
+func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
+	paths := map[string]WritablePath{}
+	for _, jar := range l.jars {
+		paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
+	}
+	return paths
+}
+
+// UnmarshalJSON converts JSON configuration from raw bytes into a
+// ConfiguredJarList structure.
+func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
+	// Try and unmarshal into a []string each item of which contains a pair
+	// <apex>:<jar>.
+	var list []string
+	err := json.Unmarshal(b, &list)
+	if err != nil {
+		// Did not work so return
+		return err
+	}
+
+	apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
+	if err != nil {
+		return err
+	}
+	l.apexes = apexes
+	l.jars = jars
+	return nil
+}
+
+func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) {
+	if len(l.apexes) != len(l.jars) {
+		return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars))
+	}
+
+	list := make([]string, 0, len(l.apexes))
+
+	for i := 0; i < len(l.apexes); i++ {
+		list = append(list, l.apexes[i]+":"+l.jars[i])
+	}
+
+	return json.Marshal(list)
+}
+
+// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
+//
+// TODO(b/139391334): hard coded until we find a good way to query the stem of a
+// module before any other mutators are run.
+func ModuleStem(module string) string {
+	if module == "framework-minus-apex" {
+		return "framework"
+	}
+	return module
+}
+
+// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
+// based on the operating system.
+func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
+	paths := make([]string, l.Len())
+	for i, jar := range l.jars {
+		apex := l.apexes[i]
+		name := ModuleStem(jar) + ".jar"
+
+		var subdir string
+		if apex == "platform" {
+			subdir = "system/framework"
+		} else if apex == "system_ext" {
+			subdir = "system_ext/framework"
+		} else {
+			subdir = filepath.Join("apex", apex, "javalib")
+		}
+
+		if ostype.Class == Host {
+			paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
+		} else {
+			paths[i] = filepath.Join("/", subdir, name)
+		}
+	}
+	return paths
+}
+
+func (l *ConfiguredJarList) String() string {
+	var pairs []string
+	for i := 0; i < l.Len(); i++ {
+		pairs = append(pairs, l.apexes[i]+":"+l.jars[i])
+	}
+	return strings.Join(pairs, ",")
+}
+
+func splitListOfPairsIntoPairOfLists(list []string) ([]string, []string, error) {
+	// Now we need to populate this list by splitting each item in the slice of
+	// pairs and appending them to the appropriate list of apexes or jars.
+	apexes := make([]string, len(list))
+	jars := make([]string, len(list))
+
+	for i, apexjar := range list {
+		apex, jar, err := splitConfiguredJarPair(apexjar)
+		if err != nil {
+			return nil, nil, err
+		}
+		apexes[i] = apex
+		jars[i] = jar
+	}
+
+	return apexes, jars, nil
+}
+
+// Expected format for apexJarValue = <apex name>:<jar name>
+func splitConfiguredJarPair(str string) (string, string, error) {
+	pair := strings.SplitN(str, ":", 2)
+	if len(pair) == 2 {
+		apex := pair[0]
+		jar := pair[1]
+		if apex == "" {
+			return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
+		}
+		return apex, jar, nil
+	} else {
+		return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
+	}
+}
+
+// EmptyConfiguredJarList returns an empty jar list.
+func EmptyConfiguredJarList() ConfiguredJarList {
+	return ConfiguredJarList{}
+}
+
+var earlyBootJarsKey = NewOnceKey("earlyBootJars")
diff --git a/android/defaults.go b/android/defaults.go
index 54f44bc..7906e94 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -151,12 +151,12 @@
 // retrieve the values it is necessary to iterate over properties(). E.g. to get
 // the commonProperties instance that have the real values:
 //
-//   d := myModule.(Defaults)
-//   for _, props := range d.properties() {
-//     if cp, ok := props.(*commonProperties); ok {
-//       ... access property values in cp ...
-//     }
-//   }
+//	d := myModule.(Defaults)
+//	for _, props := range d.properties() {
+//	  if cp, ok := props.(*commonProperties); ok {
+//	    ... access property values in cp ...
+//	  }
+//	}
 //
 // The rationale is that the properties on a defaults module apply to the
 // defaultable modules using it, not to the defaults module itself. E.g. setting
@@ -448,7 +448,7 @@
 	}
 
 	for _, defaults := range defaultsList {
-		if ctx.Config().runningAsBp2Build {
+		if ctx.Config().BuildMode == Bp2build {
 			applyNamespacedVariableDefaults(defaults, ctx)
 		}
 
diff --git a/android/defs.go b/android/defs.go
index 362b382..2a28e98 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -68,7 +68,7 @@
 
 	CpExecutable = pctx.AndroidStaticRule("CpExecutable",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out$extraCmds",
+			Command:     "rm -f $out && cp $cpFlags $in $out && chmod +x $out$extraCmds",
 			Description: "cp $out",
 		},
 		"cpFlags", "extraCmds")
diff --git a/android/filegroup.go b/android/filegroup.go
index 9e5769a..e609f63 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"path/filepath"
+	"regexp"
 	"strings"
 
 	"android/soong/bazel"
@@ -36,11 +38,44 @@
 	return ctx.OtherModuleType(m) == "filegroup"
 }
 
+var (
+	// ignoring case, checks for proto or protos as an independent word in the name, whether at the
+	// beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
+	filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
+	filegroupLikelyAidlPattern  = regexp.MustCompile("(?i)(^|[^a-z])aidl([^a-z]|$)")
+
+	ProtoSrcLabelPartition = bazel.LabelPartition{
+		Extensions:  []string{".proto"},
+		LabelMapper: isFilegroupWithPattern(filegroupLikelyProtoPattern),
+	}
+	AidlSrcLabelPartition = bazel.LabelPartition{
+		Extensions:  []string{".aidl"},
+		LabelMapper: isFilegroupWithPattern(filegroupLikelyAidlPattern),
+	}
+)
+
+func isFilegroupWithPattern(pattern *regexp.Regexp) bazel.LabelMapper {
+	return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+		m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+		labelStr := label.Label
+		if !exists || !IsFilegroup(ctx, m) {
+			return labelStr, false
+		}
+		likelyMatched := pattern.MatchString(label.OriginalModuleName)
+		return labelStr, likelyMatched
+	}
+}
+
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
 	Srcs bazel.LabelListAttribute
 }
 
+type bazelAidlLibraryAttributes struct {
+	Srcs                bazel.LabelListAttribute
+	Strip_import_prefix *string
+}
+
 // ConvertWithBp2build performs bp2build conversion of filegroup
 func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
@@ -66,16 +101,33 @@
 		}
 	}
 
-	attrs := &bazelFilegroupAttributes{
-		Srcs: srcs,
-	}
+	// Convert module that has only AIDL files to aidl_library
+	// If the module has a mixed bag of AIDL and non-AIDL files, split the filegroup manually
+	// and then convert
+	if fg.ShouldConvertToAidlLibrary(ctx) {
+		attrs := &bazelAidlLibraryAttributes{
+			Srcs:                srcs,
+			Strip_import_prefix: fg.properties.Path,
+		}
 
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "filegroup",
-		Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
-	}
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "aidl_library",
+			Bzl_load_location: "//build/bazel/rules/aidl:library.bzl",
+		}
 
-	ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
+		ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
+	} else {
+		attrs := &bazelFilegroupAttributes{
+			Srcs: srcs,
+		}
+
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "filegroup",
+			Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
+		}
+
+		ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
+	}
 }
 
 type fileGroupProperties struct {
@@ -98,12 +150,14 @@
 type fileGroup struct {
 	ModuleBase
 	BazelModuleBase
+	Bp2buildAidlLibrary
 	properties fileGroupProperties
 	srcs       Paths
 }
 
 var _ MixedBuildBuildable = (*fileGroup)(nil)
 var _ SourceFileProducer = (*fileGroup)(nil)
+var _ Bp2buildAidlLibrary = (*fileGroup)(nil)
 
 // filegroup contains a list of files that are referenced by other modules
 // properties (such as "srcs") using the syntax ":<name>". filegroup are
@@ -164,12 +218,17 @@
 }
 
 func (fg *fileGroup) ProcessBazelQueryResponse(ctx ModuleContext) {
-	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
+	bazelCtx := ctx.Config().BazelContext
+	// This is a short-term solution because we rely on info from Android.bp to handle
+	// a converted module. This will block when we want to remove Android.bp for all
+	// converted modules at some point.
+	// TODO(b/242847534): Implement a long-term solution in which we don't need to rely
+	// on info form Android.bp for modules that are already converted to Bazel
+	relativeRoot := ctx.ModuleDir()
 	if fg.properties.Path != nil {
-		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
+		relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path)
 	}
 
-	bazelCtx := ctx.Config().BazelContext
 	filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{Common.String(), CommonOS})
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
@@ -178,8 +237,40 @@
 
 	bazelOuts := make(Paths, 0, len(filePaths))
 	for _, p := range filePaths {
-		bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, ctx.ModuleDir(), p))
+		bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, relativeRoot, p))
 	}
-
 	fg.srcs = bazelOuts
 }
+
+func (fg *fileGroup) ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool {
+	if len(fg.properties.Srcs) == 0 || !fg.ShouldConvertWithBp2build(ctx) {
+		return false
+	}
+	for _, src := range fg.properties.Srcs {
+		if !strings.HasSuffix(src, ".aidl") {
+			return false
+		}
+	}
+	return true
+}
+
+func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string {
+	if ctx.OtherModuleDir(fg.module) == ctx.ModuleDir() {
+		return ":" + fg.Name()
+	} else {
+		return fg.GetBazelLabel(ctx, fg)
+	}
+}
+
+// Given a name in srcs prop, check to see if the name references a filegroup
+// and the filegroup is converted to aidl_library
+func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool {
+	if module, ok := ctx.ModuleFromName(name); ok {
+		if IsFilegroup(ctx, module) {
+			if fg, ok := module.(Bp2buildAidlLibrary); ok {
+				return fg.ShouldConvertToAidlLibrary(ctx)
+			}
+		}
+	}
+	return false
+}
diff --git a/android/filegroup_test.go b/android/filegroup_test.go
new file mode 100644
index 0000000..a7ea805
--- /dev/null
+++ b/android/filegroup_test.go
@@ -0,0 +1,58 @@
+package android
+
+import (
+	"path/filepath"
+	"testing"
+)
+
+func TestFileGroupWithPathProp(t *testing.T) {
+	outBaseDir := "outputbase"
+	pathPrefix := outBaseDir + "/execroot/__main__"
+	expectedOutputfile := filepath.Join(pathPrefix, "a/b/c/d/test.aidl")
+
+	testCases := []struct {
+		bp  string
+		rel string
+	}{
+		{
+			bp: `
+	filegroup {
+		name: "baz",
+		srcs: ["a/b/c/d/test.aidl"],
+		path: "a/b",
+		bazel_module: { label: "//:baz" },
+	}
+`,
+			rel: "c/d/test.aidl",
+		},
+		{
+			bp: `
+	filegroup {
+		name: "baz",
+		srcs: ["a/b/c/d/test.aidl"],
+		bazel_module: { label: "//:baz" },
+	}
+`,
+			rel: "a/b/c/d/test.aidl",
+		},
+	}
+
+	for _, testCase := range testCases {
+		outBaseDir := "outputbase"
+		result := GroupFixturePreparers(
+			PrepareForTestWithFilegroup,
+			FixtureModifyConfig(func(config Config) {
+				config.BazelContext = MockBazelContext{
+					OutputBaseDir: outBaseDir,
+					LabelToOutputFiles: map[string][]string{
+						"//:baz": []string{"a/b/c/d/test.aidl"},
+					},
+				}
+			}),
+		).RunTestWithBp(t, testCase.bp)
+
+		fg := result.Module("baz", "").(*fileGroup)
+		AssertStringEquals(t, "src relativeRoot", testCase.rel, fg.srcs[0].Rel())
+		AssertStringEquals(t, "src full path", expectedOutputfile, fg.srcs[0].String())
+	}
+}
diff --git a/android/fixture.go b/android/fixture.go
index 0690a5a..f33e718 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -420,11 +420,13 @@
 // instances.
 //
 // base      - a list of already flattened and deduped preparers that will be applied first before
-//             the list of additional preparers. Any duplicates of these in the additional preparers
-//             will be ignored.
+//
+//	the list of additional preparers. Any duplicates of these in the additional preparers
+//	will be ignored.
 //
 // preparers - a list of additional unflattened, undeduped preparers that will be applied after the
-//             base preparers.
+//
+//	base preparers.
 //
 // Returns a deduped and flattened list of the preparers starting with the ones in base with any
 // additional ones from the preparers list added afterwards.
@@ -498,10 +500,10 @@
 // FixtureErrorHandler determines how to respond to errors reported by the code under test.
 //
 // Some possible responses:
-// * Fail the test if any errors are reported, see FixtureExpectsNoErrors.
-// * Fail the test if at least one error that matches a pattern is not reported see
-//   FixtureExpectsAtLeastOneErrorMatchingPattern
-// * Fail the test if any unexpected errors are reported.
+//   - Fail the test if any errors are reported, see FixtureExpectsNoErrors.
+//   - Fail the test if at least one error that matches a pattern is not reported see
+//     FixtureExpectsAtLeastOneErrorMatchingPattern
+//   - Fail the test if any unexpected errors are reported.
 //
 // Although at the moment all the error handlers are implemented as simply a wrapper around a
 // function this is defined as an interface to allow future enhancements, e.g. provide different
@@ -640,6 +642,20 @@
 	NinjaDeps []string
 }
 
+type TestPathContext struct {
+	*TestResult
+}
+
+var _ PathContext = &TestPathContext{}
+
+func (t *TestPathContext) Config() Config {
+	return t.TestResult.Config
+}
+
+func (t *TestPathContext) AddNinjaFileDeps(deps ...string) {
+	panic("unimplemented")
+}
+
 func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture {
 	config := TestConfig(buildDir, nil, "", nil)
 	ctx := NewTestContext(config)
@@ -866,10 +882,12 @@
 // that produced this result.
 //
 // e.g. assuming that this result was created by running:
-//     GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t)
+//
+//	GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t)
 //
 // Then this method will be equivalent to running:
-//     GroupFixturePreparers(preparer1, preparer2, preparer3)
+//
+//	GroupFixturePreparers(preparer1, preparer2, preparer3)
 //
 // This is intended for use by tests whose output is Android.bp files to verify that those files
 // are valid, e.g. tests of the snapshots produced by the sdk module type.
diff --git a/android/gen_notice.go b/android/gen_notice.go
index e2b839f..2eb6bec 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -179,7 +179,7 @@
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
-	initAndroidModuleBase(module)
+	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
 	InitDefaultableModule(module)
 
 	return module
diff --git a/android/gen_notice_test.go b/android/gen_notice_test.go
index 4ad2ecf..b45ce4f 100644
--- a/android/gen_notice_test.go
+++ b/android/gen_notice_test.go
@@ -12,19 +12,6 @@
 	expectedErrors []string
 }{
 	{
-		name: "gen_notice must not accept licenses property",
-		fs: map[string][]byte{
-			"top/Android.bp": []byte(`
-				gen_notice {
-					name: "top_license",
-					licenses: ["other_license"],
-				}`),
-		},
-		expectedErrors: []string{
-			`unrecognized property "licenses"`,
-		},
-	},
-	{
 		name: "bad gen_notice",
 		fs: map[string][]byte{
 			"top/Android.bp": []byte(`
diff --git a/android/module.go b/android/module.go
index 450dba9..5908233 100644
--- a/android/module.go
+++ b/android/module.go
@@ -936,6 +936,27 @@
 	Dists []Dist `android:"arch_variant"`
 }
 
+// CommonTestOptions represents the common `test_options` properties in
+// Android.bp.
+type CommonTestOptions struct {
+	// If the test is a hostside (no device required) unittest that shall be run
+	// during presubmit check.
+	Unit_test *bool
+
+	// Tags provide additional metadata to customize test execution by downstream
+	// test runners. The tags have no special meaning to Soong.
+	Tags []string
+}
+
+// SetAndroidMkEntries sets AndroidMkEntries according to the value of base
+// `test_options`.
+func (t *CommonTestOptions) SetAndroidMkEntries(entries *AndroidMkEntries) {
+	entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(t.Unit_test))
+	if len(t.Tags) > 0 {
+		entries.AddStrings("LOCAL_TEST_OPTIONS_TAGS", t.Tags...)
+	}
+}
+
 // The key to use in TaggedDistFiles when a Dist structure does not specify a
 // tag property. This intentionally does not use "" as the default because that
 // would mean that an empty tag would have a different meaning when used in a dist
@@ -1095,7 +1116,7 @@
 // property structs for architecture-specific versions of generic properties tagged with
 // `android:"arch_variant"`.
 //
-//  InitAndroidModule should not be called if InitAndroidArchModule was called.
+//	InitAndroidModule should not be called if InitAndroidArchModule was called.
 func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
 	InitAndroidModule(m)
 
@@ -1336,30 +1357,30 @@
 //
 // For example:
 //
-//     import (
-//         "android/soong/android"
-//     )
+//	import (
+//	    "android/soong/android"
+//	)
 //
-//     type myModule struct {
-//         android.ModuleBase
-//         properties struct {
-//             MyProperty string
-//         }
-//     }
+//	type myModule struct {
+//	    android.ModuleBase
+//	    properties struct {
+//	        MyProperty string
+//	    }
+//	}
 //
-//     func NewMyModule() android.Module {
-//         m := &myModule{}
-//         m.AddProperties(&m.properties)
-//         android.InitAndroidModule(m)
-//         return m
-//     }
+//	func NewMyModule() android.Module {
+//	    m := &myModule{}
+//	    m.AddProperties(&m.properties)
+//	    android.InitAndroidModule(m)
+//	    return m
+//	}
 //
-//     func (m *myModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-//         // Get the CPU architecture for the current build variant.
-//         variantArch := ctx.Arch()
+//	func (m *myModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+//	    // Get the CPU architecture for the current build variant.
+//	    variantArch := ctx.Arch()
 //
-//         // ...
-//     }
+//	    // ...
+//	}
 type ModuleBase struct {
 	// Putting the curiously recurring thing pointing to the thing that contains
 	// the thing pattern to good use.
@@ -2353,9 +2374,6 @@
 }
 
 func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
-	if !ctx.Config().BazelContext.BazelEnabled() {
-		return nil, false
-	}
 	if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
 		if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
 			return mixedBuildMod, true
diff --git a/android/module_test.go b/android/module_test.go
index 77ef146..0580bef 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -911,3 +911,70 @@
 		})
 	}
 }
+
+func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) {
+	tests := []struct {
+		name        string
+		testOptions CommonTestOptions
+		expected    map[string][]string
+	}{
+		{
+			name:        "empty",
+			testOptions: CommonTestOptions{},
+			expected:    map[string][]string{},
+		},
+		{
+			name: "is unit test",
+			testOptions: CommonTestOptions{
+				Unit_test: boolPtr(true),
+			},
+			expected: map[string][]string{
+				"LOCAL_IS_UNIT_TEST": []string{"true"},
+			},
+		},
+		{
+			name: "is not unit test",
+			testOptions: CommonTestOptions{
+				Unit_test: boolPtr(false),
+			},
+			expected: map[string][]string{},
+		},
+		{
+			name: "empty tag",
+			testOptions: CommonTestOptions{
+				Tags: []string{},
+			},
+			expected: map[string][]string{},
+		},
+		{
+			name: "single tag",
+			testOptions: CommonTestOptions{
+				Tags: []string{"tag1"},
+			},
+			expected: map[string][]string{
+				"LOCAL_TEST_OPTIONS_TAGS": []string{"tag1"},
+			},
+		},
+		{
+			name: "multiple tag",
+			testOptions: CommonTestOptions{
+				Tags: []string{"tag1", "tag2", "tag3"},
+			},
+			expected: map[string][]string{
+				"LOCAL_TEST_OPTIONS_TAGS": []string{"tag1", "tag2", "tag3"},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			actualEntries := AndroidMkEntries{
+				EntryMap: map[string][]string{},
+			}
+			tt.testOptions.SetAndroidMkEntries(&actualEntries)
+			actual := actualEntries.EntryMap
+			t.Logf("actual: %v", actual)
+			t.Logf("expected: %v", tt.expected)
+			AssertDeepEquals(t, "TestProcessCommonTestOptions ", tt.expected, actual)
+		})
+	}
+}
diff --git a/android/paths.go b/android/paths.go
index f8e7018..27f4bf5 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -387,20 +387,21 @@
 }
 
 // PathsForModuleSrc returns a Paths{} containing the resolved references in paths:
-// * filepath, relative to local module directory, resolves as a filepath relative to the local
-//   source directory
-// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
-//  source directory.
-// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//    or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
-//    filepath.
+//   - filepath, relative to local module directory, resolves as a filepath relative to the local
+//     source directory
+//   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//     source directory.
+//   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//     or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+//     filepath.
+//
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
 // path_deps mutator.
 // If a requested module is not found as a dependency:
-//   * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
+//   - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
 //     missing dependencies
-//   * otherwise, a ModuleError is thrown.
+//   - otherwise, a ModuleError is thrown.
 func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths {
 	return PathsForModuleSrcExcludes(ctx, paths, nil)
 }
@@ -414,21 +415,22 @@
 
 // PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
 // those listed in excludes. Elements of paths and excludes are resolved as:
-// * filepath, relative to local module directory, resolves as a filepath relative to the local
-//   source directory
-// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
-//  source directory. Not valid in excludes.
-// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//    or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
-//    filepath.
+//   - filepath, relative to local module directory, resolves as a filepath relative to the local
+//     source directory
+//   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//     source directory. Not valid in excludes.
+//   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//     or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+//     filepath.
+//
 // excluding the items (similarly resolved
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
 // path_deps mutator.
 // If a requested module is not found as a dependency:
-//   * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
+//   - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
 //     missing dependencies
-//   * otherwise, a ModuleError is thrown.
+//   - otherwise, a ModuleError is thrown.
 func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
 	return PathsRelativeToModuleSourceDir(SourceInput{
 		Context:      ctx,
@@ -548,13 +550,14 @@
 
 // PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in
 // paths, minus those listed in excludes. Elements of paths and excludes are resolved as:
-// * filepath, relative to local module directory, resolves as a filepath relative to the local
-//   source directory
-// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
-//  source directory. Not valid in excludes.
-// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//    or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
-//    filepath.
+//   - filepath, relative to local module directory, resolves as a filepath relative to the local
+//     source directory
+//   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//     source directory. Not valid in excludes.
+//   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//     or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+//     filepath.
+//
 // and a list of the module names of missing module dependencies are returned as the second return.
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
@@ -1471,41 +1474,6 @@
 	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
 }
 
-// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
-// reference abi dump for the given module. This is not guaranteed to be valid.
-func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
-	isNdk, isVndk, isGzip bool) OptionalPath {
-
-	currentArchType := ctx.Arch().ArchType
-	primaryArchType := ctx.Config().DevicePrimaryArchType()
-	archName := currentArchType.String()
-	if currentArchType != primaryArchType {
-		archName += "_" + primaryArchType.String()
-	}
-
-	var dirName string
-	if isNdk {
-		dirName = "ndk"
-	} else if isVndk {
-		dirName = "vndk"
-	} else {
-		dirName = "platform" // opt-in libs
-	}
-
-	binderBitness := ctx.DeviceConfig().BinderBitness()
-
-	var ext string
-	if isGzip {
-		ext = ".lsdump.gz"
-	} else {
-		ext = ".lsdump"
-	}
-
-	return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
-		version, binderBitness, archName, "source-based",
-		fileName+ext)
-}
-
 // PathForModuleOut returns a Path representing the paths... under the module's
 // output directory.
 func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath {
@@ -1975,6 +1943,18 @@
 	return testPath{basePath{path: p, rel: p}}
 }
 
+func PathForTestingWithRel(path, rel string) Path {
+	p, err := validateSafePath(path, rel)
+	if err != nil {
+		panic(err)
+	}
+	r, err := validatePath(rel)
+	if err != nil {
+		panic(err)
+	}
+	return testPath{basePath{path: p, rel: r}}
+}
+
 // PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
 func PathsForTesting(strs ...string) Paths {
 	p := make(Paths, len(strs))
diff --git a/android/proto.go b/android/proto.go
index c3759f8..25cecf4 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -15,10 +15,10 @@
 package android
 
 import (
-	"android/soong/bazel"
-	"regexp"
 	"strings"
 
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -27,14 +27,6 @@
 	canonicalPathFromRootDefault = true
 )
 
-var (
-	// ignoring case, checks for proto or protos as an independent word in the name, whether at the
-	// beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
-	filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
-
-	ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup}
-)
-
 // TODO(ccross): protos are often used to communicate between multiple modules.  If the only
 // way to convert a proto to source is to reference it as a source file, and external modules cannot
 // reference source files in other modules, then every module that owns a proto file will need to
@@ -170,6 +162,13 @@
 type protoAttrs struct {
 	Srcs                bazel.LabelListAttribute
 	Strip_import_prefix *string
+	Deps                bazel.LabelListAttribute
+}
+
+// For each package in the include_dirs property a proto_library target should
+// be added to the BUILD file in that package and a mapping should be added here
+var includeDirsToProtoDeps = map[string]string{
+	"external/protobuf/src": "//external/protobuf:libprotobuf-proto",
 }
 
 // Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
@@ -200,6 +199,14 @@
 					path := ""
 					attrs.Strip_import_prefix = &path
 				}
+
+				for _, dir := range props.Proto.Include_dirs {
+					if dep, ok := includeDirsToProtoDeps[dir]; ok {
+						attrs.Deps.Add(bazel.MakeLabelAttribute(dep))
+					} else {
+						ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir)
+					}
+				}
 			} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
 				ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
 			}
@@ -213,13 +220,3 @@
 
 	return info, true
 }
-
-func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
-	m, exists := ctx.ModuleFromName(label.OriginalModuleName)
-	labelStr := label.Label
-	if !exists || !IsFilegroup(ctx, m) {
-		return labelStr, false
-	}
-	likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
-	return labelStr, likelyProtos
-}
diff --git a/android/register.go b/android/register.go
index 4ff8fff..d4ce5f1 100644
--- a/android/register.go
+++ b/android/register.go
@@ -164,10 +164,6 @@
 	return ctx
 }
 
-func (ctx *Context) SetRunningAsBp2build() {
-	ctx.config.runningAsBp2Build = true
-}
-
 // RegisterForBazelConversion registers an alternate shadow pipeline of
 // singletons, module types and mutators to register for converting Blueprint
 // files to semantically equivalent BUILD files.
@@ -258,20 +254,20 @@
 
 // Used to register build components from an init() method, e.g.
 //
-// init() {
-//   RegisterBuildComponents(android.InitRegistrationContext)
-// }
+//	init() {
+//	  RegisterBuildComponents(android.InitRegistrationContext)
+//	}
 //
-// func RegisterBuildComponents(ctx android.RegistrationContext) {
-//   ctx.RegisterModuleType(...)
-//   ...
-// }
+//	func RegisterBuildComponents(ctx android.RegistrationContext) {
+//	  ctx.RegisterModuleType(...)
+//	  ...
+//	}
 //
 // Extracting the actual registration into a separate RegisterBuildComponents(ctx) function
 // allows it to be used to initialize test context, e.g.
 //
-//   ctx := android.NewTestContext(config)
-//   RegisterBuildComponents(ctx)
+//	ctx := android.NewTestContext(config)
+//	RegisterBuildComponents(ctx)
 var InitRegistrationContext RegistrationContext = &initRegistrationContext{
 	moduleTypes:       make(map[string]ModuleFactory),
 	singletonTypes:    make(map[string]SingletonFactory),
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 11da36c..155fbdf 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -1031,7 +1031,8 @@
 // be also added to the dependencies returned by RuleBuilder.Tools.
 //
 // It is equivalent to:
-//  cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
+//
+//	cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
 func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
 	if c.rule.ctx.Config().UseHostMusl() {
 		// If the host is using musl, assume that the tool was built against musl libc and include
@@ -1053,7 +1054,8 @@
 // dependencies returned by RuleBuilder.Tools.
 //
 // It is equivalent to:
-//  cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+//
+//	cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
 func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
 	return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
 }
diff --git a/android/sdk.go b/android/sdk.go
index a71f7f2..a477cba 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -430,13 +430,13 @@
 // required for some members but not others. Traits can cause additional information to be output
 // to the sdk snapshot or replace the default information exported for a member with something else.
 // e.g.
-// * By default cc libraries only export the default image variants to the SDK. However, for some
-//   members it may be necessary to export specific image variants, e.g. vendor, or recovery.
-// * By default cc libraries export all the configured architecture variants except for the native
-//   bridge architecture variants. However, for some members it may be necessary to export the
-//   native bridge architecture variants as well.
-// * By default cc libraries export the platform variant (i.e. sdk:). However, for some members it
-//   may be necessary to export the sdk variant (i.e. sdk:sdk).
+//   - By default cc libraries only export the default image variants to the SDK. However, for some
+//     members it may be necessary to export specific image variants, e.g. vendor, or recovery.
+//   - By default cc libraries export all the configured architecture variants except for the native
+//     bridge architecture variants. However, for some members it may be necessary to export the
+//     native bridge architecture variants as well.
+//   - By default cc libraries export the platform variant (i.e. sdk:). However, for some members it
+//     may be necessary to export the sdk variant (i.e. sdk:sdk).
 //
 // A sdk can request a module to provide no traits, one trait or a collection of traits. The exact
 // behavior of a trait is determined by how SdkMemberType implementations handle the traits. A trait
@@ -447,17 +447,17 @@
 // SdkPropertyName(). Each property contains a list of modules that are required to have that trait.
 // e.g. something like this:
 //
-//   sdk {
-//     name: "sdk",
-//     ...
-//     traits: {
-//       recovery_image: ["module1", "module4", "module5"],
-//       native_bridge: ["module1", "module2"],
-//       native_sdk: ["module1", "module3"],
-//       ...
-//     },
-//     ...
-//   }
+//	sdk {
+//	  name: "sdk",
+//	  ...
+//	  traits: {
+//	    recovery_image: ["module1", "module4", "module5"],
+//	    native_bridge: ["module1", "module2"],
+//	    native_sdk: ["module1", "module3"],
+//	    ...
+//	  },
+//	  ...
+//	}
 type SdkMemberTrait interface {
 	// SdkPropertyName returns the name of the traits property on an sdk module.
 	SdkPropertyName() string
@@ -639,20 +639,19 @@
 // The basic implementation should look something like this, where ModuleType is
 // the name of the module type being supported.
 //
-//    type moduleTypeSdkMemberType struct {
-//        android.SdkMemberTypeBase
-//    }
+//	type moduleTypeSdkMemberType struct {
+//	    android.SdkMemberTypeBase
+//	}
 //
-//    func init() {
-//        android.RegisterSdkMemberType(&moduleTypeSdkMemberType{
-//            SdkMemberTypeBase: android.SdkMemberTypeBase{
-//                PropertyName: "module_types",
-//            },
-//        }
-//    }
+//	func init() {
+//	    android.RegisterSdkMemberType(&moduleTypeSdkMemberType{
+//	        SdkMemberTypeBase: android.SdkMemberTypeBase{
+//	            PropertyName: "module_types",
+//	        },
+//	    }
+//	}
 //
-//    ...methods...
-//
+//	...methods...
 type SdkMemberType interface {
 	// SdkPropertyName returns the name of the member type property on an sdk module.
 	SdkPropertyName() string
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index bd73645..cd36ae0 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -190,77 +190,78 @@
 //
 // Each soong_config_variable supports an additional value `conditions_default`. The properties
 // specified in `conditions_default` will only be used under the following conditions:
-//   bool variable: the variable is unspecified or not set to a true value
-//   value variable: the variable is unspecified
-//   string variable: the variable is unspecified or the variable is set to a string unused in the
-//                    given module. For example, string variable `test` takes values: "a" and "b",
-//                    if the module contains a property `a` and `conditions_default`, when test=b,
-//                    the properties under `conditions_default` will be used. To specify that no
-//                    properties should be amended for `b`, you can set `b: {},`.
+//
+//	bool variable: the variable is unspecified or not set to a true value
+//	value variable: the variable is unspecified
+//	string variable: the variable is unspecified or the variable is set to a string unused in the
+//	                 given module. For example, string variable `test` takes values: "a" and "b",
+//	                 if the module contains a property `a` and `conditions_default`, when test=b,
+//	                 the properties under `conditions_default` will be used. To specify that no
+//	                 properties should be amended for `b`, you can set `b: {},`.
 //
 // For example, an Android.bp file could have:
 //
-//     soong_config_module_type {
-//         name: "acme_cc_defaults",
-//         module_type: "cc_defaults",
-//         config_namespace: "acme",
-//         variables: ["board"],
-//         bool_variables: ["feature"],
-//         value_variables: ["width"],
-//         properties: ["cflags", "srcs"],
-//     }
+//	    soong_config_module_type {
+//	        name: "acme_cc_defaults",
+//	        module_type: "cc_defaults",
+//	        config_namespace: "acme",
+//	        variables: ["board"],
+//	        bool_variables: ["feature"],
+//	        value_variables: ["width"],
+//	        properties: ["cflags", "srcs"],
+//	    }
 //
-//     soong_config_string_variable {
-//         name: "board",
-//         values: ["soc_a", "soc_b"],
-//     }
+//	    soong_config_string_variable {
+//	        name: "board",
+//	        values: ["soc_a", "soc_b"],
+//	    }
 //
-//     acme_cc_defaults {
-//         name: "acme_defaults",
-//         cflags: ["-DGENERIC"],
-//         soong_config_variables: {
-//             board: {
-//                 soc_a: {
-//                     cflags: ["-DSOC_A"],
-//                 },
-//                 soc_b: {
-//                     cflags: ["-DSOC_B"],
-//                 },
-//                 conditions_default: {
-//                     cflags: ["-DSOC_DEFAULT"],
-//                 },
-//             },
-//             feature: {
-//                 cflags: ["-DFEATURE"],
-//                 conditions_default: {
-//                     cflags: ["-DFEATURE_DEFAULT"],
-//                 },
-//             },
-//             width: {
-//	               cflags: ["-DWIDTH=%s"],
-//                 conditions_default: {
-//                     cflags: ["-DWIDTH=DEFAULT"],
-//                 },
-//             },
-//         },
-//     }
+//	    acme_cc_defaults {
+//	        name: "acme_defaults",
+//	        cflags: ["-DGENERIC"],
+//	        soong_config_variables: {
+//	            board: {
+//	                soc_a: {
+//	                    cflags: ["-DSOC_A"],
+//	                },
+//	                soc_b: {
+//	                    cflags: ["-DSOC_B"],
+//	                },
+//	                conditions_default: {
+//	                    cflags: ["-DSOC_DEFAULT"],
+//	                },
+//	            },
+//	            feature: {
+//	                cflags: ["-DFEATURE"],
+//	                conditions_default: {
+//	                    cflags: ["-DFEATURE_DEFAULT"],
+//	                },
+//	            },
+//	            width: {
+//		               cflags: ["-DWIDTH=%s"],
+//	                conditions_default: {
+//	                    cflags: ["-DWIDTH=DEFAULT"],
+//	                },
+//	            },
+//	        },
+//	    }
 //
-//     cc_library {
-//         name: "libacme_foo",
-//         defaults: ["acme_defaults"],
-//         srcs: ["*.cpp"],
-//     }
+//	    cc_library {
+//	        name: "libacme_foo",
+//	        defaults: ["acme_defaults"],
+//	        srcs: ["*.cpp"],
+//	    }
 //
 // If an acme BoardConfig.mk file contained:
 //
-//     SOONG_CONFIG_NAMESPACES += acme
-//     SOONG_CONFIG_acme += \
-//         board \
-//         feature \
+//	SOONG_CONFIG_NAMESPACES += acme
+//	SOONG_CONFIG_acme += \
+//	    board \
+//	    feature \
 //
-//     SOONG_CONFIG_acme_board := soc_a
-//     SOONG_CONFIG_acme_feature := true
-//     SOONG_CONFIG_acme_width := 200
+//	SOONG_CONFIG_acme_board := soc_a
+//	SOONG_CONFIG_acme_feature := true
+//	SOONG_CONFIG_acme_width := 200
 //
 // Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
 func SoongConfigModuleTypeFactory() Module {
@@ -381,7 +382,7 @@
 		defer r.Close()
 
 		mtDef, errs := soongconfig.Parse(r, from)
-		if ctx.Config().runningAsBp2Build {
+		if ctx.Config().BuildMode == Bp2build {
 			ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
 		}
 
@@ -397,7 +398,7 @@
 		for name, moduleType := range mtDef.ModuleTypes {
 			factory := globalModuleTypes[moduleType.BaseModuleType]
 			if factory != nil {
-				factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
+				factories[name] = configModuleFactory(factory, moduleType, ctx.Config().BuildMode == Bp2build)
 			} else {
 				reportErrors(ctx, from,
 					fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 212b752..7d21b75 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -343,23 +343,26 @@
 //
 // For example, the acme_cc_defaults example above would
 // produce a reflect.Value whose type is:
-// *struct {
-//     Soong_config_variables struct {
-//         Board struct {
-//             Soc_a interface{}
-//             Soc_b interface{}
-//         }
-//     }
-// }
+//
+//	*struct {
+//	    Soong_config_variables struct {
+//	        Board struct {
+//	            Soc_a interface{}
+//	            Soc_b interface{}
+//	        }
+//	    }
+//	}
+//
 // And whose value is:
-// &{
-//     Soong_config_variables: {
-//         Board: {
-//             Soc_a: (*struct{ Cflags []string })(nil),
-//             Soc_b: (*struct{ Cflags []string })(nil),
-//         },
-//     },
-// }
+//
+//	&{
+//	    Soong_config_variables: {
+//	        Board: {
+//	            Soc_a: (*struct{ Cflags []string })(nil),
+//	            Soc_b: (*struct{ Cflags []string })(nil),
+//	        },
+//	    },
+//	}
 func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
 	var fields []reflect.StructField
 
diff --git a/android/test_config.go b/android/test_config.go
new file mode 100644
index 0000000..0f88d51
--- /dev/null
+++ b/android/test_config.go
@@ -0,0 +1,150 @@
+// 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 android
+
+import (
+	"encoding/json"
+	"os"
+	"path/filepath"
+	"runtime"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// TestConfig returns a Config object for testing.
+func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	envCopy := make(map[string]string)
+	for k, v := range env {
+		envCopy[k] = v
+	}
+
+	// Copy the real PATH value to the test environment, it's needed by
+	// NonHermeticHostSystemTool() used in x86_darwin_host.go
+	envCopy["PATH"] = os.Getenv("PATH")
+
+	config := &config{
+		productVariables: productVariables{
+			DeviceName:                          stringPtr("test_device"),
+			DeviceProduct:                       stringPtr("test_product"),
+			Platform_sdk_version:                intPtr(30),
+			Platform_sdk_codename:               stringPtr("S"),
+			Platform_base_sdk_extension_version: intPtr(1),
+			Platform_version_active_codenames:   []string{"S", "Tiramisu"},
+			DeviceSystemSdkVersions:             []string{"14", "15"},
+			Platform_systemsdk_versions:         []string{"29", "30"},
+			AAPTConfig:                          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+			AAPTPreferredConfig:                 stringPtr("xhdpi"),
+			AAPTCharacteristics:                 stringPtr("nosdcard"),
+			AAPTPrebuiltDPI:                     []string{"xhdpi", "xxhdpi"},
+			UncompressPrivAppDex:                boolPtr(true),
+			ShippingApiLevel:                    stringPtr("30"),
+		},
+
+		outDir:       buildDir,
+		soongOutDir:  filepath.Join(buildDir, "soong"),
+		captureBuild: true,
+		env:          envCopy,
+
+		// Set testAllowNonExistentPaths so that test contexts don't need to specify every path
+		// passed to PathForSource or PathForModuleSrc.
+		TestAllowNonExistentPaths: true,
+
+		BazelContext:              noopBazelContext{},
+		mixedBuildDisabledModules: make(map[string]struct{}),
+		mixedBuildEnabledModules:  make(map[string]struct{}),
+	}
+	config.deviceConfig = &deviceConfig{
+		config: config,
+	}
+	config.TestProductVariables = &config.productVariables
+
+	config.mockFileSystem(bp, fs)
+
+	determineBuildOS(config)
+
+	return Config{config}
+}
+
+func modifyTestConfigToSupportArchMutator(testConfig Config) {
+	config := testConfig.config
+
+	config.Targets = map[OsType][]Target{
+		Android: []Target{
+			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
+		},
+		config.BuildOS: []Target{
+			{config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+			{config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
+		},
+	}
+
+	if runtime.GOOS == "darwin" {
+		config.Targets[config.BuildOS] = config.Targets[config.BuildOS][:1]
+	}
+
+	config.BuildOSTarget = config.Targets[config.BuildOS][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+	config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
+	config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
+	config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
+	config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
+	config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
+	config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
+}
+
+func modifyTestConfigForMusl(config Config) {
+	delete(config.Targets, config.BuildOS)
+	config.productVariables.HostMusl = boolPtr(true)
+	determineBuildOS(config.config)
+	config.Targets[config.BuildOS] = []Target{
+		{config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+		{config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
+	}
+
+	config.BuildOSTarget = config.Targets[config.BuildOS][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+}
+
+func modifyTestConfigForMuslArm64HostCross(config Config) {
+	config.Targets[LinuxMusl] = append(config.Targets[LinuxMusl],
+		Target{config.BuildOS, Arch{ArchType: Arm64}, NativeBridgeDisabled, "", "", true})
+}
+
+// TestArchConfig returns a Config object suitable for using for tests that
+// need to run the arch mutator.
+func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	testConfig := TestConfig(buildDir, env, bp, fs)
+	modifyTestConfigToSupportArchMutator(testConfig)
+	return testConfig
+}
+
+// CreateTestConfiguredJarList is a function to create ConfiguredJarList for tests.
+func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
+	// Create the ConfiguredJarList in as similar way as it is created at runtime by marshalling to
+	// a json list of strings and then unmarshalling into a ConfiguredJarList instance.
+	b, err := json.Marshal(list)
+	if err != nil {
+		panic(err)
+	}
+
+	var jarList ConfiguredJarList
+	err = json.Unmarshal(b, &jarList)
+	if err != nil {
+		panic(err)
+	}
+
+	return jarList
+}
diff --git a/android/testing.go b/android/testing.go
index 85bdca4..7b74c89 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -213,8 +213,8 @@
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
 
-func (ctx *TestContext) RegisterBp2BuildConfig(config bp2BuildConversionAllowlist) {
-	ctx.config.bp2buildPackageConfig = config
+func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConversionAllowlist) {
+	ctx.config.Bp2buildPackageConfig = config
 }
 
 // PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
@@ -457,7 +457,7 @@
 
 // RegisterForBazelConversion prepares a test context for bp2build conversion.
 func (ctx *TestContext) RegisterForBazelConversion() {
-	ctx.SetRunningAsBp2build()
+	ctx.config.BuildMode = Bp2build
 	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
 }
 
@@ -688,17 +688,17 @@
 //
 // The parts of this structure which are changed are:
 // * BuildParams
-//   * Args
-//   * All Path, Paths, WritablePath and WritablePaths fields.
+//   - Args
+//   - All Path, Paths, WritablePath and WritablePaths fields.
 //
 // * RuleParams
-//   * Command
-//   * Depfile
-//   * Rspfile
-//   * RspfileContent
-//   * SymlinkOutputs
-//   * CommandDeps
-//   * CommandOrderOnly
+//   - Command
+//   - Depfile
+//   - Rspfile
+//   - RspfileContent
+//   - SymlinkOutputs
+//   - CommandDeps
+//   - CommandOrderOnly
 //
 // See PathRelativeToTop for more details.
 //
@@ -1037,7 +1037,7 @@
 		}
 	}
 	if !found {
-		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
+		t.Errorf("could not match the expected error regex %q (checked %d error(s))", pattern, len(errs))
 		for i, err := range errs {
 			t.Errorf("errs[%d] = %q", i, err)
 		}
diff --git a/android/variable.go b/android/variable.go
index 874b69d..b156051 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -249,7 +249,8 @@
 	AAPTPreferredConfig *string  `json:",omitempty"`
 	AAPTPrebuiltDPI     []string `json:",omitempty"`
 
-	DefaultAppCertificate *string `json:",omitempty"`
+	DefaultAppCertificate           *string `json:",omitempty"`
+	MainlineSepolicyDevCertificates *string `json:",omitempty"`
 
 	AppsDefaultVersionName *string `json:",omitempty"`
 
@@ -429,6 +430,7 @@
 
 	ShippingApiLevel *string `json:",omitempty"`
 
+	BuildBrokenClangProperty           bool     `json:",omitempty"`
 	BuildBrokenDepfile                 *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
@@ -590,6 +592,9 @@
 	// "acme__board__soc_a", "acme__board__soc_b", and
 	// "acme__board__conditions_default"
 	FullConfig string
+
+	// keeps track of whether this product variable is nested under an arch variant
+	OuterAxis bazel.ConfigurationAxis
 }
 
 func (p *ProductConfigProperty) AlwaysEmit() bool {
@@ -598,11 +603,11 @@
 
 func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
 	if p.Namespace == "" {
-		return bazel.ProductVariableConfigurationAxis(p.FullConfig)
+		return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis)
 	} else {
 		// Soong config variables can be uniquely identified by the namespace
 		// (e.g. acme, android) and the product variable name (e.g. board, size)
-		return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.Name)
+		return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis)
 	}
 }
 
@@ -661,9 +666,11 @@
 			moduleBase.variableProperties,
 			"",
 			"",
-			&productConfigProperties)
+			&productConfigProperties,
+			bazel.ConfigurationAxis{},
+		)
 
-		for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
 			for config, props := range configToProps {
 				// GetArchVariantProperties is creating an instance of the requested type
 				// and productVariablesValues expects an interface, so no need to cast
@@ -672,7 +679,8 @@
 					props,
 					"",
 					config,
-					&productConfigProperties)
+					&productConfigProperties,
+					axis)
 			}
 		}
 	}
@@ -685,7 +693,8 @@
 					namespacedVariableProp,
 					namespace,
 					"",
-					&productConfigProperties)
+					&productConfigProperties,
+					bazel.NoConfigAxis)
 			}
 		}
 	}
@@ -701,20 +710,20 @@
 //
 // If the ProductConfigProperties map contains these items, as parsed from the .bp file:
 //
-// library_linking_strategy: {
-//     prefer_static: {
-//         static_libs: [
-//             "lib_a",
-//             "lib_b",
-//         ],
-//     },
-//     conditions_default: {
-//         shared_libs: [
-//             "lib_a",
-//             "lib_b",
-//         ],
-//     },
-// },
+//	library_linking_strategy: {
+//	    prefer_static: {
+//	        static_libs: [
+//	            "lib_a",
+//	            "lib_b",
+//	        ],
+//	    },
+//	    conditions_default: {
+//	        shared_libs: [
+//	            "lib_a",
+//	            "lib_b",
+//	        ],
+//	    },
+//	},
 //
 // Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
 // Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
@@ -727,13 +736,13 @@
 // instead of putting lib_a and lib_b directly into dynamic_deps without a
 // select:
 //
-// dynamic_deps = select({
-//     "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
-//     "//conditions:default": [
-//         "//foo/bar:lib_a",
-//         "//foo/bar:lib_b",
-//     ],
-// }),
+//	dynamic_deps = select({
+//	    "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+//	    "//conditions:default": [
+//	        "//foo/bar:lib_a",
+//	        "//foo/bar:lib_b",
+//	    ],
+//	}),
 func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
 	// A map of product config properties to the zero values of their respective
 	// property value.
@@ -801,6 +810,7 @@
 					p.Name,
 					p.FullConfig,
 					zeroValue,
+					bazel.NoConfigAxis,
 				)
 			}
 		}
@@ -808,7 +818,7 @@
 }
 
 func (p *ProductConfigProperties) AddProductConfigProperty(
-	propertyName, namespace, productVariableName, config string, property interface{}) {
+	propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
 	if (*p)[propertyName] == nil {
 		(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
 	}
@@ -817,6 +827,7 @@
 		Namespace:  namespace,           // e.g. acme, android
 		Name:       productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
 		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
+		OuterAxis:  outerAxis,
 	}
 
 	if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
@@ -867,7 +878,7 @@
 	return v, true
 }
 
-func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) {
 	// variableValues can either be a product_variables or
 	// soong_config_variables struct.
 	//
@@ -972,7 +983,8 @@
 						namespace,           // e.g. acme, android
 						productVariableName, // e.g. size, feature1, FEATURE2, board
 						config,
-						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
+						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"],
+						outerAxis,
 					)
 				}
 			} else if property.Kind() != reflect.Interface {
@@ -986,6 +998,7 @@
 					productVariableName,
 					config,
 					property.Interface(),
+					outerAxis,
 				)
 			}
 		}
@@ -996,14 +1009,14 @@
 // product_variables and soong_config_variables to structs that can be generated
 // as select statements.
 func productVariableValues(
-	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
+	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) {
 	if suffix != "" {
 		suffix = "-" + suffix
 	}
 
 	// variableValues represent the product_variables or soong_config_variables struct.
 	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
-	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues)
+	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis)
 }
 
 func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 280dae8..f646742 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -166,9 +166,10 @@
 		}
 	} else {
 		llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
-		llvmLib := config.ClangPath(ctx, "lib64/libc++.so.1")
+		llvmLib64 := config.ClangPath(ctx, "lib64/libc++.so.1")
+		llvmLib := config.ClangPath(ctx, "lib/libc++.so.1")
 		for _, strip := range s.properties.Strip_files {
-			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
+			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib64).ImplicitTool(llvmLib)
 			if !ctx.Windows() {
 				cmd.Flag("-x")
 			}
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index aaafdc7..2e8810f 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -421,9 +421,9 @@
 // For example, if prefix is "foo" and name is "bar" with a value of "baz", then
 // the following variable will be generated:
 //
-// foo {
-//   bar: "baz"
-// }
+//	foo {
+//	  bar: "baz"
+//	}
 //
 // If prefix is the empty string and name is "foo" with a value of "bar", the
 // following variable will be generated (if it is a property):
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index 8afbe7e..be36859 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -38,10 +38,10 @@
 // For example, "$(FOO)/bar/baz" will be represented as the
 // following lists:
 //
-// {
-//   Strings: ["", "/bar/baz"],
-//   Variables: ["FOO"]
-// }
+//	{
+//	  Strings: ["", "/bar/baz"],
+//	  Variables: ["FOO"]
+//	}
 type MakeString struct {
 	StringPos Pos
 	Strings   []string
diff --git a/apex/apex.go b/apex/apex.go
index e9b0815..949809a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -192,6 +192,10 @@
 	// with the tool to sign payload contents.
 	Custom_sign_tool *string
 
+	// Whether this is a dynamic common lib apex, if so the native shared libs will be placed
+	// in a special way that include the digest of the lib file under /lib(64)?
+	Dynamic_common_lib_apex *bool
+
 	// Canonical name of this APEX bundle. Used to determine the path to the
 	// activated APEX on device (i.e. /apex/<apexVariationName>), and used for the
 	// apex mutator variations. For override_apex modules, this is the name of the
@@ -1472,6 +1476,11 @@
 	return proptools.Bool(a.properties.Test_only_force_compression)
 }
 
+// See the dynamic_common_lib_apex property
+func (a *apexBundle) dynamic_common_lib_apex() bool {
+	return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
+}
+
 // These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
 // members) can be sanitized, either forcibly, or by the global configuration. For some of the
 // sanitizers, extra dependencies can be forcibly added as well.
@@ -3015,34 +3024,9 @@
 	//
 	// Module separator
 	//
-	m["com.android.bluetooth"] = []string{
-		"android.hardware.audio.common@5.0",
-		"android.hardware.bluetooth.a2dp@1.0",
-		"android.hardware.bluetooth.audio@2.0",
-		"android.hardware.bluetooth@1.0",
-		"android.hardware.bluetooth@1.1",
-		"android.hardware.graphics.bufferqueue@1.0",
-		"android.hardware.graphics.bufferqueue@2.0",
-		"android.hardware.graphics.common@1.0",
-		"android.hardware.graphics.common@1.1",
-		"android.hardware.graphics.common@1.2",
-		"android.hardware.media@1.0",
-		"android.hidl.safe_union@1.0",
-		"android.hidl.token@1.0",
-		"android.hidl.token@1.0-utils",
-		"avrcp-target-service",
-		"avrcp_headers",
+	m["com.android.btservices"] = []string{
 		"bluetooth-protos-lite",
-		"bluetooth.mapsapi",
-		"com.android.vcard",
-		"dnsresolver_aidl_interface-V2-java",
-		"ipmemorystore-aidl-interfaces-V5-java",
-		"ipmemorystore-aidl-interfaces-java",
 		"internal_include_headers",
-		"lib-bt-packets",
-		"lib-bt-packets-avrcp",
-		"lib-bt-packets-base",
-		"libFraunhoferAAC",
 		"libaudio-a2dp-hw-utils",
 		"libaudio-hearing-aid-hw-utils",
 		"libbluetooth",
@@ -3066,28 +3050,36 @@
 		"libbte",
 		"libbtif",
 		"libchrome",
-		"libevent",
-		"libfmq",
-		"libg722codec",
-		"libgui_headers",
-		"libmedia_headers",
-		"libmodpb64",
-		"libosi",
-		"libstagefright_foundation_headers",
-		"libstagefright_headers",
-		"libstatslog",
-		"libstatssocket",
-		"libtinyxml2",
-		"libudrv-uipc",
-		"libz",
-		"media_plugin_headers",
-		"net-utils-services-common",
-		"netd_aidl_interface-unstable-java",
-		"netd_event_listener_interface-java",
-		"netlink-client",
-		"networkstack-client",
-		"sap-api-java-static",
-		"services.net",
+	}
+	//
+	// Module separator
+	//
+	m["com.android.bluetooth"] = []string{
+		"bluetooth-protos-lite",
+		"internal_include_headers",
+		"libaudio-a2dp-hw-utils",
+		"libaudio-hearing-aid-hw-utils",
+		"libbluetooth",
+		"libbluetooth-types",
+		"libbluetooth-types-header",
+		"libbluetooth_gd",
+		"libbluetooth_headers",
+		"libbluetooth_jni",
+		"libbt-audio-hal-interface",
+		"libbt-bta",
+		"libbt-common",
+		"libbt-hci",
+		"libbt-platform-protos-lite",
+		"libbt-protos-lite",
+		"libbt-sbc-decoder",
+		"libbt-sbc-encoder",
+		"libbt-stack",
+		"libbt-utils",
+		"libbtcore",
+		"libbtdevice",
+		"libbte",
+		"libbtif",
+		"libchrome",
 	}
 	//
 	// Module separator
@@ -3127,258 +3119,13 @@
 	// Module separator
 	//
 	m["com.android.media"] = []string{
-		"android.frameworks.bufferhub@1.0",
-		"android.hardware.cas.native@1.0",
-		"android.hardware.cas@1.0",
-		"android.hardware.configstore-utils",
-		"android.hardware.configstore@1.0",
-		"android.hardware.configstore@1.1",
-		"android.hardware.graphics.allocator@2.0",
-		"android.hardware.graphics.allocator@3.0",
-		"android.hardware.graphics.bufferqueue@1.0",
-		"android.hardware.graphics.bufferqueue@2.0",
-		"android.hardware.graphics.common@1.0",
-		"android.hardware.graphics.common@1.1",
-		"android.hardware.graphics.common@1.2",
-		"android.hardware.graphics.mapper@2.0",
-		"android.hardware.graphics.mapper@2.1",
-		"android.hardware.graphics.mapper@3.0",
-		"android.hardware.media.omx@1.0",
-		"android.hardware.media@1.0",
-		"android.hidl.allocator@1.0",
-		"android.hidl.memory.token@1.0",
-		"android.hidl.memory@1.0",
-		"android.hidl.token@1.0",
-		"android.hidl.token@1.0-utils",
-		"bionic_libc_platform_headers",
-		"exoplayer2-extractor",
-		"exoplayer2-extractor-annotation-stubs",
-		"gl_headers",
-		"jsr305",
-		"libEGL",
-		"libEGL_blobCache",
-		"libEGL_getProcAddress",
-		"libFLAC",
-		"libFLAC-config",
-		"libFLAC-headers",
-		"libGLESv2",
-		"libaacextractor",
-		"libamrextractor",
-		"libarect",
-		"libaudio_system_headers",
-		"libaudioclient",
-		"libaudioclient_headers",
-		"libaudiofoundation",
-		"libaudiofoundation_headers",
-		"libaudiomanager",
-		"libaudiopolicy",
-		"libaudioutils",
-		"libaudioutils_fixedfft",
-		"libbluetooth-types-header",
-		"libbufferhub",
-		"libbufferhub_headers",
-		"libbufferhubqueue",
-		"libc_malloc_debug_backtrace",
-		"libcamera_client",
-		"libcamera_metadata",
-		"libdvr_headers",
-		"libexpat",
-		"libfifo",
-		"libflacextractor",
-		"libgrallocusage",
-		"libgraphicsenv",
-		"libgui",
-		"libgui_headers",
-		"libhardware_headers",
-		"libinput",
-		"liblzma",
-		"libmath",
-		"libmedia",
-		"libmedia_codeclist",
-		"libmedia_headers",
-		"libmedia_helper",
-		"libmedia_helper_headers",
-		"libmedia_midiiowrapper",
-		"libmedia_omx",
-		"libmediautils",
-		"libmidiextractor",
-		"libmkvextractor",
-		"libmp3extractor",
-		"libmp4extractor",
-		"libmpeg2extractor",
-		"libnativebase_headers",
-		"libnativewindow_headers",
-		"libnblog",
-		"liboggextractor",
-		"libpackagelistparser",
-		"libpdx",
-		"libpdx_default_transport",
-		"libpdx_headers",
-		"libpdx_uds",
-		"libprocinfo",
-		"libspeexresampler",
-		"libspeexresampler",
-		"libstagefright_esds",
-		"libstagefright_flacdec",
-		"libstagefright_flacdec",
-		"libstagefright_foundation",
-		"libstagefright_foundation_headers",
-		"libstagefright_foundation_without_imemory",
-		"libstagefright_headers",
-		"libstagefright_id3",
-		"libstagefright_metadatautils",
-		"libstagefright_mpeg2extractor",
-		"libstagefright_mpeg2support",
-		"libui",
-		"libui_headers",
-		"libunwindstack",
-		"libvibrator",
-		"libvorbisidec",
-		"libwavextractor",
-		"libwebm",
-		"media_ndk_headers",
-		"media_plugin_headers",
-		"updatable-media",
+		// empty
 	}
 	//
 	// Module separator
 	//
 	m["com.android.media.swcodec"] = []string{
-		"android.frameworks.bufferhub@1.0",
-		"android.hardware.common-ndk_platform",
-		"android.hardware.configstore-utils",
-		"android.hardware.configstore@1.0",
-		"android.hardware.configstore@1.1",
-		"android.hardware.graphics.allocator@2.0",
-		"android.hardware.graphics.allocator@3.0",
-		"android.hardware.graphics.allocator@4.0",
-		"android.hardware.graphics.bufferqueue@1.0",
-		"android.hardware.graphics.bufferqueue@2.0",
-		"android.hardware.graphics.common-ndk_platform",
-		"android.hardware.graphics.common@1.0",
-		"android.hardware.graphics.common@1.1",
-		"android.hardware.graphics.common@1.2",
-		"android.hardware.graphics.mapper@2.0",
-		"android.hardware.graphics.mapper@2.1",
-		"android.hardware.graphics.mapper@3.0",
-		"android.hardware.graphics.mapper@4.0",
-		"android.hardware.media.bufferpool@2.0",
-		"android.hardware.media.c2@1.0",
-		"android.hardware.media.c2@1.1",
-		"android.hardware.media.omx@1.0",
-		"android.hardware.media@1.0",
-		"android.hardware.media@1.0",
-		"android.hidl.memory.token@1.0",
-		"android.hidl.memory@1.0",
-		"android.hidl.safe_union@1.0",
-		"android.hidl.token@1.0",
-		"android.hidl.token@1.0-utils",
-		"libEGL",
-		"libFLAC",
-		"libFLAC-config",
-		"libFLAC-headers",
-		"libFraunhoferAAC",
-		"libLibGuiProperties",
-		"libarect",
-		"libaudio_system_headers",
-		"libaudioutils",
-		"libaudioutils",
-		"libaudioutils_fixedfft",
-		"libavcdec",
-		"libavcenc",
-		"libavservices_minijail",
-		"libavservices_minijail",
-		"libbinderthreadstateutils",
-		"libbluetooth-types-header",
-		"libbufferhub_headers",
-		"libcodec2",
-		"libcodec2_headers",
-		"libcodec2_hidl@1.0",
-		"libcodec2_hidl@1.1",
-		"libcodec2_internal",
-		"libcodec2_soft_aacdec",
-		"libcodec2_soft_aacenc",
-		"libcodec2_soft_amrnbdec",
-		"libcodec2_soft_amrnbenc",
-		"libcodec2_soft_amrwbdec",
-		"libcodec2_soft_amrwbenc",
-		"libcodec2_soft_av1dec_gav1",
-		"libcodec2_soft_avcdec",
-		"libcodec2_soft_avcenc",
-		"libcodec2_soft_common",
-		"libcodec2_soft_flacdec",
-		"libcodec2_soft_flacenc",
-		"libcodec2_soft_g711alawdec",
-		"libcodec2_soft_g711mlawdec",
-		"libcodec2_soft_gsmdec",
-		"libcodec2_soft_h263dec",
-		"libcodec2_soft_h263enc",
-		"libcodec2_soft_hevcdec",
-		"libcodec2_soft_hevcenc",
-		"libcodec2_soft_mp3dec",
-		"libcodec2_soft_mpeg2dec",
-		"libcodec2_soft_mpeg4dec",
-		"libcodec2_soft_mpeg4enc",
-		"libcodec2_soft_opusdec",
-		"libcodec2_soft_opusenc",
-		"libcodec2_soft_rawdec",
-		"libcodec2_soft_vorbisdec",
-		"libcodec2_soft_vp8dec",
-		"libcodec2_soft_vp8enc",
-		"libcodec2_soft_vp9dec",
-		"libcodec2_soft_vp9enc",
-		"libcodec2_vndk",
-		"libdvr_headers",
-		"libfmq",
-		"libfmq",
-		"libgav1",
-		"libgralloctypes",
-		"libgrallocusage",
-		"libgraphicsenv",
-		"libgsm",
-		"libgui_bufferqueue_static",
-		"libgui_headers",
-		"libhardware",
-		"libhardware_headers",
-		"libhevcdec",
-		"libhevcenc",
-		"libion",
-		"libjpeg",
-		"liblzma",
-		"libmath",
-		"libmedia_codecserviceregistrant",
-		"libmedia_headers",
-		"libmpeg2dec",
-		"libnativebase_headers",
-		"libnativewindow_headers",
-		"libpdx_headers",
-		"libscudo_wrapper",
-		"libsfplugin_ccodec_utils",
-		"libspeexresampler",
-		"libstagefright_amrnb_common",
-		"libstagefright_amrnbdec",
-		"libstagefright_amrnbenc",
-		"libstagefright_amrwbdec",
-		"libstagefright_amrwbenc",
-		"libstagefright_bufferpool@2.0.1",
-		"libstagefright_enc_common",
-		"libstagefright_flacdec",
-		"libstagefright_foundation",
-		"libstagefright_foundation_headers",
-		"libstagefright_headers",
-		"libstagefright_m4vh263dec",
-		"libstagefright_m4vh263enc",
-		"libstagefright_mp3dec",
-		"libui",
-		"libui_headers",
-		"libunwindstack",
-		"libvorbisidec",
-		"libvpx",
-		"libyuv",
-		"libyuv_static",
-		"media_ndk_headers",
-		"media_plugin_headers",
-		"mediaswcodec",
+		// empty
 	}
 	//
 	// Module separator
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 49a5d2a..1805291 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -8843,9 +8843,7 @@
 		android.FixtureWithRootAndroidBp(bp),
 		dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
-		android.FixtureMergeEnv(map[string]string{
-			"EMMA_INSTRUMENT": "true",
-		}),
+		java.PrepareForTestWithJacocoInstrumentation,
 	).RunTest(t)
 
 	// Make sure jacoco ran on both mylib and mybootclasspathlib
diff --git a/apex/bp2build.go b/apex/bp2build.go
index 221ab13..d28f512 100644
--- a/apex/bp2build.go
+++ b/apex/bp2build.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/apex/builder.go b/apex/builder.go
index f1b1448..b95b3bd 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -39,6 +39,7 @@
 	pctx.Import("android/soong/cc/config")
 	pctx.Import("android/soong/java")
 	pctx.HostBinToolVariable("apexer", "apexer")
+	pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
 	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
 	// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
 	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
@@ -115,7 +116,35 @@
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "payload_fs_type")
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+		"opt_flags", "manifest")
+
+	DCLAApexRule = pctx.StaticRule("DCLAApexRule", blueprint.RuleParams{
+		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+			`(. ${out}.copy_commands) && ` +
+			`APEXER_TOOL_PATH=${tool_path} ` +
+			`${apexer_with_DCLA_preprocessing} ` +
+			`--apexer ${apexer} ` +
+			`--canned_fs_config ${canned_fs_config} ` +
+			`${image_dir} ` +
+			`${out} ` +
+			`-- ` +
+			`--include_build_info ` +
+			`--force ` +
+			`--payload_type image ` +
+			`--key ${key} ` +
+			`--file_contexts ${file_contexts} ` +
+			`--manifest ${manifest} ` +
+			`${opt_flags} `,
+		CommandDeps: []string{"${apexer_with_DCLA_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
+			"${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
+			"${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
+			"prebuilts/sdk/current/public/android.jar"},
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "APEX ${image_dir} => ${out}",
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+		"opt_flags", "manifest", "is_DCLA")
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
@@ -662,22 +691,41 @@
 
 		optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())
 
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        apexRule,
-			Implicits:   implicitInputs,
-			Output:      unsignedOutputFile,
-			Description: "apex (" + apexType.name() + ")",
-			Args: map[string]string{
-				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":        imageDir.String(),
-				"copy_commands":    strings.Join(copyCommands, " && "),
-				"manifest":         a.manifestPbOut.String(),
-				"file_contexts":    fileContexts.String(),
-				"canned_fs_config": cannedFsConfig.String(),
-				"key":              a.privateKeyFile.String(),
-				"opt_flags":        strings.Join(optFlags, " "),
-			},
-		})
+		if a.dynamic_common_lib_apex() {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        DCLAApexRule,
+				Implicits:   implicitInputs,
+				Output:      unsignedOutputFile,
+				Description: "apex (" + apexType.name() + ")",
+				Args: map[string]string{
+					"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+					"image_dir":        imageDir.String(),
+					"copy_commands":    strings.Join(copyCommands, " && "),
+					"manifest":         a.manifestPbOut.String(),
+					"file_contexts":    fileContexts.String(),
+					"canned_fs_config": cannedFsConfig.String(),
+					"key":              a.privateKeyFile.String(),
+					"opt_flags":        strings.Join(optFlags, " "),
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        apexRule,
+				Implicits:   implicitInputs,
+				Output:      unsignedOutputFile,
+				Description: "apex (" + apexType.name() + ")",
+				Args: map[string]string{
+					"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+					"image_dir":        imageDir.String(),
+					"copy_commands":    strings.Join(copyCommands, " && "),
+					"manifest":         a.manifestPbOut.String(),
+					"file_contexts":    fileContexts.String(),
+					"canned_fs_config": cannedFsConfig.String(),
+					"key":              a.privateKeyFile.String(),
+					"opt_flags":        strings.Join(optFlags, " "),
+				},
+			})
+		}
 
 		// TODO(jiyong): make the two rules below as separate functions
 		apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix)
diff --git a/apex/key.go b/apex/key.go
index 9c5bb05..d449589 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -105,7 +105,7 @@
 	m.keyName = pubKeyName
 }
 
-////////////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////////
 // apex_keys_text
 type apexKeysText struct {
 	output android.OutputPath
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 187e0df..172a201 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -364,16 +364,16 @@
 // While it may be possible to provide sufficient information to determine whether two prebuilt_apex
 // modules were compatible it would be a lot of work and would not provide much benefit for a couple
 // of reasons:
-// * The number of prebuilt_apex modules that will be exporting files for the same module will be
-//   low as the prebuilt_apex only exports files for the direct dependencies that require it and
-//   very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
-//   few com.android.art* apex files that contain the same contents and could export files for the
-//   same modules but only one of them needs to do so. Contrast that with source apex modules which
-//   need apex specific variants for every module that contributes code to the apex, whether direct
-//   or indirect.
-// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
-//   extra copying of files. Contrast that with source apex modules that has to build each variant
-//   from source.
+//   - The number of prebuilt_apex modules that will be exporting files for the same module will be
+//     low as the prebuilt_apex only exports files for the direct dependencies that require it and
+//     very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+//     few com.android.art* apex files that contain the same contents and could export files for the
+//     same modules but only one of them needs to do so. Contrast that with source apex modules which
+//     need apex specific variants for every module that contributes code to the apex, whether direct
+//     or indirect.
+//   - The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+//     extra copying of files. Contrast that with source apex modules that has to build each variant
+//     from source.
 func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
 
 	// Collect direct dependencies into contents.
@@ -703,28 +703,29 @@
 // e.g. make dex implementation jars available for java_import modules listed in exported_java_libs,
 // it does so as follows:
 //
-// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
-//    makes them available for use by other modules, at both Soong and ninja levels.
+//  1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
+//     makes them available for use by other modules, at both Soong and ninja levels.
 //
-// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
-//    an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
-//    dexpreopt, will work the same way from source and prebuilt.
+//  2. It adds a dependency onto those modules and creates an apex specific variant similar to what
+//     an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
+//     dexpreopt, will work the same way from source and prebuilt.
 //
-// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
-//    itself so that they can retrieve the file paths to those files.
+//  3. The `deapexer` module adds a dependency from the modules that require the exported files onto
+//     itself so that they can retrieve the file paths to those files.
 //
 // It also creates a child module `selector` that is responsible for selecting the appropriate
 // input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons:
-// 1. To dedup the selection logic so it only runs in one module.
-// 2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
-//    `apex_set`.
 //
-//                     prebuilt_apex
-//                    /      |      \
-//                 /         |         \
-//              V            V            V
-//       selector  <---  deapexer  <---  exported java lib
+//  1. To dedup the selection logic so it only runs in one module.
 //
+//  2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
+//     `apex_set`.
+//
+//     prebuilt_apex
+//     /      |      \
+//     /         |         \
+//     V            V            V
+//     selector  <---  deapexer  <---  exported java lib
 func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
 	baseModuleName := p.BaseModuleName()
 
diff --git a/apex/testing.go b/apex/testing.go
index 337c862..69bd73e 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -24,7 +24,6 @@
 	android.MockFS{
 		// Needed by apex.
 		"system/core/rootdir/etc/public.libraries.android.txt": nil,
-		"build/soong/scripts/gen_java_usedby_apex.sh":          nil,
 		"build/soong/scripts/gen_ndk_backedby_apex.sh":         nil,
 		// Needed by prebuilt_apex.
 		"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 418b143..05f6ed4 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -52,8 +52,9 @@
 
 // AqueryDepset is a depset definition from Bazel's aquery response. This is
 // akin to the `depSetOfFiles` in the response proto, except:
-//   * direct artifacts are enumerated by full path instead of by ID
-//   * it has a hash of the depset contents, instead of an int ID (for determinism)
+//   - direct artifacts are enumerated by full path instead of by ID
+//   - it has a hash of the depset contents, instead of an int ID (for determinism)
+//
 // A depset is a data structure for efficient transitive handling of artifact
 // paths. A single depset consists of one or more artifact paths and one or
 // more "child" depsets.
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 0ab49eb..7ff202b 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"math"
+	"sort"
 	"strings"
 )
 
@@ -27,7 +29,7 @@
 	archX86_64 = "x86_64"
 
 	// OsType names in arch.go
-	osAndroid     = "android"
+	OsAndroid     = "android"
 	osDarwin      = "darwin"
 	osLinux       = "linux_glibc"
 	osLinuxMusl   = "linux_musl"
@@ -64,8 +66,76 @@
 	ConditionsDefaultSelectKey = "//conditions:default"
 
 	productVariableBazelPackage = "//build/bazel/product_variables"
+
+	AndroidAndInApex  = "android-in_apex"
+	AndroidAndNonApex = "android-non_apex"
 )
 
+func PowerSetWithoutEmptySet[T any](items []T) [][]T {
+	resultSize := int(math.Pow(2, float64(len(items))))
+	powerSet := make([][]T, 0, resultSize-1)
+	for i := 1; i < resultSize; i++ {
+		combination := make([]T, 0)
+		for j := 0; j < len(items); j++ {
+			if (i>>j)%2 == 1 {
+				combination = append(combination, items[j])
+			}
+		}
+		powerSet = append(powerSet, combination)
+	}
+	return powerSet
+}
+
+func createPlatformArchMap() map[string]string {
+	// Copy of archFeatures from android/arch_list.go because the bazel
+	// package can't access the android package
+	archFeatures := map[string][]string{
+		"arm": {
+			"neon",
+		},
+		"arm64": {
+			"dotprod",
+		},
+		"x86": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"aes_ni",
+			"avx",
+			"avx2",
+			"avx512",
+			"popcnt",
+			"movbe",
+		},
+		"x86_64": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"aes_ni",
+			"avx",
+			"avx2",
+			"avx512",
+			"popcnt",
+		},
+	}
+	result := make(map[string]string)
+	for arch, allFeatures := range archFeatures {
+		result[arch] = "//build/bazel/platforms/arch:" + arch
+		// Sometimes we want to select on multiple features being active, so
+		// add the power set of all possible features to the map. More details
+		// in android.ModuleBase.GetArchVariantProperties
+		for _, features := range PowerSetWithoutEmptySet(allFeatures) {
+			sort.Strings(features)
+			archFeaturesName := arch + "-" + strings.Join(features, "-")
+			result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
+		}
+	}
+	result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
+	return result
+}
+
 var (
 	// These are the list of OSes and architectures with a Bazel config_setting
 	// and constraint value equivalent. These exist in arch.go, but the android
@@ -74,18 +144,12 @@
 
 	// A map of architectures to the Bazel label of the constraint_value
 	// for the @platforms//cpu:cpu constraint_setting
-	platformArchMap = map[string]string{
-		archArm:                    "//build/bazel/platforms/arch:arm",
-		archArm64:                  "//build/bazel/platforms/arch:arm64",
-		archX86:                    "//build/bazel/platforms/arch:x86",
-		archX86_64:                 "//build/bazel/platforms/arch:x86_64",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of as arch select map.
-	}
+	platformArchMap = createPlatformArchMap()
 
 	// A map of target operating systems to the Bazel label of the
 	// constraint_value for the @platforms//os:os constraint_setting
 	platformOsMap = map[string]string{
-		osAndroid:                  "//build/bazel/platforms/os:android",
+		OsAndroid:                  "//build/bazel/platforms/os:android",
 		osDarwin:                   "//build/bazel/platforms/os:darwin",
 		osLinux:                    "//build/bazel/platforms/os:linux",
 		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
@@ -120,7 +184,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, archX86, archX86_64},
 		osLinux:       {archX86, archX86_64},
 		osLinuxMusl:   {archX86, archX86_64},
 		osDarwin:      {archArm64, archX86_64},
@@ -128,6 +192,12 @@
 		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
 		osWindows: {archX86, archX86_64},
 	}
+
+	osAndInApexMap = map[string]string{
+		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
+		AndroidAndNonApex:          "//build/bazel/rules/apex:android-non_apex",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
+	}
 )
 
 // basic configuration types
@@ -139,6 +209,7 @@
 	os
 	osArch
 	productVariables
+	osAndInApex
 )
 
 func osArchString(os string, arch string) string {
@@ -152,6 +223,7 @@
 		os:               "os",
 		osArch:           "arch_os",
 		productVariables: "product_variables",
+		osAndInApex:      "os_in_apex",
 	}[ct]
 }
 
@@ -175,6 +247,10 @@
 		}
 	case productVariables:
 		// do nothing
+	case osAndInApex:
+		if _, ok := osAndInApexMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os+in_apex config: %s", config))
+		}
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
 	}
@@ -198,6 +274,8 @@
 			return ConditionsDefaultSelectKey
 		}
 		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
+	case osAndInApex:
+		return osAndInApexMap[config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
 	}
@@ -212,13 +290,16 @@
 	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
 	// An axis for arch+os-specific configurations
 	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
+	// An axis for os+in_apex-specific configurations
+	OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
 )
 
 // ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
+func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis {
 	return ConfigurationAxis{
 		configurationType: productVariables,
 		subType:           variable,
+		outerAxisType:     outerAxis.configurationType,
 	}
 }
 
@@ -229,6 +310,8 @@
 	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
 	// distinguish between them without needing to list all 17 product variables.
 	subType string
+	// used to keep track of which product variables are arch variant
+	outerAxisType configurationType
 }
 
 func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index d32e619..cf8e9f7 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -134,13 +134,13 @@
 sharedLibraries = []
 rootSharedLibraries = []
 
-shared_info_tag = "@_builtins//:common/cc/experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
+shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo"
+
 if shared_info_tag in providers(target):
   shared_info = providers(target)[shared_info_tag]
-  for lib in shared_info.linker_input.libraries:
-    path = lib.dynamic_library.path
-    rootSharedLibraries += [path]
-    sharedLibraries.append(path)
+  path = shared_info.output_file.path
+  sharedLibraries.append(path)
+  rootSharedLibraries += [path]
 else:
   for linker_input in linker_inputs:
     for library in linker_input.libraries:
@@ -235,14 +235,14 @@
 //   - The function body should not be indented outside of its own scope.
 func (g getApexInfoType) StarlarkFunctionBody() string {
 	return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"]
-return "{%s}" % ",".join([
-    json_for_file("signed_output", info.signed_output),
-    json_for_file("unsigned_output", info.unsigned_output),
-    json_for_labels("provides_native_libs", info.provides_native_libs),
-    json_for_labels("requires_native_libs", info.requires_native_libs),
-    json_for_files("bundle_key_pair", info.bundle_key_pair),
-    json_for_files("container_key_pair", info.container_key_pair)
-    ])`
+return json_encode({
+    "signed_output": info.signed_output.path,
+    "unsigned_output": info.unsigned_output.path,
+    "provides_native_libs": [str(lib) for lib in info.provides_native_libs],
+    "requires_native_libs": [str(lib) for lib in info.requires_native_libs],
+    "bundle_key_pair": [f.path for f in info.bundle_key_pair],
+    "container_key_pair": [f.path for f in info.container_key_pair]
+})`
 }
 
 type ApexCqueryInfo struct {
@@ -259,7 +259,9 @@
 // Starlark given in StarlarkFunctionBody.
 func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo {
 	var info ApexCqueryInfo
-	if err := json.Unmarshal([]byte(rawString), &info); err != nil {
+	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))
 	}
 	return info
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 34248ce..46eb0b6 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -161,7 +161,7 @@
 		actualOutput, err := GetCcInfo.ParseResult(tc.input)
 		if (err == nil && tc.expectedErrorMessage != "") ||
 			(err != nil && err.Error() != tc.expectedErrorMessage) {
-			t.Errorf("%q:\nexpected Error %s\n, got %s", tc.description, tc.expectedErrorMessage, err)
+			t.Errorf("%q:\n%12s: %q\n%12s: %q", tc.description, "expect Error", tc.expectedErrorMessage, "but got", err)
 		} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
 			t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput)
 		}
diff --git a/bazel/properties.go b/bazel/properties.go
index aba97c6..11f6247 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -119,7 +119,7 @@
 	return dirs
 }
 
-// Add inserts the label Label at the end of the LabelList.
+// Add inserts the label Label at the end of the LabelList.Includes.
 func (ll *LabelList) Add(label *Label) {
 	if label == nil {
 		return
@@ -127,6 +127,14 @@
 	ll.Includes = append(ll.Includes, *label)
 }
 
+// AddExclude inserts the label Label at the end of the LabelList.Excludes.
+func (ll *LabelList) AddExclude(label *Label) {
+	if label == nil {
+		return
+	}
+	ll.Excludes = append(ll.Excludes, *label)
+}
+
 // Append appends the fields of other labelList to the corresponding fields of ll.
 func (ll *LabelList) Append(other LabelList) {
 	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
@@ -137,6 +145,30 @@
 	}
 }
 
+// Partition splits a LabelList into two LabelLists depending on the return value
+// of the predicate.
+// This function preserves the Includes and Excludes, but it does not provide
+// that information to the partition function.
+func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
+	predicated := LabelList{}
+	unpredicated := LabelList{}
+	for _, include := range ll.Includes {
+		if predicate(include) {
+			predicated.Add(&include)
+		} else {
+			unpredicated.Add(&include)
+		}
+	}
+	for _, exclude := range ll.Excludes {
+		if predicate(exclude) {
+			predicated.AddExclude(&exclude)
+		} else {
+			unpredicated.AddExclude(&exclude)
+		}
+	}
+	return predicated, unpredicated
+}
+
 // UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
 // the slice in a sorted order.
 func UniqueSortedBazelLabels(originalLabels []Label) []Label {
@@ -277,7 +309,19 @@
 	_, containsProductVariables := axisTypes[productVariables]
 	if containsProductVariables {
 		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+			if containsArch {
+				allProductVariablesAreArchVariant := true
+				for k := range la.ConfigurableValues {
+					if k.configurationType == productVariables && k.outerAxisType != arch {
+						allProductVariablesAreArchVariant = false
+					}
+				}
+				if !allProductVariablesAreArchVariant {
+					return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+				}
+			} else {
+				return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+			}
 		}
 	}
 	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
@@ -332,7 +376,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		la.Value = &value
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		if la.ConfigurableValues == nil {
 			la.ConfigurableValues = make(configurableLabels)
 		}
@@ -348,7 +392,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		return la.Value
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		return la.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -424,7 +468,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		ba.Value = value
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		if ba.ConfigurableValues == nil {
 			ba.ConfigurableValues = make(configurableBools)
 		}
@@ -540,7 +584,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		return ba.Value
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		if v, ok := ba.ConfigurableValues[axis][config]; ok {
 			return &v
 		} else {
@@ -676,7 +720,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		lla.Value = list
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		if lla.ConfigurableValues == nil {
 			lla.ConfigurableValues = make(configurableLabelLists)
 		}
@@ -692,7 +736,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		return lla.Value
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		return lla.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -822,6 +866,29 @@
 	}
 }
 
+// Partition splits a LabelListAttribute into two LabelListAttributes depending
+// on the return value of the predicate.
+// This function preserves the Includes and Excludes, but it does not provide
+// that information to the partition function.
+func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
+	predicated := LabelListAttribute{}
+	unpredicated := LabelListAttribute{}
+
+	valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
+	predicated.SetValue(valuePartitionTrue)
+	unpredicated.SetValue(valuePartitionFalse)
+
+	for axis, selectValueLabelLists := range lla.ConfigurableValues {
+		for config, labelList := range selectValueLabelLists {
+			configPredicated, configUnpredicated := labelList.Partition(predicate)
+			predicated.SetSelectValue(axis, config, configPredicated)
+			unpredicated.SetSelectValue(axis, config, configUnpredicated)
+		}
+	}
+
+	return predicated, unpredicated
+}
+
 // OtherModuleContext is a limited context that has methods with information about other modules.
 type OtherModuleContext interface {
 	ModuleFromName(name string) (blueprint.Module, bool)
@@ -960,6 +1027,146 @@
 	return ret
 }
 
+// StringAttribute corresponds to the string Bazel attribute type with
+// support for additional metadata, like configurations.
+type StringAttribute struct {
+	// The base value of the string attribute.
+	Value *string
+
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableStrings
+}
+
+type configurableStrings map[ConfigurationAxis]stringSelectValues
+
+func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
+	if cs[axis] == nil {
+		cs[axis] = make(stringSelectValues)
+	}
+	var v = ""
+	if str != nil {
+		v = *str
+	}
+	cs[axis][config] = v
+}
+
+type stringSelectValues map[string]string
+
+// HasConfigurableValues returns true if the attribute contains axis-specific string values.
+func (sa StringAttribute) HasConfigurableValues() bool {
+	for _, selectValues := range sa.ConfigurableValues {
+		if len(selectValues) > 0 {
+			return true
+		}
+	}
+	return false
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		sa.Value = str
+	case arch, os, osArch, productVariables:
+		if sa.ConfigurableValues == nil {
+			sa.ConfigurableValues = make(configurableStrings)
+		}
+		sa.ConfigurableValues.setValueForAxis(axis, config, str)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return sa.Value
+	case arch, os, osArch, productVariables:
+		if v, ok := sa.ConfigurableValues[axis][config]; ok {
+			return &v
+		} else {
+			return nil
+		}
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
+	for k := range sa.ConfigurableValues {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
+// Collapse reduces the configurable axes of the string attribute to a single axis.
+// This is necessary for final writing to bp2build, as a configurable string
+// attribute can only be comprised by a single select.
+func (sa *StringAttribute) Collapse() error {
+	axisTypes := sa.axisTypes()
+	_, containsOs := axisTypes[os]
+	_, containsArch := axisTypes[arch]
+	_, containsOsArch := axisTypes[osArch]
+	_, containsProductVariables := axisTypes[productVariables]
+	if containsProductVariables {
+		if containsOs || containsArch || containsOsArch {
+			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
+		}
+	}
+	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
+		// If a bool attribute has both os and arch configuration axes, the only
+		// way to successfully union their values is to increase the granularity
+		// of the configuration criteria to os_arch.
+		for osType, supportedArchs := range osToArchMap {
+			for _, supportedArch := range supportedArchs {
+				osArch := osArchString(osType, supportedArch)
+				if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
+					// Do nothing, as the arch_os is explicitly defined already.
+				} else {
+					archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
+					osVal := sa.SelectValue(OsConfigurationAxis, osType)
+					if osVal != nil && archVal != nil {
+						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
+						// runs after os mutator.
+						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+					} else if osVal != nil && archVal == nil {
+						sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
+					} else if osVal == nil && archVal != nil {
+						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+					}
+				}
+			}
+		}
+		// All os_arch values are now set. Clear os and arch axes.
+		delete(sa.ConfigurableValues, ArchConfigurationAxis)
+		delete(sa.ConfigurableValues, OsConfigurationAxis)
+		// Verify post-condition; this should never fail, provided no additional
+		// axes are introduced.
+		if len(sa.ConfigurableValues) > 1 {
+			panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
+		}
+	}
+	return nil
+}
+
+func (sa *StringAttribute) axisTypes() map[configurationType]bool {
+	types := map[configurationType]bool{}
+	for k := range sa.ConfigurableValues {
+		if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
+			types[k.configurationType] = true
+		}
+	}
+	return types
+}
+
 // StringListAttribute corresponds to the string_list Bazel attribute type with
 // support for additional metadata, like configurations.
 type StringListAttribute struct {
@@ -1049,7 +1256,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		sla.Value = list
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		if sla.ConfigurableValues == nil {
 			sla.ConfigurableValues = make(configurableStringLists)
 		}
@@ -1065,7 +1272,7 @@
 	switch axis.configurationType {
 	case noConfig:
 		return sla.Value
-	case arch, os, osArch, productVariables:
+	case arch, os, osArch, productVariables, osAndInApex:
 		return sla.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
@@ -1085,15 +1292,18 @@
 
 // DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
 // configuration-specific values. For example, if we would convert this StringListAttribute as:
-// ["a", "b", "c"] + select({
-//    "//condition:one": ["a", "d"],
-//    "//conditions:default": [],
-// })
+//
+//	["a", "b", "c"] + select({
+//	   "//condition:one": ["a", "d"],
+//	   "//conditions:default": [],
+//	})
+//
 // after this function, we would convert this StringListAttribute as:
-// ["a", "b", "c"] + select({
-//    "//condition:one": ["d"],
-//    "//conditions:default": [],
-// })
+//
+//	["a", "b", "c"] + select({
+//	   "//condition:one": ["d"],
+//	   "//conditions:default": [],
+//	})
 func (sla *StringListAttribute) DeduplicateAxesFromBase() {
 	base := sla.Value
 	for axis, configToList := range sla.ConfigurableValues {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 7b76b74..8729381 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -247,13 +247,13 @@
 			OsArchConfigurationAxis: labelListSelectValues{
 				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_with_defaults"): labelListSelectValues{
+			ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{
 				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
 				"b":                        makeLabelList([]string{"b_val"}, []string{}),
 				"c":                        makeLabelList([]string{"c_val"}, []string{}),
 				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{
+			ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
 				"a": makeLabelList([]string{}, []string{"not_in_value"}),
 			},
 		},
@@ -281,7 +281,7 @@
 			"linux_x86":                makeLabels("linux_x86_include"),
 			ConditionsDefaultConfigKey: nilLabels,
 		},
-		ProductVariableConfigurationAxis("product_with_defaults"): {
+		ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): {
 			"a":                        nilLabels,
 			"b":                        makeLabels("b_val"),
 			"c":                        makeLabels("c_val"),
@@ -310,6 +310,134 @@
 	}
 }
 
+func TestLabelListAttributePartition(t *testing.T) {
+	testCases := []struct {
+		name         string
+		input        LabelListAttribute
+		predicated   LabelListAttribute
+		unpredicated LabelListAttribute
+		predicate    func(label Label) bool
+	}{
+		{
+			name: "move all to predicated partition",
+			input: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+			)),
+			predicated: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+			)),
+			unpredicated: LabelListAttribute{},
+			predicate: func(label Label) bool {
+				return true
+			},
+		},
+		{
+			name: "move all to unpredicated partition",
+			input: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+			)),
+			predicated: LabelListAttribute{},
+			unpredicated: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+			)),
+			predicate: func(label Label) bool {
+				return false
+			},
+		},
+		{
+			name: "partition includes and excludes",
+			input: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+			)),
+			predicated: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "keep2"},
+				[]string{"keep1", "keep2"},
+			)),
+			unpredicated: MakeLabelListAttribute(makeLabelList(
+				[]string{"throw1", "throw2"},
+				[]string{"throw1", "throw2"},
+			)),
+			predicate: func(label Label) bool {
+				return strings.HasPrefix(label.Label, "keep")
+			},
+		},
+		{
+			name: "partition excludes only",
+			input: MakeLabelListAttribute(makeLabelList(
+				[]string{},
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+			)),
+			predicated: MakeLabelListAttribute(makeLabelList(
+				[]string{},
+				[]string{"keep1", "keep2"},
+			)),
+			unpredicated: MakeLabelListAttribute(makeLabelList(
+				[]string{},
+				[]string{"throw1", "throw2"},
+			)),
+			predicate: func(label Label) bool {
+				return strings.HasPrefix(label.Label, "keep")
+			},
+		},
+		{
+			name: "partition includes only",
+			input: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "throw1", "keep2", "throw2"},
+				[]string{},
+			)),
+			predicated: MakeLabelListAttribute(makeLabelList(
+				[]string{"keep1", "keep2"},
+				[]string{},
+			)),
+			unpredicated: MakeLabelListAttribute(makeLabelList(
+				[]string{"throw1", "throw2"},
+				[]string{},
+			)),
+			predicate: func(label Label) bool {
+				return strings.HasPrefix(label.Label, "keep")
+			},
+		},
+		{
+			name:         "empty partition",
+			input:        MakeLabelListAttribute(makeLabelList([]string{}, []string{})),
+			predicated:   LabelListAttribute{},
+			unpredicated: LabelListAttribute{},
+			predicate: func(label Label) bool {
+				return true
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			predicated, unpredicated := tc.input.Partition(tc.predicate)
+			if !predicated.Value.Equals(tc.predicated.Value) {
+				t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
+			}
+			for axis, configs := range predicated.ConfigurableValues {
+				tcConfigs, ok := tc.predicated.ConfigurableValues[axis]
+				if !ok || !reflect.DeepEqual(configs, tcConfigs) {
+					t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
+				}
+			}
+			if !unpredicated.Value.Equals(tc.unpredicated.Value) {
+				t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
+			}
+			for axis, configs := range unpredicated.ConfigurableValues {
+				tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis]
+				if !ok || !reflect.DeepEqual(configs, tcConfigs) {
+					t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
+				}
+			}
+		})
+	}
+}
+
 // labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
 // typ
 func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
@@ -546,7 +674,7 @@
 			OsArchConfigurationAxis: stringListSelectValues{
 				"linux_x86": {"linux_x86_include"},
 			},
-			ProductVariableConfigurationAxis("a"): stringListSelectValues{
+			ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
 				"a": []string{"not_in_value"},
 			},
 		},
@@ -571,7 +699,7 @@
 			"linux": []string{"linux_include"},
 		},
 		OsArchConfigurationAxis: stringListSelectValues{},
-		ProductVariableConfigurationAxis("a"): stringListSelectValues{
+		ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
 			"a": []string{"not_in_value"},
 		},
 	}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 1fabfaa..3d9fc5a 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -15,6 +15,7 @@
         "conversion.go",
         "metrics.go",
         "symlink_forest.go",
+        "testing.go",
     ],
     deps: [
         "soong-android",
@@ -50,6 +51,7 @@
         "cc_prebuilt_library_conversion_test.go",
         "cc_prebuilt_library_shared_test.go",
         "cc_prebuilt_library_static_test.go",
+        "cc_test_conversion_test.go",
         "cc_yasm_conversion_test.go",
         "conversion_test.go",
         "filegroup_conversion_test.go",
@@ -62,13 +64,13 @@
         "java_plugin_conversion_test.go",
         "java_proto_conversion_test.go",
         "linker_config_conversion_test.go",
+        "ndk_headers_conversion_test.go",
         "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
         "python_library_conversion_test.go",
         "sh_conversion_test.go",
         "soong_config_module_type_conversion_test.go",
-        "testing.go",
     ],
     pluginFor: [
         "soong_build",
diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go
index 8e7c2b5..ff82694 100644
--- a/bp2build/aar_conversion_test.go
+++ b/bp2build/aar_conversion_test.go
@@ -53,7 +53,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget(
+			MakeBazelTarget(
 				"android_library",
 				"TestLib",
 				AttrNameToString{
@@ -120,7 +120,7 @@
 }
 `,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget(
+				MakeBazelTarget(
 					"aar_import",
 					"TestImport",
 					AttrNameToString{
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index a37af12..e112be3 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -47,7 +47,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("android_binary", "TestApp", AttrNameToString{
+			MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
 				"srcs":           `["app.java"]`,
 				"manifest":       `"AndroidManifest.xml"`,
 				"resource_files": `["res/res.png"]`,
@@ -79,7 +79,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("android_binary", "TestApp", AttrNameToString{
+			MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
 				"srcs":     `["app.java"]`,
 				"manifest": `"manifest/AndroidManifest.xml"`,
 				"resource_files": `[
@@ -119,7 +119,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("android_binary", "TestApp", AttrNameToString{
+			MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
 				"srcs": `select({
         "//build/bazel/platforms/arch:arm": ["arm.java"],
         "//build/bazel/platforms/arch:x86": ["x86.java"],
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 415e695..b0a2966 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -141,7 +141,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"android_manifest": `"ApogeeAndroidManifest.xml"`,
 				"binaries": `[
         ":cc_binary_1",
@@ -203,7 +203,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"file_contexts": `"//a/b:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 			}),
@@ -223,7 +223,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"file_contexts": `"file_contexts_file"`,
 				"manifest":      `"apex_manifest.json"`,
 			}),
@@ -252,7 +252,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 			}),
@@ -275,7 +275,7 @@
 		},
 		Blueprint: createMultilibBlueprint("both"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_32": `[
         ":native_shared_lib_1",
         ":native_shared_lib_3",
@@ -319,7 +319,7 @@
 		},
 		Blueprint: createMultilibBlueprint("first"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_32": `select({
         "//build/bazel/platforms/arch:arm": [
             ":native_shared_lib_1",
@@ -368,7 +368,7 @@
 		},
 		Blueprint: createMultilibBlueprint("32"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_32": `[
         ":native_shared_lib_1",
         ":native_shared_lib_3",
@@ -399,7 +399,7 @@
 		},
 		Blueprint: createMultilibBlueprint("64"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 				"native_shared_libs_64": `select({
         "//build/bazel/platforms/arch:arm64": [
             ":native_shared_lib_1",
@@ -439,7 +439,7 @@
 	manifest: "apogee_manifest.json",
 }
 `,
-		ExpectedBazelTargets: []string{makeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+		ExpectedBazelTargets: []string{MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
 			"manifest":      `"apogee_manifest.json"`,
 			"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 		}),
@@ -467,7 +467,7 @@
 	bazel_module: { bp2build_available: true },
 }
 `,
-		ExpectedBazelTargets: []string{makeBazelTarget("apex", "apogee", AttrNameToString{
+		ExpectedBazelTargets: []string{MakeBazelTarget("apex", "apogee", AttrNameToString{
 			"manifest":      `"manifest.json"`,
 			"file_contexts": `"//system/sepolicy/apex:apogee-file_contexts"`,
 		}),
@@ -624,7 +624,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"android_manifest": `"ApogeeAndroidManifest.xml"`,
 				"binaries": `[
         ":cc_binary_1",
@@ -684,7 +684,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"//a/b:apex_manifest.json"`,
 			}),
@@ -718,7 +718,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"//a/b:apogee_manifest.json"`,
 			}),
@@ -750,7 +750,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 			}),
@@ -783,7 +783,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apogee_manifest.json"`,
 			}),
@@ -816,7 +816,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 				"package_name":  `"com.google.android.apogee"`,
@@ -855,7 +855,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 				"prebuilts":     `[":prebuilt_file"]`,
@@ -900,7 +900,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 				"prebuilts":     `[":prebuilt_file2"]`,
@@ -940,7 +940,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 				"prebuilts":     `[]`,
@@ -974,7 +974,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":       `"apex_manifest.json"`,
 				"logging_parent": `"foo.bar.baz"`,
@@ -1009,7 +1009,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":       `"apex_manifest.json"`,
 				"logging_parent": `"foo.bar.baz.override"`,
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 242ea1e..ca8185e 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -43,7 +43,6 @@
 	content         string
 	ruleClass       string
 	bzlLoadLocation string
-	handcrafted     bool
 }
 
 // IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
@@ -65,25 +64,9 @@
 // BazelTargets is a typedef for a slice of BazelTarget objects.
 type BazelTargets []BazelTarget
 
-// HasHandcraftedTargetsreturns true if a set of bazel targets contain
-// handcrafted ones.
-func (targets BazelTargets) hasHandcraftedTargets() bool {
-	for _, target := range targets {
-		if target.handcrafted {
-			return true
-		}
-	}
-	return false
-}
-
-// sort a list of BazelTargets in-place, by name, and by generated/handcrafted types.
+// sort a list of BazelTargets in-place by name
 func (targets BazelTargets) sort() {
 	sort.Slice(targets, func(i, j int) bool {
-		if targets[i].handcrafted != targets[j].handcrafted {
-			// Handcrafted targets will be generated after the bp2build generated targets.
-			return targets[j].handcrafted
-		}
-		// This will cover all bp2build generated targets.
 		return targets[i].name < targets[j].name
 	})
 }
@@ -94,18 +77,6 @@
 func (targets BazelTargets) String() string {
 	var res string
 	for i, target := range targets {
-		// There is only at most 1 handcrafted "target", because its contents
-		// represent the entire BUILD file content from the tree. See
-		// build_conversion.go#getHandcraftedBuildContent for more information.
-		//
-		// Add a header to make it easy to debug where the handcrafted targets
-		// are in a generated BUILD file.
-		if target.handcrafted {
-			res += "# -----------------------------\n"
-			res += "# Section: Handcrafted targets. \n"
-			res += "# -----------------------------\n\n"
-		}
-
 		res += target.content
 		if i != len(targets)-1 {
 			res += "\n\n"
@@ -256,7 +227,6 @@
 
 func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
 	buildFileToTargets := make(map[string]BazelTargets)
-	buildFileToAppend := make(map[string]bool)
 
 	// Simple metrics tracking for bp2build
 	metrics := CodegenMetrics{
@@ -288,30 +258,10 @@
 				// Handle modules converted to handcrafted targets.
 				//
 				// Since these modules are associated with some handcrafted
-				// target in a BUILD file, we simply append the entire contents
-				// of that BUILD file to the generated BUILD file.
-				//
-				// The append operation is only done once, even if there are
-				// multiple modules from the same directory associated to
-				// targets in the same BUILD file (or package).
+				// target in a BUILD file, we don't autoconvert them.
 
 				// Log the module.
 				metrics.AddConvertedModule(m, moduleType, Handcrafted)
-
-				pathToBuildFile := getBazelPackagePath(b)
-				if _, exists := buildFileToAppend[pathToBuildFile]; exists {
-					// Append the BUILD file content once per package, at most.
-					return
-				}
-				t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
-				if err != nil {
-					errs = append(errs, fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
-					return
-				}
-				targets = append(targets, t)
-				// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
-				// something more targeted based on the rule type and target
-				buildFileToAppend[pathToBuildFile] = true
 			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
 				// Handle modules converted to generated targets.
 
@@ -339,7 +289,9 @@
 						return
 					}
 				}
-				targets = generateBazelTargets(bpCtx, aModule)
+				var targetErrs []error
+				targets, targetErrs = generateBazelTargets(bpCtx, aModule)
+				errs = append(errs, targetErrs...)
 				for _, t := range targets {
 					// A module can potentially generate more than 1 Bazel
 					// target, each of a different rule class.
@@ -356,7 +308,10 @@
 				// be mapped cleanly to a bazel label.
 				return
 			}
-			t := generateSoongModuleTarget(bpCtx, m)
+			t, err := generateSoongModuleTarget(bpCtx, m)
+			if err != nil {
+				errs = append(errs, err)
+			}
 			targets = append(targets, t)
 		default:
 			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
@@ -373,6 +328,15 @@
 	if generateFilegroups {
 		// Add a filegroup target that exposes all sources in the subtree of this package
 		// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
+		//
+		// This works because: https://bazel.build/reference/be/functions#exports_files
+		// "As a legacy behaviour, also files mentioned as input to a rule are exported with the
+		// default visibility until the flag --incompatible_no_implicit_file_export is flipped. However, this behavior
+		// should not be relied upon and actively migrated away from."
+		//
+		// 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 {
 			buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
 				name:      "bp2build_all_srcs",
@@ -388,35 +352,18 @@
 	}, errs
 }
 
-func getBazelPackagePath(b android.Bazelable) string {
-	label := b.HandcraftedLabel()
-	pathToBuildFile := strings.TrimPrefix(label, "//")
-	pathToBuildFile = strings.Split(pathToBuildFile, ":")[0]
-	return pathToBuildFile
-}
-
-func getHandcraftedBuildContent(ctx *CodegenContext, b android.Bazelable, pathToBuildFile string) (BazelTarget, error) {
-	p := android.ExistentPathForSource(ctx, pathToBuildFile, HandcraftedBuildFileName)
-	if !p.Valid() {
-		return BazelTarget{}, fmt.Errorf("Could not find file %q for handcrafted target.", pathToBuildFile)
-	}
-	c, err := b.GetBazelBuildFileContents(ctx.Config(), pathToBuildFile, HandcraftedBuildFileName)
-	if err != nil {
-		return BazelTarget{}, err
-	}
-	// TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
-	return BazelTarget{
-		content:     c,
-		handcrafted: true,
-	}, nil
-}
-
-func generateBazelTargets(ctx bpToBuildContext, m android.Module) []BazelTarget {
+func generateBazelTargets(ctx bpToBuildContext, m android.Module) ([]BazelTarget, []error) {
 	var targets []BazelTarget
+	var errs []error
 	for _, m := range m.Bp2buildTargets() {
-		targets = append(targets, generateBazelTarget(ctx, m))
+		target, err := generateBazelTarget(ctx, m)
+		if err != nil {
+			errs = append(errs, err)
+			return targets, errs
+		}
+		targets = append(targets, target)
 	}
-	return targets
+	return targets, errs
 }
 
 type bp2buildModule interface {
@@ -427,13 +374,16 @@
 	BazelAttributes() []interface{}
 }
 
-func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) BazelTarget {
+func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) (BazelTarget, error) {
 	ruleClass := m.BazelRuleClass()
 	bzlLoadLocation := m.BazelRuleLoadLocation()
 
 	// extract the bazel attributes from the module.
 	attrs := m.BazelAttributes()
-	props := extractModuleProperties(attrs, true)
+	props, err := extractModuleProperties(attrs, true)
+	if err != nil {
+		return BazelTarget{}, err
+	}
 
 	// name is handled in a special manner
 	delete(props.Attrs, "name")
@@ -453,14 +403,13 @@
 			targetName,
 			attributes,
 		),
-		handcrafted: false,
-	}
+	}, nil
 }
 
 // Convert a module and its deps and props into a Bazel macro/rule
 // representation in the BUILD file.
-func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
-	props := getBuildProperties(ctx, m)
+func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) (BazelTarget, error) {
+	props, err := getBuildProperties(ctx, m)
 
 	// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
 	// items, if the modules are added using different DependencyTag. Figure
@@ -494,21 +443,21 @@
 			ctx.ModuleSubDir(m),
 			depLabelList,
 			attributes),
-	}
+	}, err
 }
 
-func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) (BazelAttributes, error) {
 	// TODO: this omits properties for blueprint modules (blueprint_go_binary,
 	// bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
 	if aModule, ok := m.(android.Module); ok {
 		return extractModuleProperties(aModule.GetProperties(), false)
 	}
 
-	return BazelAttributes{}
+	return BazelAttributes{}, nil
 }
 
 // Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) BazelAttributes {
+func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) (BazelAttributes, error) {
 	ret := map[string]string{}
 
 	// Iterate over this android.Module's property structs.
@@ -521,24 +470,29 @@
 		// manipulate internal props, if needed.
 		if isStructPtr(propertiesValue.Type()) {
 			structValue := propertiesValue.Elem()
-			for k, v := range extractStructProperties(structValue, 0) {
+			ok, err := extractStructProperties(structValue, 0)
+			if err != nil {
+				return BazelAttributes{}, err
+			}
+			for k, v := range ok {
 				if existing, exists := ret[k]; checkForDuplicateProperties && exists {
-					panic(fmt.Errorf(
+					return BazelAttributes{}, fmt.Errorf(
 						"%s (%v) is present in properties whereas it should be consolidated into a commonAttributes",
-						k, existing))
+						k, existing)
 				}
 				ret[k] = v
 			}
 		} else {
-			panic(fmt.Errorf(
-				"properties must be a pointer to a struct, got %T",
-				propertiesValue.Interface()))
+			return BazelAttributes{},
+				fmt.Errorf(
+					"properties must be a pointer to a struct, got %T",
+					propertiesValue.Interface())
 		}
 	}
 
 	return BazelAttributes{
 		Attrs: ret,
-	}
+	}, nil
 }
 
 func isStructPtr(t reflect.Type) bool {
@@ -596,7 +550,12 @@
 		}
 
 		// Sort and print the struct props by the key.
-		structProps := extractStructProperties(propertyValue, indent)
+		structProps, err := extractStructProperties(propertyValue, indent)
+
+		if err != nil {
+			return "", err
+		}
+
 		if len(structProps) == 0 {
 			return "", nil
 		}
@@ -615,11 +574,13 @@
 // which each property value correctly pretty-printed and indented at the right nest level,
 // since property structs can be nested. In Starlark, nested structs are represented as nested
 // dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+func extractStructProperties(structValue reflect.Value, indent int) (map[string]string, error) {
 	if structValue.Kind() != reflect.Struct {
-		panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+		return map[string]string{}, fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind())
 	}
 
+	var err error
+
 	ret := map[string]string{}
 	structType := structValue.Type()
 	for i := 0; i < structValue.NumField(); i++ {
@@ -640,7 +601,10 @@
 				fieldValue = fieldValue.Elem()
 			}
 			if fieldValue.Type().Kind() == reflect.Struct {
-				propsToMerge := extractStructProperties(fieldValue, indent)
+				propsToMerge, err := extractStructProperties(fieldValue, indent)
+				if err != nil {
+					return map[string]string{}, err
+				}
 				for prop, value := range propsToMerge {
 					ret[prop] = value
 				}
@@ -649,20 +613,20 @@
 		}
 
 		propertyName := proptools.PropertyNameForField(field.Name)
-		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1, false)
+		var prettyPrintedValue string
+		prettyPrintedValue, err = prettyPrint(fieldValue, indent+1, false)
 		if err != nil {
-			panic(
-				fmt.Errorf(
-					"Error while parsing property: %q. %s",
-					propertyName,
-					err))
+			return map[string]string{}, fmt.Errorf(
+				"Error while parsing property: %q. %s",
+				propertyName,
+				err)
 		}
 		if prettyPrintedValue != "" {
 			ret[propertyName] = prettyPrintedValue
 		}
 	}
 
-	return ret
+	return ret, nil
 }
 
 func isZero(value reflect.Value) bool {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index c5644ed..dd28c3c 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -231,6 +231,52 @@
 func TestGenerateBazelTargetModules(t *testing.T) {
 	testCases := []Bp2buildTestCase{
 		{
+			Description: "string prop empty",
+			Blueprint: `custom {
+	name: "foo",
+    string_literal_prop: "",
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("custom", "foo", AttrNameToString{
+					"string_literal_prop": `""`,
+				}),
+			},
+		},
+		{
+			Description: `string prop "PROP"`,
+			Blueprint: `custom {
+	name: "foo",
+    string_literal_prop: "PROP",
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("custom", "foo", AttrNameToString{
+					"string_literal_prop": `"PROP"`,
+				}),
+			},
+		},
+		{
+			Description: `string prop arch variant`,
+			Blueprint: `custom {
+    name: "foo",
+    arch: {
+        arm: { string_literal_prop: "ARM" },
+        arm64: { string_literal_prop: "ARM64" },
+    },
+    bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("custom", "foo", AttrNameToString{
+					"string_literal_prop": `select({
+        "//build/bazel/platforms/arch:arm": "ARM",
+        "//build/bazel/platforms/arch:arm64": "ARM64",
+        "//conditions:default": None,
+    })`,
+				}),
+			},
+		},
+		{
 			Description: "string ptr props",
 			Blueprint: `custom {
 	name: "foo",
@@ -238,13 +284,13 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "foo", AttrNameToString{
+				MakeBazelTarget("custom", "foo", AttrNameToString{
 					"string_ptr_prop": `""`,
 				}),
 			},
 		},
 		{
-			Description: "string props",
+			Description: "string list props",
 			Blueprint: `custom {
   name: "foo",
     string_list_prop: ["a", "b"],
@@ -252,7 +298,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "foo", AttrNameToString{
+				MakeBazelTarget("custom", "foo", AttrNameToString{
 					"string_list_prop": `[
         "a",
         "b",
@@ -270,7 +316,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "foo", AttrNameToString{
+				MakeBazelTarget("custom", "foo", AttrNameToString{
 					"string_list_prop": `[
         "\t",
         "\n",
@@ -293,10 +339,10 @@
   bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "dep", AttrNameToString{
+				MakeBazelTarget("custom", "dep", AttrNameToString{
 					"arch_paths": `["abc"]`,
 				}),
-				makeBazelTarget("custom", "has_dep", AttrNameToString{
+				MakeBazelTarget("custom", "has_dep", AttrNameToString{
 					"arch_paths": `[":dep"]`,
 				}),
 			},
@@ -309,7 +355,7 @@
   bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "has_dep", AttrNameToString{
+				MakeBazelTarget("custom", "has_dep", AttrNameToString{
 					"arch_paths": `[":dep__BP2BUILD__MISSING__DEP"]`,
 				}),
 			},
@@ -345,7 +391,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "arch_paths", AttrNameToString{
+				MakeBazelTarget("custom", "arch_paths", AttrNameToString{
 					"arch_paths": `select({
         "//build/bazel/platforms/arch:arm": [
             "arm.txt",
@@ -423,10 +469,10 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "dep", AttrNameToString{
+				MakeBazelTarget("custom", "dep", AttrNameToString{
 					"arch_paths": `["abc"]`,
 				}),
-				makeBazelTarget("custom", "has_dep", AttrNameToString{
+				MakeBazelTarget("custom", "has_dep", AttrNameToString{
 					"arch_paths": `select({
         "//build/bazel/platforms/arch:x86": [":dep"],
         "//conditions:default": [],
@@ -442,7 +488,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "embedded_props", AttrNameToString{
+				MakeBazelTarget("custom", "embedded_props", AttrNameToString{
 					"embedded_attr": `"abc"`,
 				}),
 			},
@@ -455,7 +501,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("custom", "ptr_to_embedded_props", AttrNameToString{
+				MakeBazelTarget("custom", "ptr_to_embedded_props", AttrNameToString{
 					"other_embedded_attr": `"abc"`,
 				}),
 			},
@@ -705,7 +751,7 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.Description, func(t *testing.T) {
-			runBp2BuildTestCaseSimple(t, tc)
+			RunBp2BuildTestCaseSimple(t, tc)
 		})
 	}
 }
@@ -1253,9 +1299,7 @@
     name: "fg_foo",
     bazel_module: { label: "//other:fg_foo" },
 }`,
-			ExpectedBazelTargets: []string{
-				`// BUILD file`,
-			},
+			ExpectedBazelTargets: []string{},
 			Filesystem: map[string]string{
 				"other/BUILD.bazel": `// BUILD file`,
 			},
@@ -1273,9 +1317,7 @@
         name: "foo",
         bazel_module: { label: "//other:foo" },
     }`,
-			ExpectedBazelTargets: []string{
-				`// BUILD file`,
-			},
+			ExpectedBazelTargets: []string{},
 			Filesystem: map[string]string{
 				"other/BUILD.bazel": `// BUILD file`,
 			},
@@ -1303,7 +1345,6 @@
 			},
 			ExpectedBazelTargets: []string{
 				MakeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
-				`// definition for fg_bar`,
 			},
 		},
 		{
@@ -1329,7 +1370,6 @@
     }`,
 			ExpectedBazelTargets: []string{
 				MakeBazelTargetNoRestrictions("filegroup", "fg_bar", map[string]string{}),
-				`// BUILD file`,
 			},
 		},
 	}
@@ -1374,9 +1414,6 @@
 			if actualCount != expectedCount {
 				t.Errorf("Expected %d bazel target, got %d\n%s", expectedCount, actualCount, bazelTargets)
 			}
-			if !strings.Contains(bazelTargets.String(), "Section: Handcrafted targets. ") {
-				t.Errorf("Expected string representation of bazelTargets to contain handcrafted section header.")
-			}
 			for i, target := range bazelTargets {
 				actualContent := target.content
 				expectedContent := testCase.ExpectedBazelTargets[i]
@@ -1457,7 +1494,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.Description, func(t *testing.T) {
-			runBp2BuildTestCaseSimple(t, testCase)
+			RunBp2BuildTestCaseSimple(t, testCase)
 		})
 	}
 }
@@ -1499,7 +1536,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("py_library", "fg_foo", map[string]string{
+				MakeBazelTarget("py_library", "fg_foo", map[string]string{
 					"data": `select({
         "//build/bazel/platforms/arch:arm": [":reqdarm"],
         "//build/bazel/platforms/arch:x86": [":reqdx86"],
@@ -1526,7 +1563,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("py_library", "fg_foo", map[string]string{
+				MakeBazelTarget("py_library", "fg_foo", map[string]string{
 					"data": `[
         "data.bin",
         ":reqd",
@@ -1556,7 +1593,7 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.Description, func(t *testing.T) {
-			runBp2BuildTestCaseSimple(t, tc)
+			RunBp2BuildTestCaseSimple(t, tc)
 		})
 	}
 }
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 6cb9509..28d2c75 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -15,11 +15,12 @@
 package bp2build
 
 import (
-	"android/soong/android"
 	"io/ioutil"
 	"os"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func setUp() {
@@ -103,6 +104,7 @@
         "one_to_many_prop": attr.bool(),
         "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
+        "string_literal_prop": attr.string(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
     },
@@ -132,6 +134,7 @@
         "one_to_many_prop": attr.bool(),
         "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
+        "string_literal_prop": attr.string(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
     },
@@ -161,6 +164,7 @@
         "one_to_many_prop": attr.bool(),
         "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
+        "string_literal_prop": attr.string(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
         # test_prop start
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 95869dd..67d4a1c 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -543,3 +543,129 @@
 		},
 	})
 }
+
+func TestCcBinaryRuntimeLibs(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary with runtime libs",
+		blueprint: `
+cc_library {
+    name: "bar",
+    srcs: ["b.cc"],
+}
+
+{rule_name} {
+    name: "foo",
+    srcs: ["a.cc"],
+    runtime_libs: ["bar"],
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_library_static", "bar_bp2build_cc_library_static", AttrNameToString{
+				"local_includes":         `["."]`,
+				"srcs":                   `["b.cc"]`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			},
+			},
+			{"cc_library_shared", "bar", AttrNameToString{
+				"local_includes":         `["."]`,
+				"srcs":                   `["b.cc"]`,
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			},
+			},
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"srcs":           `["a.cc"]`,
+				"runtime_deps":   `[":bar"]`,
+			},
+			},
+		},
+	})
+}
+
+func TestCcBinaryWithInstructionSet(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "instruction set",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    arch: {
+      arm: {
+        instruction_set: "arm",
+      }
+    }
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/arch:arm": [
+            "arm_isa_arm",
+            "-arm_isa_thumb",
+        ],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryEmptySuffix(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "binary with empty suffix",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    suffix: "",
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"suffix":         `""`,
+			}},
+		},
+	})
+}
+
+func TestCcBinarySuffix(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "binary with suffix",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    suffix: "-suf",
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"suffix":         `"-suf"`,
+			}},
+		},
+	})
+}
+
+func TestCcArchVariantBinarySuffix(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "binary with suffix",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    arch: {
+        arm64: { suffix: "-64" },
+        arm:   { suffix: "-32" },
+		},
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"suffix": `select({
+        "//build/bazel/platforms/arch:arm": "-32",
+        "//build/bazel/platforms/arch:arm64": "-64",
+        "//conditions:default": None,
+    })`,
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 6c56d41..880ae75 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -355,7 +355,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
 				"copts": `[
         "bothflag",
         "staticflag",
@@ -377,7 +377,7 @@
         ":whole_and_static_lib_for_both",
         ":whole_static_lib_for_static",
     ]`}),
-			makeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"copts": `[
         "bothflag",
         "sharedflag",
@@ -463,7 +463,7 @@
 			simpleModuleDoNotConvertBp2build("cc_library", "shared_dep_for_both") +
 			simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_both"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
 				"copts": `[
         "bothflag",
         "staticflag",
@@ -495,7 +495,7 @@
         ":whole_static_dep_for_static",
     ]`,
 			}),
-			makeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"copts": `[
         "bothflag",
         "sharedflag",
@@ -560,13 +560,13 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
 				"whole_archive_deps": `[
         ":whole_static_lib_for_both_alwayslink",
         ":whole_static_lib_for_static_alwayslink",
     ]`,
 			}),
-			makeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"whole_archive_deps": `[
         ":whole_static_lib_for_both_alwayslink",
         ":whole_static_lib_for_shared_alwayslink",
@@ -654,7 +654,7 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
 				"copts": `[
         "bothflag",
         "staticflag",
@@ -678,7 +678,7 @@
         "//conditions:default": [],
     })`,
 			}),
-			makeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"copts": `[
         "bothflag",
         "sharedflag",
@@ -807,7 +807,7 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
 				"local_includes": `["."]`,
 				"srcs": `[
         "both_source.cpp",
@@ -832,7 +832,7 @@
         ":static_filegroup_c_srcs",
     ]`,
 			}),
-			makeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"local_includes": `["."]`,
 				"srcs": `[
         "both_source.cpp",
@@ -1199,6 +1199,40 @@
 	)
 }
 
+func TestCcLibraryProductVariablesHeaderLibs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+    name: "foo_static",
+    srcs: ["common.c"],
+    product_variables: {
+        malloc_not_svelte: {
+            header_libs: ["malloc_not_svelte_header_lib"],
+        },
+    },
+    include_build_directory: false,
+}
+
+cc_library {
+    name: "malloc_not_svelte_header_lib",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{
+			"implementation_deps": `select({
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_header_lib"],
+        "//conditions:default": [],
+    })`,
+			"srcs_c":                 `["common.c"]`,
+			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+		}),
+	},
+	)
+}
+
 func TestCCLibraryNoCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library - nocrt: true emits attribute",
@@ -1301,10 +1335,10 @@
 		"additional_linker_inputs": true,
 		"linkopts":                 true,
 		"strip":                    true,
-		"stubs_symbol_file":        true,
-		"stubs_versions":           true,
 		"inject_bssl_hash":         true,
+		"has_stubs":                true,
 	}
+
 	sharedAttrs := AttrNameToString{}
 	staticAttrs := AttrNameToString{}
 	for key, val := range attrs {
@@ -1315,12 +1349,32 @@
 			staticAttrs[key] = val
 		}
 	}
-	sharedTarget := makeBazelTarget("cc_library_shared", name, sharedAttrs)
-	staticTarget := makeBazelTarget("cc_library_static", name+"_bp2build_cc_library_static", staticAttrs)
+	sharedTarget := MakeBazelTarget("cc_library_shared", name, sharedAttrs)
+	staticTarget := MakeBazelTarget("cc_library_static", name+"_bp2build_cc_library_static", staticAttrs)
 
 	return []string{staticTarget, sharedTarget}
 }
 
+func makeCcStubSuiteTargets(name string, attrs AttrNameToString) string {
+	if _, hasStubs := attrs["stubs_symbol_file"]; !hasStubs {
+		return ""
+	}
+	STUB_SUITE_ATTRS := map[string]string{
+		"stubs_symbol_file": "symbol_file",
+		"stubs_versions":    "versions",
+		"soname":            "soname",
+		"source_library":    "source_library",
+	}
+
+	stubSuiteAttrs := AttrNameToString{}
+	for key, _ := range attrs {
+		if _, stubSuiteAttr := STUB_SUITE_ATTRS[key]; stubSuiteAttr {
+			stubSuiteAttrs[STUB_SUITE_ATTRS[key]] = attrs[key]
+		}
+	}
+	return MakeBazelTarget("cc_stub_suite", name+"_stub_libs", stubSuiteAttrs)
+}
+
 func TestCCLibraryNoLibCrtFalse(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		ModuleTypeUnderTest:        "cc_library",
@@ -1629,10 +1683,10 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", AttrNameToString{
 				"system_dynamic_deps": "[]",
 			}),
-			makeBazelTarget("cc_library_shared", "static_empty", AttrNameToString{}),
+			MakeBazelTarget("cc_library_shared", "static_empty", AttrNameToString{}),
 		},
 	})
 }
@@ -1652,8 +1706,8 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}),
-			makeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}),
+			MakeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{
 				"system_dynamic_deps": "[]",
 			}),
 		},
@@ -1679,8 +1733,8 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}),
-			makeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", AttrNameToString{}),
+			MakeBazelTarget("cc_library_shared", "shared_empty", AttrNameToString{
 				"system_dynamic_deps": "[]",
 			}),
 		},
@@ -1762,10 +1816,10 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"system_dynamic_deps": `[":libc"]`,
 			}),
-			makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"system_dynamic_deps": `[
         ":libc",
         ":libm",
@@ -1980,7 +2034,7 @@
 }
 `, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
 				ExpectedBazelTargets: []string{
-					makeBazelTarget("cc_library_static", name_prefix+"_static", attrs),
+					MakeBazelTarget("cc_library_static", name_prefix+"_static", attrs),
 				},
 			})
 
@@ -1999,7 +2053,7 @@
 }
 `, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
 				ExpectedBazelTargets: []string{
-					makeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs),
+					MakeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs),
 				},
 			})
 		})
@@ -2016,14 +2070,14 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
 				"deps":                              `[":libprotobuf-cpp-lite"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
 			}),
 		},
@@ -2041,15 +2095,15 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs":                `["foo.proto"]`,
 				"strip_import_prefix": `""`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
 				"deps":                              `[":libprotobuf-cpp-lite"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
 			}),
 		},
@@ -2067,14 +2121,14 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
 				"deps":                              `[":libprotobuf-cpp-lite"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
 			}),
 		},
@@ -2094,14 +2148,14 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_proto_library", "foo_cc_proto", AttrNameToString{
+			}), MakeBazelTarget("cc_proto_library", "foo_cc_proto", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_proto"]`,
 				"deps":                              `[":libprotobuf-cpp-full"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps": `[":libprotobuf-cpp-full"]`,
 			}),
 		},
@@ -2121,14 +2175,14 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
 				"deps":                              `[":libprotobuf-cpp-lite"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
 			}),
 		},
@@ -2148,14 +2202,14 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"deps":               `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
 			}),
@@ -2163,6 +2217,50 @@
 	})
 }
 
+func TestCcLibraryProtoIncludeDirs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		include_dirs: ["external/protobuf/src"],
+	},
+	include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+				"srcs": `["foo.proto"]`,
+				"deps": `["//external/protobuf:libprotobuf-proto"]`,
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"deps":                              `[":libprotobuf-cpp-lite"]`,
+				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoIncludeDirsUnknown(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		include_dirs: ["external/protobuf/abc"],
+	},
+	include_build_directory: false,
+}`,
+		ExpectedErr: fmt.Errorf("module \"foo\": Could not find the proto_library target for include dir: external/protobuf/abc"),
+	})
+}
+
 func TestCcLibraryProtoFilegroups(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		ModuleTypeUnderTest:        "cc_library",
@@ -2208,65 +2306,65 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "a_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "a_proto", AttrNameToString{
 				"srcs": `[":a_fg_proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
 				"deps": `[":a_proto"]`,
-			}), makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
 				"deps":               `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":a_cc_proto_lite"]`,
 				"srcs":               `[":a_fg_proto_cpp_srcs"]`,
 				"srcs_as":            `[":a_fg_proto_as_srcs"]`,
 				"srcs_c":             `[":a_fg_proto_c_srcs"]`,
-			}), makeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":a_cc_proto_lite"]`,
 				"srcs":               `[":a_fg_proto_cpp_srcs"]`,
 				"srcs_as":            `[":a_fg_proto_as_srcs"]`,
 				"srcs_c":             `[":a_fg_proto_c_srcs"]`,
-			}), makeBazelTarget("proto_library", "b_proto", AttrNameToString{
+			}), MakeBazelTarget("proto_library", "b_proto", AttrNameToString{
 				"srcs": `[":b_protos"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", AttrNameToString{
 				"deps": `[":b_proto"]`,
-			}), makeBazelTarget("cc_library_static", "b_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "b_bp2build_cc_library_static", AttrNameToString{
 				"deps":               `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":b_cc_proto_lite"]`,
 				"srcs":               `[":b_protos_cpp_srcs"]`,
 				"srcs_as":            `[":b_protos_as_srcs"]`,
 				"srcs_c":             `[":b_protos_c_srcs"]`,
-			}), makeBazelTarget("cc_library_shared", "b", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
 				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":b_cc_proto_lite"]`,
 				"srcs":               `[":b_protos_cpp_srcs"]`,
 				"srcs_as":            `[":b_protos_as_srcs"]`,
 				"srcs_c":             `[":b_protos_c_srcs"]`,
-			}), makeBazelTarget("proto_library", "c_proto", AttrNameToString{
+			}), MakeBazelTarget("proto_library", "c_proto", AttrNameToString{
 				"srcs": `[":c-proto-srcs"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", AttrNameToString{
 				"deps": `[":c_proto"]`,
-			}), makeBazelTarget("cc_library_static", "c_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "c_bp2build_cc_library_static", AttrNameToString{
 				"deps":               `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":c_cc_proto_lite"]`,
 				"srcs":               `[":c-proto-srcs_cpp_srcs"]`,
 				"srcs_as":            `[":c-proto-srcs_as_srcs"]`,
 				"srcs_c":             `[":c-proto-srcs_c_srcs"]`,
-			}), makeBazelTarget("cc_library_shared", "c", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "c", AttrNameToString{
 				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":c_cc_proto_lite"]`,
 				"srcs":               `[":c-proto-srcs_cpp_srcs"]`,
 				"srcs_as":            `[":c-proto-srcs_as_srcs"]`,
 				"srcs_c":             `[":c-proto-srcs_c_srcs"]`,
-			}), makeBazelTarget("proto_library", "d_proto", AttrNameToString{
+			}), MakeBazelTarget("proto_library", "d_proto", AttrNameToString{
 				"srcs": `[":proto-srcs-d"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", AttrNameToString{
 				"deps": `[":d_proto"]`,
-			}), makeBazelTarget("cc_library_static", "d_bp2build_cc_library_static", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "d_bp2build_cc_library_static", AttrNameToString{
 				"deps":               `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":d_cc_proto_lite"]`,
 				"srcs":               `[":proto-srcs-d_cpp_srcs"]`,
 				"srcs_as":            `[":proto-srcs-d_as_srcs"]`,
 				"srcs_c":             `[":proto-srcs-d_c_srcs"]`,
-			}), makeBazelTarget("cc_library_shared", "d", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "d", AttrNameToString{
 				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":d_cc_proto_lite"]`,
 				"srcs":               `[":proto-srcs-d_cpp_srcs"]`,
@@ -2365,10 +2463,10 @@
   },
 	include_build_directory: false,
 }`,
-		ExpectedBazelTargets: []string{makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+		ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 			"srcs":                   `["foo.cpp"]`,
 			"target_compatible_with": `["@platforms//:incompatible"]`,
-		}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+		}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 			"srcs": `["foo.cpp"]`,
 			"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
@@ -2405,13 +2503,13 @@
 	},
 	include_build_directory: false,
 }`,
-		ExpectedBazelTargets: []string{makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+		ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 			"srcs": `["foo.cpp"]`,
 			"target_compatible_with": `select({
         "//build/bazel/platforms/os:windows": ["@platforms//:incompatible"],
         "//conditions:default": [],
     })`,
-		}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+		}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 			"srcs": `["foo.cpp"]`,
 			"target_compatible_with": `select({
         "//build/bazel/platforms/os_arch:darwin_arm64": [],
@@ -2424,6 +2522,19 @@
 }
 
 func TestCcLibraryStubs(t *testing.T) {
+	expectedBazelTargets := makeCcLibraryTargets("a", AttrNameToString{
+		"has_stubs": `True`,
+	})
+	expectedBazelTargets = append(expectedBazelTargets, makeCcStubSuiteTargets("a", AttrNameToString{
+		"soname":            `"a.so"`,
+		"source_library":    `":a"`,
+		"stubs_symbol_file": `"a.map.txt"`,
+		"stubs_versions": `[
+        "28",
+        "29",
+        "current",
+    ]`,
+	}))
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library stubs",
 		ModuleTypeUnderTest:        "cc_library",
@@ -2439,15 +2550,8 @@
 }
 `,
 		},
-		Blueprint: soongCcLibraryPreamble,
-		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
-			"stubs_symbol_file": `"a.map.txt"`,
-			"stubs_versions": `[
-        "28",
-        "29",
-        "current",
-    ]`,
-		}),
+		Blueprint:            soongCcLibraryPreamble,
+		ExpectedBazelTargets: expectedBazelTargets,
 	},
 	)
 }
@@ -2487,14 +2591,14 @@
 	bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: append([]string{
-			makeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{
+			MakeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{
 				"srcs": `[
         "foo1.l",
         "foo2.l",
     ]`,
 				"lexopts": `["--foo_flags"]`,
 			}),
-			makeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{
+			MakeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{
 				"srcs": `[
         "bar1.ll",
         "bar2.ll",
@@ -2514,3 +2618,256 @@
 			})...),
 	})
 }
+
+func TestCCLibraryRuntimeDeps(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Blueprint: `cc_library_shared {
+	name: "bar",
+}
+
+cc_library {
+  name: "foo",
+  runtime_libs: ["foo"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"runtime_deps":   `[":foo"]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"runtime_deps":   `[":foo"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithInstructionSet(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `cc_library {
+    name: "foo",
+    arch: {
+      arm: {
+        instruction_set: "arm",
+      }
+    }
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": [
+            "arm_isa_arm",
+            "-arm_isa_thumb",
+        ],
+        "//conditions:default": [],
+    })`,
+			"local_includes": `["."]`,
+		}),
+	})
+}
+
+func TestCcLibraryEmptySuffix(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with empty suffix",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"foo.c": "",
+		},
+		Blueprint: `cc_library {
+    name: "foo",
+    suffix: "",
+    srcs: ["foo.c"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+				"suffix": `""`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySuffix(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with suffix",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"foo.c": "",
+		},
+		Blueprint: `cc_library {
+    name: "foo",
+    suffix: "-suf",
+    srcs: ["foo.c"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+				"suffix": `"-suf"`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryArchVariantSuffix(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with arch-variant suffix",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"foo.c": "",
+		},
+		Blueprint: `cc_library {
+    name: "foo",
+    arch: {
+        arm64: { suffix: "-64" },
+        arm:   { suffix: "-32" },
+		},
+    srcs: ["foo.c"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+				"suffix": `select({
+        "//build/bazel/platforms/arch:arm": "-32",
+        "//build/bazel/platforms/arch:arm64": "-64",
+        "//conditions:default": None,
+    })`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithAidlSrcs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with aidl srcs",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+filegroup {
+    name: "A_aidl",
+    srcs: ["aidl/A.aidl"],
+	path: "aidl",
+}
+cc_library {
+	name: "foo",
+	srcs: [
+		":A_aidl",
+		"B.aidl",
+	],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{
+				"srcs":                `["aidl/A.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+			}),
+			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+				"srcs": `["B.aidl"]`,
+			}),
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps": `[
+        ":A_aidl",
+        ":foo_aidl_library",
+    ]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+			}),
+			// TODO(b/239311679) Add implementation_whole_archive_deps to cc_library_shared
+			// for bp2build to be fully correct. This fallback is affecting proto as well.
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithNonAdjacentAidlFilegroup(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with non aidl filegroup",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"path/to/A/Android.bp": `
+filegroup {
+    name: "A_aidl",
+    srcs: ["aidl/A.aidl"],
+    path: "aidl",
+}`,
+		},
+		Blueprint: `
+cc_library {
+    name: "foo",
+    srcs: [
+        ":A_aidl",
+    ],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps": `["//path/to/A:A_aidl"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+			}),
+			// TODO(b/239311679) Add implementation_whole_archive_deps to cc_library_shared
+			// for bp2build to be fully correct. This fallback is affecting proto as well.
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithExportAidlHeaders(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with export aidl headers",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+    name: "foo",
+    srcs: [
+        "Foo.aidl",
+    ],
+    aidl: {
+        export_aidl_headers: true,
+    }
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+				"srcs": `["Foo.aidl"]`,
+			}),
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps": `[":foo_aidl_library"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":     `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":     `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index bb59c63..5fa7cac 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -106,7 +106,7 @@
     // TODO: Also support export_header_lib_headers
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"export_includes": `[
         "dir-1",
         "dir-2",
@@ -183,7 +183,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"deps": `[":base-lib"] + select({
         "//build/bazel/platforms/os:android": [":android-lib"],
         "//build/bazel/platforms/os:darwin": [":darwin-lib"],
@@ -223,7 +223,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"deps": `select({
         "//build/bazel/platforms/os:android": [":exported-lib"],
         "//conditions:default": [],
@@ -276,7 +276,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"export_system_includes": `["shared_include_dir"] + select({
         "//build/bazel/platforms/arch:arm": ["arm_include_dir"],
         "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"],
@@ -318,7 +318,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "lib-1", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "lib-1", AttrNameToString{
 				"export_includes": `["lib-1"]`,
 			}),
 		},
@@ -340,7 +340,7 @@
 }
 ` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"deps": `[":foo_export"]`,
 			}),
 		},
@@ -362,7 +362,7 @@
 }
 ` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"deps": `[":foo_export"]`,
 			}),
 		},
@@ -384,7 +384,28 @@
 }
 ` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHeadersWholeStaticLibsReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_headers whole_static_libs is reexported",
+		ModuleTypeUnderTest:        "cc_library_headers",
+		ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		whole_static_libs: ["foo_export"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
 				"deps": `[":foo_export"]`,
 			}),
 		},
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 6a47862..4d8e59b 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -142,7 +142,7 @@
     // TODO: Also support export_header_lib_headers
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"absolute_includes": `[
         "include_dir_1",
         "include_dir_2",
@@ -202,7 +202,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"implementation_dynamic_deps": `select({
         "//build/bazel/platforms/arch:arm64": [":shared_dep"],
         "//conditions:default": [],
@@ -231,7 +231,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"implementation_dynamic_deps": `select({
         "//build/bazel/platforms/os:android": [":shared_dep"],
         "//conditions:default": [],
@@ -266,7 +266,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"implementation_dynamic_deps": `[":shared_dep"] + select({
         "//build/bazel/platforms/arch:arm64": [":shared_dep3"],
         "//conditions:default": [],
@@ -295,7 +295,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"srcs_c": `[
         "common.c",
         "foo-a.c",
@@ -322,7 +322,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"strip": `{
         "all": True,
         "keep_symbols": False,
@@ -351,7 +351,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"additional_linker_inputs": `["version_script"]`,
 				"linkopts":                 `["-Wl,--version-script,$(location version_script)"]`,
 			}),
@@ -374,7 +374,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"link_crt": `False`,
 				"srcs":     `["impl.cpp"]`,
 			}),
@@ -397,7 +397,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
 				"srcs": `["impl.cpp"]`,
 			}),
 		},
@@ -440,11 +440,11 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
 			}),
@@ -460,7 +460,7 @@
         include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"use_version_lib": "True",
 			}),
 		},
@@ -484,13 +484,8 @@
 `,
 		},
 		Blueprint: soongCcLibraryPreamble,
-		ExpectedBazelTargets: []string{makeBazelTarget("cc_library_shared", "a", AttrNameToString{
-			"stubs_symbol_file": `"a.map.txt"`,
-			"stubs_versions": `[
-        "28",
-        "29",
-        "current",
-    ]`,
+		ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+			"has_stubs": `True`,
 		}),
 		},
 	},
@@ -515,7 +510,7 @@
     defaults: ["empty_defaults"],
 }
 `,
-		ExpectedBazelTargets: []string{makeBazelTarget("cc_library_shared", "empty", AttrNameToString{
+		ExpectedBazelTargets: []string{MakeBazelTarget("cc_library_shared", "empty", AttrNameToString{
 			"system_dynamic_deps": "[]",
 		})},
 	})
@@ -542,21 +537,21 @@
 	bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{
+			MakeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{
 				"srcs": `[
         "foo1.l",
         "foo2.l",
     ]`,
 				"lexopts": `["--foo_flags"]`,
 			}),
-			makeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{
+			MakeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{
 				"srcs": `[
         "bar1.ll",
         "bar2.ll",
     ]`,
 				"lexopts": `["--foo_flags"]`,
 			}),
-			makeBazelTarget("cc_library_shared", "foo_lib", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo_lib", AttrNameToString{
 				"srcs": `[
         "bar.cc",
         ":foo_lib_genlex_ll",
@@ -581,7 +576,7 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"conlyflags": `["-a"]`,
 				"copts":      `["-b"]`,
 				"cppflags":   `["-c"]`,
@@ -605,7 +600,7 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"additional_linker_inputs": `["version_script"]`,
 				"conlyflags": `[
         "-include",
@@ -624,3 +619,98 @@
 		},
 	})
 }
+
+func TestCCLibrarySharedRuntimeDeps(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Blueprint: `cc_library_shared {
+	name: "bar",
+}
+
+cc_library_shared {
+  name: "foo",
+  runtime_libs: ["foo"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"runtime_deps":   `[":foo"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedEmptySuffix(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared with empty suffix",
+		Filesystem: map[string]string{
+			"foo.c": "",
+		},
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    suffix: "",
+    srcs: ["foo.c"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+				"suffix": `""`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedSuffix(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared with suffix",
+		Filesystem: map[string]string{
+			"foo.c": "",
+		},
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    suffix: "-suf",
+    srcs: ["foo.c"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+				"suffix": `"-suf"`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedArchVariantSuffix(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared with arch-variant suffix",
+		Filesystem: map[string]string{
+			"foo.c": "",
+		},
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    arch: {
+        arm64: { suffix: "-64" },
+        arm:   { suffix: "-32" },
+		},
+    srcs: ["foo.c"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+				"srcs_c": `["foo.c"]`,
+				"suffix": `select({
+        "//build/bazel/platforms/arch:arm": "-32",
+        "//build/bazel/platforms/arch:arm64": "-64",
+        "//conditions:default": None,
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index ddb7bf9..316fa3e 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -15,12 +15,12 @@
 package bp2build
 
 import (
+	"fmt"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/genrule"
-	"fmt"
-
-	"testing"
 )
 
 const (
@@ -173,7 +173,7 @@
     // TODO: Also support export_header_lib_headers
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"absolute_includes": `[
         "include_dir_1",
         "include_dir_2",
@@ -238,7 +238,7 @@
     ],
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"absolute_includes": `["subpackage"]`,
 				"local_includes":    `["."]`,
 			}),
@@ -262,7 +262,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"export_includes": `["subpackage"]`,
 			}),
 		},
@@ -285,7 +285,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"export_system_includes": `["subpackage"]`,
 			}),
 		},
@@ -320,7 +320,7 @@
 		},
 		Blueprint: soongCcLibraryStaticPreamble,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"absolute_includes": `[
         "subpackage/subsubpackage",
         "subpackage2",
@@ -352,7 +352,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"absolute_includes": `["subpackage"]`,
 				"local_includes":    `["subpackage2"]`,
 			}),
@@ -379,7 +379,7 @@
     include_build_directory: true,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"absolute_includes": `["subpackage"]`,
 				"local_includes": `[
         "subpackage2",
@@ -409,7 +409,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"implementation_deps": `select({
         "//build/bazel/platforms/arch:arm64": [":static_dep"],
         "//conditions:default": [],
@@ -442,7 +442,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"implementation_deps": `select({
         "//build/bazel/platforms/os:android": [":static_dep"],
         "//conditions:default": [],
@@ -486,7 +486,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"implementation_deps": `[":static_dep"] + select({
         "//build/bazel/platforms/arch:arm64": [":static_dep4"],
         "//conditions:default": [],
@@ -516,7 +516,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `[
         "common.c",
         "foo-a.c",
@@ -541,7 +541,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["foo-arm.c"],
         "//conditions:default": [],
@@ -571,7 +571,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-arm.c"],
         "//conditions:default": ["not-for-arm.c"],
@@ -603,7 +603,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "not-for-x86.c",
@@ -652,7 +652,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "not-for-arm64.c",
@@ -709,7 +709,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs": `["common.cc"] + select({
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": ["foo-no-arm.cc"],
@@ -740,7 +740,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs": `["common.cc"] + select({
         "//build/bazel/platforms/arch:arm": [],
         "//build/bazel/platforms/arch:x86": [
@@ -769,7 +769,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"implementation_deps": `[":static_dep"]`,
 			}),
 		},
@@ -794,7 +794,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-lib32.c"],
         "//build/bazel/platforms/arch:x86": ["for-lib32.c"],
@@ -826,7 +826,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "not-for-lib64.c",
@@ -891,7 +891,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "not-for-arm64.c",
@@ -962,7 +962,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"export_includes": `["."]`,
 				"local_includes":  `["."]`,
 				"hdrs":            `[":export_generated_hdr"]`,
@@ -1030,7 +1030,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs": `[
         "common.cpp",
         ":generated_src",
@@ -1093,7 +1093,7 @@
     include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"srcs_c": `select({
         "//build/bazel/platforms/os:android": ["android_src.c"],
         "//conditions:default": [],
@@ -1132,7 +1132,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"copts": `select({
         "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
         "//conditions:default": [],
@@ -1192,7 +1192,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"copts": `select({
         "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
         "//conditions:default": [],
@@ -1231,7 +1231,7 @@
     include_build_directory: false,
 } `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"asflags": `select({
         "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
@@ -1253,7 +1253,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "root_empty", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "root_empty", AttrNameToString{
 				"system_dynamic_deps": `[]`,
 			}),
 		},
@@ -1277,7 +1277,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "static_empty", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "static_empty", AttrNameToString{
 				"system_dynamic_deps": `[]`,
 			}),
 		},
@@ -1299,7 +1299,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{
 				"system_dynamic_deps": `[]`,
 			}),
 		},
@@ -1325,7 +1325,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{
 				"system_dynamic_deps": `[]`,
 			}),
 		},
@@ -1348,7 +1348,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "target_bionic", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "target_bionic", AttrNameToString{
 				"system_dynamic_deps": `select({
         "//build/bazel/platforms/os:android": [":libc"],
         "//build/bazel/platforms/os:linux_bionic": [":libc"],
@@ -1377,7 +1377,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{
 				"system_dynamic_deps": `[":libc"] + select({
         "//build/bazel/platforms/os:linux_bionic": [":libm"],
         "//conditions:default": [],
@@ -1392,6 +1392,16 @@
 		Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble +
 			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+
+cc_library {
+    name: "libm",
+    stubs: {
+        symbol_file: "libm.map.txt",
+        versions: ["current"],
+    },
+    bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "used_in_bionic_oses",
     target: {
@@ -1414,23 +1424,38 @@
 cc_library_static {
     name: "keep_for_empty_system_shared_libs",
     shared_libs: ["libc"],
-		system_shared_libs: [],
+    system_shared_libs: [],
+    include_build_directory: false,
+}
+
+cc_library_static {
+    name: "used_with_stubs",
+    shared_libs: ["libm"],
+    include_build_directory: false,
+}
+
+cc_library_static {
+    name: "keep_with_stubs",
+    shared_libs: ["libm"],
+    system_shared_libs: [],
     include_build_directory: false,
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "all", AttrNameToString{
-				"implementation_dynamic_deps": `select({
-        "//build/bazel/platforms/os:android": [],
-        "//build/bazel/platforms/os:linux_bionic": [],
-        "//conditions:default": [":libc"],
-    })`,
-			}),
-			makeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "all", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", AttrNameToString{
 				"implementation_dynamic_deps": `[":libc"]`,
 				"system_dynamic_deps":         `[]`,
 			}),
-			makeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "keep_with_stubs", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": [":libm_stub_libs_current"],
+        "//conditions:default": [":libm"],
+    })`,
+				"system_dynamic_deps": `[]`,
+			}),
+			MakeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "used_with_stubs", AttrNameToString{}),
 		},
 	})
 }
@@ -1446,11 +1471,11 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
 				"srcs": `["foo.proto"]`,
-			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
 				"deps": `[":foo_proto"]`,
-			}), makeBazelTarget("cc_library_static", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"deps":               `[":libprotobuf-cpp-lite"]`,
 				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
 			}),
@@ -1466,7 +1491,7 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"use_version_lib": "True",
 			}),
 		},
@@ -1483,7 +1508,7 @@
 	include_build_directory: false,
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_library_static", "foo", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"conlyflags": `["-std=conly"]`,
 				"cppflags":   `["-std=cpp"]`,
 			}),
@@ -1553,9 +1578,31 @@
 	%s
 }`, tc.prop),
 				ExpectedBazelTargets: []string{
-					makeBazelTarget("cc_library_static", "foo", tc.attr),
+					MakeBazelTarget("cc_library_static", "foo", tc.attr),
 				},
 			})
 		})
 	}
 }
+
+func TestCCLibraryStaticRuntimeDeps(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Blueprint: `cc_library_shared {
+	name: "bar",
+}
+
+cc_library_static {
+  name: "foo",
+  runtime_libs: ["foo"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "bar", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"runtime_deps":   `[":foo"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 37d5580..b8dc690 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -60,7 +60,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts": `[
         "-fno-addrsig",
         "-Wno-gcc-compat",
@@ -106,7 +106,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts": `[
         "-Werror",
         "-fno-addrsig",
@@ -141,11 +141,11 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "bar", AttrNameToString{
+			MakeBazelTarget("cc_object", "bar", AttrNameToString{
 				"copts":               `["-fno-addrsig"]`,
 				"srcs":                `["x/y/z.c"]`,
 				"system_dynamic_deps": `[]`,
-			}), makeBazelTarget("cc_object", "foo", AttrNameToString{
+			}), MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts":               `["-fno-addrsig"]`,
 				"deps":                `[":bar"]`,
 				"srcs":                `["a/b/c.c"]`,
@@ -170,7 +170,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts":               `["-fno-addrsig"]`,
 				"srcs":                `["a/b/c.c"]`,
 				"system_dynamic_deps": `[]`,
@@ -195,7 +195,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"asflags": `select({
         "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
@@ -227,7 +227,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts": `["-fno-addrsig"] + select({
         "//build/bazel/platforms/arch:x86": ["-fPIC"],
         "//conditions:default": [],
@@ -271,7 +271,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts": `["-fno-addrsig"] + select({
         "//build/bazel/platforms/arch:arm": ["-Wall"],
         "//build/bazel/platforms/arch:arm64": ["-Wall"],
@@ -303,7 +303,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts":         `["-fno-addrsig"]`,
 				"linker_script": `"bunny.lds"`,
 				"srcs":          `["base.cpp"]`,
@@ -360,7 +360,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts": `["-fno-addrsig"]`,
 				"deps": `select({
         "//build/bazel/platforms/arch:arm": [":arm_obj"],
@@ -401,7 +401,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("cc_object", "foo", AttrNameToString{
+			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"copts": `["-fno-addrsig"]`,
 				"srcs": `["base.cpp"] + select({
         "//build/bazel/platforms/os_arch:android_arm64": [
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
index 74ad005..dd217c3 100644
--- a/bp2build/cc_prebuilt_library_conversion_test.go
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
@@ -21,7 +21,7 @@
 )
 
 func TestPrebuiltLibraryStaticAndSharedSimple(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library static and shared simple",
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
@@ -36,10 +36,10 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
 					"static_library": `"libf.so"`,
 				}),
-				makeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libf.so"`,
 				}),
 			},
@@ -47,7 +47,7 @@
 }
 
 func TestPrebuiltLibraryWithArchVariance(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library with arch variance",
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
@@ -66,14 +66,14 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
 					"static_library": `select({
         "//build/bazel/platforms/arch:arm": "libg.so",
         "//build/bazel/platforms/arch:arm64": "libf.so",
         "//conditions:default": None,
     })`,
 				}),
-				makeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `select({
         "//build/bazel/platforms/arch:arm": "libg.so",
         "//build/bazel/platforms/arch:arm64": "libf.so",
@@ -85,7 +85,7 @@
 }
 
 func TestPrebuiltLibraryAdditionalAttrs(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library additional attributes",
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
@@ -104,13 +104,13 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
 					"static_library":         `"libf.so"`,
 					"export_includes":        `["testdir/1/"]`,
 					"export_system_includes": `["testdir/2/"]`,
 				}),
 				// TODO(b/229374533): When fixed, update this test
-				makeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libf.so"`,
 				}),
 			},
@@ -118,7 +118,7 @@
 }
 
 func TestPrebuiltLibrarySharedStanzaFails(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library with shared stanza fails because multiple sources",
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
@@ -141,7 +141,7 @@
 }
 
 func TestPrebuiltLibraryStaticStanzaFails(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library with static stanza fails because multiple sources",
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
@@ -164,7 +164,7 @@
 }
 
 func TestPrebuiltLibrarySharedAndStaticStanzas(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library with both shared and static stanzas",
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
@@ -185,10 +185,10 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
 					"static_library": `"libf.so"`,
 				}),
-				makeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libg.so"`,
 				}),
 			},
@@ -197,7 +197,7 @@
 
 // TODO(b/228623543): When this bug is fixed, enable this test
 //func TestPrebuiltLibraryOnlyShared(t *testing.T) {
-//	runBp2BuildTestCaseSimple(t,
+//	RunBp2BuildTestCaseSimple(t,
 //		bp2buildTestCase{
 //			description:                "prebuilt library shared only",
 //			moduleTypeUnderTest:        "cc_prebuilt_library",
@@ -224,7 +224,7 @@
 
 // TODO(b/228623543): When this bug is fixed, enable this test
 //func TestPrebuiltLibraryOnlyStatic(t *testing.T) {
-//	runBp2BuildTestCaseSimple(t,
+//	RunBp2BuildTestCaseSimple(t,
 //		bp2buildTestCase{
 //			description:                "prebuilt library static only",
 //			moduleTypeUnderTest:        "cc_prebuilt_library",
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
index bcf0ce2..541ce5e 100644
--- a/bp2build/cc_prebuilt_library_shared_test.go
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -8,7 +8,7 @@
 )
 
 func TestSharedPrebuiltLibrary(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library shared simple",
 			ModuleTypeUnderTest:        "cc_prebuilt_library_shared",
@@ -23,7 +23,7 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `"libf.so"`,
 				}),
 			},
@@ -31,7 +31,7 @@
 }
 
 func TestSharedPrebuiltLibraryWithArchVariance(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library shared with arch variance",
 			ModuleTypeUnderTest:        "cc_prebuilt_library_shared",
@@ -50,7 +50,7 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_shared", "libtest", AttrNameToString{
 					"shared_library": `select({
         "//build/bazel/platforms/arch:arm": "libg.so",
         "//build/bazel/platforms/arch:arm64": "libf.so",
@@ -62,7 +62,7 @@
 }
 
 func TestSharedPrebuiltLibrarySharedStanzaFails(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library shared with shared stanza fails because multiple sources",
 			ModuleTypeUnderTest:        "cc_prebuilt_library_shared",
diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go
index 7498e50..69a1b5e 100644
--- a/bp2build/cc_prebuilt_library_static_test.go
+++ b/bp2build/cc_prebuilt_library_static_test.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
@@ -21,7 +21,7 @@
 )
 
 func TestStaticPrebuiltLibrary(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library static simple",
 			ModuleTypeUnderTest:        "cc_prebuilt_library_static",
@@ -36,7 +36,7 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_static", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_static", "libtest", AttrNameToString{
 					"static_library": `"libf.so"`,
 				}),
 			},
@@ -44,7 +44,7 @@
 }
 
 func TestStaticPrebuiltLibraryWithArchVariance(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library static with arch variance",
 			ModuleTypeUnderTest:        "cc_prebuilt_library_static",
@@ -63,7 +63,7 @@
 	bazel_module: { bp2build_available: true },
 }`,
 			ExpectedBazelTargets: []string{
-				makeBazelTarget("prebuilt_library_static", "libtest", AttrNameToString{
+				MakeBazelTarget("prebuilt_library_static", "libtest", AttrNameToString{
 					"static_library": `select({
         "//build/bazel/platforms/arch:arm": "libg.so",
         "//build/bazel/platforms/arch:arm64": "libf.so",
@@ -75,7 +75,7 @@
 }
 
 func TestStaticPrebuiltLibraryStaticStanzaFails(t *testing.T) {
-	runBp2BuildTestCaseSimple(t,
+	RunBp2BuildTestCaseSimple(t,
 		Bp2buildTestCase{
 			Description:                "prebuilt library with static stanza fails because multiple sources",
 			ModuleTypeUnderTest:        "cc_prebuilt_library_static",
@@ -118,21 +118,21 @@
 	bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{
+			MakeBazelTarget("genlex", "foo_lib_genlex_l", AttrNameToString{
 				"srcs": `[
         "foo1.l",
         "foo2.l",
     ]`,
 				"lexopts": `["--foo_flags"]`,
 			}),
-			makeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{
+			MakeBazelTarget("genlex", "foo_lib_genlex_ll", AttrNameToString{
 				"srcs": `[
         "bar1.ll",
         "bar2.ll",
     ]`,
 				"lexopts": `["--foo_flags"]`,
 			}),
-			makeBazelTarget("cc_library_static", "foo_lib", AttrNameToString{
+			MakeBazelTarget("cc_library_static", "foo_lib", AttrNameToString{
 				"srcs": `[
         "bar.cc",
         ":foo_lib_genlex_ll",
diff --git a/bp2build/cc_test_conversion_test.go b/bp2build/cc_test_conversion_test.go
new file mode 100644
index 0000000..9b7748f
--- /dev/null
+++ b/bp2build/cc_test_conversion_test.go
@@ -0,0 +1,150 @@
+// 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/android"
+	"android/soong/cc"
+	"android/soong/genrule"
+)
+
+type ccTestBp2buildTestCase struct {
+	description string
+	blueprint   string
+	targets     []testBazelTarget
+}
+
+func registerCcTestModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
+	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+	ctx.RegisterModuleType("cc_test_library", cc.TestLibraryFactory)
+	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+}
+
+func runCcTestTestCase(t *testing.T, testCase ccTestBp2buildTestCase) {
+	t.Helper()
+	moduleTypeUnderTest := "cc_test"
+
+	description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+	t.Run(description, func(t *testing.T) {
+		t.Helper()
+		RunBp2BuildTestCase(t, registerCcTestModuleTypes, Bp2buildTestCase{
+			ExpectedBazelTargets:       generateBazelTargetsForTest(testCase.targets, android.HostAndDeviceSupported),
+			ModuleTypeUnderTest:        moduleTypeUnderTest,
+			ModuleTypeUnderTestFactory: cc.TestFactory,
+			Description:                description,
+			Blueprint:                  testCase.blueprint,
+		})
+	})
+}
+
+func TestBasicCcTest(t *testing.T) {
+	runCcTestTestCase(t, ccTestBp2buildTestCase{
+		description: "basic cc_test with commonly used attributes",
+		blueprint: `
+cc_test {
+    name: "mytest",
+    host_supported: true,
+    srcs: ["test.cpp"],
+    target: {
+        android: {
+            srcs: ["android.cpp"],
+            shared_libs: ["foolib"],
+        },
+        linux: {
+            srcs: ["linux.cpp"],
+        },
+        host: {
+            static_libs: ["hostlib"],
+        },
+    },
+    data: [":data_mod", "file.txt"],
+    data_bins: [":cc_bin"],
+    data_libs: [":cc_lib"],
+    cflags: ["-Wall"],
+}
+` + simpleModuleDoNotConvertBp2build("cc_library", "foolib") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "hostlib") +
+			simpleModuleDoNotConvertBp2build("genrule", "data_mod") +
+			simpleModuleDoNotConvertBp2build("cc_binary", "cc_bin") +
+			simpleModuleDoNotConvertBp2build("cc_test_library", "cc_lib"),
+		targets: []testBazelTarget{
+			{"cc_test", "mytest", AttrNameToString{
+				"copts": `["-Wall"]`,
+				"data": `[
+        ":data_mod",
+        "file.txt",
+        ":cc_bin",
+        ":cc_lib",
+    ]`,
+				"deps": `select({
+        "//build/bazel/platforms/os:darwin": [":hostlib"],
+        "//build/bazel/platforms/os:linux": [":hostlib"],
+        "//build/bazel/platforms/os:linux_bionic": [":hostlib"],
+        "//build/bazel/platforms/os:linux_musl": [":hostlib"],
+        "//build/bazel/platforms/os:windows": [":hostlib"],
+        "//conditions:default": [],
+    })`,
+				"gtest":          "True",
+				"isolated":       "True",
+				"local_includes": `["."]`,
+				"dynamic_deps": `select({
+        "//build/bazel/platforms/os:android": [":foolib"],
+        "//conditions:default": [],
+    })`,
+				"srcs": `["test.cpp"] + select({
+        "//build/bazel/platforms/os:android": [
+            "linux.cpp",
+            "android.cpp",
+        ],
+        "//build/bazel/platforms/os:linux": ["linux.cpp"],
+        "//build/bazel/platforms/os:linux_bionic": ["linux.cpp"],
+        "//build/bazel/platforms/os:linux_musl": ["linux.cpp"],
+        "//conditions:default": [],
+    })`,
+			},
+			},
+		},
+	})
+}
+
+func TestBasicCcTestGtestIsolatedDisabled(t *testing.T) {
+	runCcTestTestCase(t, ccTestBp2buildTestCase{
+		description: "cc test with disabled gtest and isolated props",
+		blueprint: `
+cc_test {
+    name: "mytest",
+    host_supported: true,
+    srcs: ["test.cpp"],
+    gtest: false,
+    isolated: false,
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_test", "mytest", AttrNameToString{
+				"gtest":          "False",
+				"isolated":       "False",
+				"local_includes": `["."]`,
+				"srcs":           `["test.cpp"]`,
+			},
+			},
+		},
+	})
+}
diff --git a/bp2build/cc_yasm_conversion_test.go b/bp2build/cc_yasm_conversion_test.go
index 2a71834..55d4feb 100644
--- a/bp2build/cc_yasm_conversion_test.go
+++ b/bp2build/cc_yasm_conversion_test.go
@@ -48,7 +48,7 @@
   srcs: ["main.cpp", "myfile.asm"],
 }`,
 		ExpectedBazelTargets: append([]string{
-			makeBazelTarget("yasm", "foo_yasm", map[string]string{
+			MakeBazelTarget("yasm", "foo_yasm", map[string]string{
 				"include_dirs": `["."]`,
 				"srcs":         `["myfile.asm"]`,
 			}),
@@ -81,7 +81,7 @@
   srcs: ["main.cpp", "myfile.asm"],
 }`,
 		ExpectedBazelTargets: append([]string{
-			makeBazelTarget("yasm", "foo_yasm", map[string]string{
+			MakeBazelTarget("yasm", "foo_yasm", map[string]string{
 				"include_dirs": `[
         "include1/foo",
         ".",
@@ -123,7 +123,7 @@
   },
 }`,
 		ExpectedBazelTargets: append([]string{
-			makeBazelTarget("yasm", "foo_yasm", map[string]string{
+			MakeBazelTarget("yasm", "foo_yasm", map[string]string{
 				"include_dirs": `["."]`,
 				"srcs": `select({
         "//build/bazel/platforms/arch:x86": ["myfile.asm"],
@@ -161,7 +161,7 @@
   },
 }`,
 		ExpectedBazelTargets: append([]string{
-			makeBazelTarget("yasm", "foo_yasm", map[string]string{
+			MakeBazelTarget("yasm", "foo_yasm", map[string]string{
 				"include_dirs": `["."]`,
 				"srcs": `["myfile.asm"] + select({
         "//build/bazel/platforms/arch:x86": ["mysecondfile.asm"],
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index d37a523..9398d12 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -13,6 +13,30 @@
 
 type selects map[string]reflect.Value
 
+func getStringValue(str bazel.StringAttribute) (reflect.Value, []selects) {
+	value := reflect.ValueOf(str.Value)
+
+	if !str.HasConfigurableValues() {
+		return value, []selects{}
+	}
+
+	ret := selects{}
+	for _, axis := range str.SortedConfigurationAxes() {
+		configToStrs := str.ConfigurableValues[axis]
+		for config, strs := range configToStrs {
+			selectKey := axis.SelectKey(config)
+			ret[selectKey] = reflect.ValueOf(strs)
+		}
+	}
+	// if there is a select, use the base value as the conditions default value
+	if len(ret) > 0 {
+		ret[bazel.ConditionsDefaultSelectKey] = value
+		value = reflect.Zero(value.Type())
+	}
+
+	return value, []selects{ret}
+}
+
 func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects) {
 	value := reflect.ValueOf(list.Value)
 	if !list.HasConfigurableValues() {
@@ -137,6 +161,12 @@
 	// If true, print the default attribute value, even if the attribute is zero.
 	shouldPrintDefault := false
 	switch list := v.(type) {
+	case bazel.StringAttribute:
+		if err := list.Collapse(); err != nil {
+			return "", err
+		}
+		value, configurableAttrs = getStringValue(list)
+		defaultSelectValue = &bazelNone
 	case bazel.StringListAttribute:
 		value, configurableAttrs = getStringListValues(list)
 		defaultSelectValue = &emptyBazelList
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 8cf9bea..731b17e 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -2,7 +2,6 @@
 
 import (
 	"encoding/json"
-	"fmt"
 	"reflect"
 	"strings"
 
@@ -80,21 +79,14 @@
 		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
 	}
 
-	files = append(files, createBuildFiles(cfg, buildToTargets, mode)...)
+	files = append(files, createBuildFiles(buildToTargets, mode)...)
 
 	return files
 }
 
-func createBuildFiles(cfg android.Config, buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
+func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
-	warnNotWriting := cfg.IsEnvTrue("BP2BUILD_VERBOSE")
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
-		if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
-			if warnNotWriting {
-				fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
-			}
-			continue
-		}
 		targets := buildToTargets[dir]
 		targets.sort()
 
@@ -104,17 +96,9 @@
 # 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.
 `
-			if targets.hasHandcraftedTargets() {
-				// For BUILD files with both handcrafted and generated targets,
-				// don't hardcode actual content, like package() declarations.
-				// Leave that responsibility to the checked-in BUILD file
-				// instead.
-				content += `# This file contains generated targets and handcrafted targets that are manually managed in the source tree.`
-			} else {
-				// For fully-generated BUILD files, hardcode the default visibility.
-				content += "package(default_visibility = [\"//visibility:public\"])"
-			}
-			content += "\n"
+
+			// Hardcode the default visibility.
+			content += "package(default_visibility = [\"//visibility:public\"])\n"
 			content += targets.LoadStatements()
 		} else if mode == QueryView {
 			content = soongModuleLoad
@@ -152,16 +136,17 @@
 var (
 	// Certain module property names are blocklisted/ignored here, for the reasons commented.
 	ignoredPropNames = map[string]bool{
-		"name":       true, // redundant, since this is explicitly generated for every target
-		"from":       true, // reserved keyword
-		"in":         true, // reserved keyword
-		"size":       true, // reserved for tests
-		"arch":       true, // interface prop type is not supported yet.
-		"multilib":   true, // interface prop type is not supported yet.
-		"target":     true, // interface prop type is not supported yet.
-		"visibility": true, // Bazel has native visibility semantics. Handle later.
-		"features":   true, // There is already a built-in attribute 'features' which cannot be overridden.
-		"for":        true, // reserved keyword, b/233579439
+		"name":               true, // redundant, since this is explicitly generated for every target
+		"from":               true, // reserved keyword
+		"in":                 true, // reserved keyword
+		"size":               true, // reserved for tests
+		"arch":               true, // interface prop type is not supported yet.
+		"multilib":           true, // interface prop type is not supported yet.
+		"target":             true, // interface prop type is not supported yet.
+		"visibility":         true, // Bazel has native visibility semantics. Handle later.
+		"features":           true, // There is already a built-in attribute 'features' which cannot be overridden.
+		"for":                true, // reserved keyword, b/233579439
+		"versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
 	}
 )
 
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index b598b85..de09a17 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -56,3 +56,68 @@
 		ExpectedErr: fmt.Errorf("filegroup 'foo' cannot contain a file with the same name"),
 	})
 }
+
+func TestFilegroupWithAidlSrcs(t *testing.T) {
+	testcases := []struct {
+		name               string
+		bp                 string
+		expectedBazelAttrs AttrNameToString
+	}{
+		{
+			name: "filegroup with only aidl srcs",
+			bp: `
+	filegroup {
+		name: "foo",
+		srcs: ["aidl/foo.aidl"],
+		path: "aidl",
+	}`,
+			expectedBazelAttrs: AttrNameToString{
+				"srcs":                `["aidl/foo.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+			},
+		},
+		{
+			name: "filegroup without path",
+			bp: `
+	filegroup {
+		name: "foo",
+		srcs: ["aidl/foo.aidl"],
+	}`,
+			expectedBazelAttrs: AttrNameToString{
+				"srcs": `["aidl/foo.aidl"]`,
+			},
+		},
+	}
+
+	for _, test := range testcases {
+		expectedBazelTargets := []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "foo", test.expectedBazelAttrs),
+		}
+		runFilegroupTestCase(t, Bp2buildTestCase{
+			Description:          test.name,
+			Blueprint:            test.bp,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	}
+}
+
+func TestFilegroupWithAidlAndNonAidlSrcs(t *testing.T) {
+	runFilegroupTestCase(t, Bp2buildTestCase{
+		Description: "filegroup with aidl and non-aidl srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+filegroup {
+    name: "foo",
+    srcs: [
+		"aidl/foo.aidl",
+		"buf.proto",
+	],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
+				"srcs": `[
+        "aidl/foo.aidl",
+        "buf.proto",
+    ]`}),
+		}})
+}
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index 50ea542..86f3d42 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -55,7 +55,7 @@
     java_version: "8",
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
+			MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
 				"srcs":       `["a.java"]`,
 				"main_class": `"com.android.test.MainClass"`,
 				"deps":       `["//other:jni-lib-1"]`,
@@ -91,7 +91,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
+			MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
 				"main_class":   `"com.android.test.MainClass"`,
 				"runtime_deps": `[":java-dep-1"]`,
 				"target_compatible_with": `select({
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
index 707ecce..05d7142 100644
--- a/bp2build/java_import_conversion_test.go
+++ b/bp2build/java_import_conversion_test.go
@@ -45,7 +45,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_import", "example_import", AttrNameToString{
+			MakeBazelTarget("java_import", "example_import", AttrNameToString{
 				"jars": `["import.jar"]`,
 			}),
 		}})
@@ -74,7 +74,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_import", "example_import", AttrNameToString{
+			MakeBazelTarget("java_import", "example_import", AttrNameToString{
 				"jars": `select({
         "//build/bazel/platforms/os:android": ["android.jar"],
         "//build/bazel/platforms/os:linux": ["linux.jar"],
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 7fa19d9..74e2dbd 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -51,11 +51,11 @@
     bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"srcs": `["a.java"]`,
 				"deps": `[":java-lib-2"]`,
 			}),
-			makeBazelTarget("java_library", "java-lib-2", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{
 				"srcs": `["b.java"]`,
 			}),
 		},
@@ -84,7 +84,7 @@
     bazel_module: { bp2build_available: false },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"srcs": `["a.java"]`,
 				"deps": `[
         ":java-lib-2",
@@ -110,7 +110,7 @@
     bazel_module: { bp2build_available: false },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"exports": `[":java-lib-2"]`,
 			}),
 		},
@@ -149,7 +149,7 @@
     bazel_module: { bp2build_available: false },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"plugins": `[":java-plugin-1"]`,
 			}),
 		},
@@ -166,7 +166,7 @@
     java_version: "11",
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"srcs":      `["a.java"]`,
 				"javacopts": `["-source 11 -target 11"]`,
 			}),
@@ -186,7 +186,7 @@
     },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"javacopts": `[
         "-Xsuper-fast",
         "-Xep:SpeedLimit:OFF",
@@ -208,7 +208,7 @@
     },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"javacopts": `["-Xsuper-fast"]`,
 				"srcs":      `["a.java"]`,
 			}),
@@ -228,7 +228,7 @@
     },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"javacopts": `["-Xsuper-fast"]`,
 				"srcs":      `["a.java"]`,
 			}),
@@ -252,13 +252,13 @@
         bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("event_log_tags", "example_lib_logtags", AttrNameToString{
+			MakeBazelTarget("event_log_tags", "example_lib_logtags", AttrNameToString{
 				"srcs": `[
         "a.logtag",
         "b.logtag",
     ]`,
 			}),
-			makeBazelTarget("java_library", "example_lib", AttrNameToString{
+			MakeBazelTarget("java_library", "example_lib", AttrNameToString{
 				"srcs": `[
         "a.java",
         "b.java",
@@ -280,7 +280,7 @@
 	java_resources: ["res/a.res", "res/b.res"],
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"resources": `[
         "res/a.res",
         "res/b.res",
@@ -302,7 +302,7 @@
 	java_resource_dirs: ["res"],
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"resource_strip_prefix": `"res"`,
 				"resources": `[
         "res/a.res",
@@ -326,7 +326,7 @@
 	exclude_java_resource_dirs: ["res/exclude"],
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"resource_strip_prefix": `"res"`,
 				"resources":             `["res/a.res"]`,
 			}),
@@ -347,7 +347,7 @@
 	exclude_java_resources: ["res/dir1/exclude.res"],
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"resource_strip_prefix": `"res"`,
 				"resources": `[
         "res/a.res",
@@ -372,3 +372,280 @@
 		ExpectedBazelTargets: []string{},
 	})
 }
+
+func TestJavaLibraryAidl(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "Java library - aidl creates separate dependency",
+		ModuleTypeUnderTest:        "java_library",
+		ModuleTypeUnderTestFactory: java.LibraryFactory,
+		Blueprint: `java_library {
+        name: "example_lib",
+        srcs: [
+			"a.java",
+			"b.java",
+			"a.aidl",
+			"b.aidl",
+		],
+        bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("aidl_library", "example_lib_aidl_library", AttrNameToString{
+				"srcs": `[
+        "a.aidl",
+        "b.aidl",
+    ]`,
+			}),
+			MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
+				"deps": `[":example_lib_aidl_library"]`,
+			}),
+			MakeBazelTarget("java_library", "example_lib", AttrNameToString{
+				"deps":    `[":example_lib_java_aidl_library"]`,
+				"exports": `[":example_lib_java_aidl_library"]`,
+				"srcs": `[
+        "a.java",
+        "b.java",
+    ]`,
+			}),
+		}})
+}
+
+func TestJavaLibraryAidlSrcsNoFileGroup(t *testing.T) {
+	runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+		Description:                "Java library - aidl filegroup is parsed",
+		ModuleTypeUnderTest:        "java_library",
+		ModuleTypeUnderTestFactory: java.LibraryFactory,
+		Blueprint: `
+java_library {
+        name: "example_lib",
+        srcs: [
+			"a.java",
+			"b.aidl",
+		],
+        bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("aidl_library", "example_lib_aidl_library", AttrNameToString{
+				"srcs": `["b.aidl"]`,
+			}),
+			MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
+				"deps": `[":example_lib_aidl_library"]`,
+			}),
+			MakeBazelTarget("java_library", "example_lib", AttrNameToString{
+				"deps":    `[":example_lib_java_aidl_library"]`,
+				"exports": `[":example_lib_java_aidl_library"]`,
+				"srcs":    `["a.java"]`,
+			}),
+		},
+	}, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	})
+}
+
+func TestJavaLibraryAidlFilegroup(t *testing.T) {
+	runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+		Description:                "Java library - aidl filegroup is parsed",
+		ModuleTypeUnderTest:        "java_library",
+		ModuleTypeUnderTestFactory: java.LibraryFactory,
+		Blueprint: `
+filegroup {
+	name: "random_other_files",
+	srcs: [
+		"a.java",
+		"b.java",
+	],
+}
+filegroup {
+	name: "aidl_files",
+	srcs: [
+		"a.aidl",
+		"b.aidl",
+	],
+}
+java_library {
+        name: "example_lib",
+        srcs: [
+			"a.java",
+			"b.java",
+			":aidl_files",
+			":random_other_files",
+		],
+        bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "aidl_files", AttrNameToString{
+				"srcs": `[
+        "a.aidl",
+        "b.aidl",
+    ]`,
+			}),
+			MakeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{
+				"deps": `[":aidl_files"]`,
+			}),
+			MakeBazelTarget("java_library", "example_lib", AttrNameToString{
+				"deps":    `[":example_lib_java_aidl_library"]`,
+				"exports": `[":example_lib_java_aidl_library"]`,
+				"srcs": `[
+        "a.java",
+        "b.java",
+        ":random_other_files",
+    ]`,
+			}),
+			MakeBazelTargetNoRestrictions("filegroup", "random_other_files", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.java",
+    ]`,
+			}),
+		},
+	}, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	})
+}
+
+func TestJavaLibraryAidlNonAdjacentAidlFilegroup(t *testing.T) {
+	runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+		Description:                "java_library with non adjacent aidl filegroup",
+		ModuleTypeUnderTest:        "java_library",
+		ModuleTypeUnderTestFactory: java.LibraryFactory,
+		Filesystem: map[string]string{
+			"path/to/A/Android.bp": `
+filegroup {
+	name: "A_aidl",
+	srcs: ["aidl/A.aidl"],
+	path: "aidl",
+}`,
+		},
+		Blueprint: `
+java_library {
+	name: "foo",
+	srcs: [
+		":A_aidl",
+	],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_aidl_library", "foo_java_aidl_library", AttrNameToString{
+				"deps": `["//path/to/A:A_aidl"]`,
+			}),
+			MakeBazelTarget("java_library", "foo", AttrNameToString{
+				"exports": `[":foo_java_aidl_library"]`,
+			}),
+		},
+	}, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	})
+}
+
+func TestConvertArmNeonVariant(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library - simple arch feature",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+	name: "TestLib",
+	manifest: "manifest/AndroidManifest.xml",
+	srcs: ["lib.java"],
+	arch: {
+		arm: {
+			neon: {
+				srcs: ["arm_neon.java"],
+			},
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `["lib.java"] + select({
+        "//build/bazel/platforms/arch/variants:arm-neon": ["arm_neon.java"],
+        "//conditions:default": [],
+    })`,
+					"manifest":       `"manifest/AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+		}})
+}
+
+func TestConvertMultipleArchFeatures(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library - multiple arch features",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+	name: "TestLib",
+	manifest: "manifest/AndroidManifest.xml",
+	srcs: ["lib.java"],
+	arch: {
+		x86: {
+			ssse3: {
+				srcs: ["ssse3.java"],
+			},
+			sse4_1: {
+				srcs: ["sse4_1.java"],
+			},
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `["lib.java"] + select({
+        "//build/bazel/platforms/arch/variants:x86-sse4_1": ["sse4_1.java"],
+        "//build/bazel/platforms/arch/variants:x86-sse4_1-ssse3": [
+            "sse4_1.java",
+            "ssse3.java",
+        ],
+        "//build/bazel/platforms/arch/variants:x86-ssse3": ["ssse3.java"],
+        "//conditions:default": [],
+    })`,
+					"manifest":       `"manifest/AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+		}})
+}
+
+func TestConvertExcludeSrcsArchFeature(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library - exclude_srcs with arch feature",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+	name: "TestLib",
+	manifest: "manifest/AndroidManifest.xml",
+	srcs: ["lib.java"],
+	arch: {
+		arm: {
+			srcs: ["arm_non_neon.java"],
+			neon: {
+				exclude_srcs: ["arm_non_neon.java"],
+			},
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `["lib.java"] + select({
+        "//build/bazel/platforms/arch/variants:arm-neon": [],
+        "//build/bazel/platforms/arch:arm": ["arm_non_neon.java"],
+        "//conditions:default": [],
+    })`,
+					"manifest":       `"manifest/AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+		}})
+}
diff --git a/bp2build/java_library_host_conversion_test.go b/bp2build/java_library_host_conversion_test.go
index 1dcf163..edd8690 100644
--- a/bp2build/java_library_host_conversion_test.go
+++ b/bp2build/java_library_host_conversion_test.go
@@ -46,7 +46,7 @@
     java_version: "9",
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_library", "java-lib-host-1", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-host-1", AttrNameToString{
 				"srcs": `["a.java"]`,
 				"deps": `[":java-lib-host-2"]`,
 				"target_compatible_with": `select({
@@ -54,7 +54,7 @@
         "//conditions:default": [],
     })`,
 			}),
-			makeBazelTarget("java_library", "java-lib-host-2", AttrNameToString{
+			MakeBazelTarget("java_library", "java-lib-host-2", AttrNameToString{
 				"javacopts": `["-source 1.9 -target 1.9"]`,
 				"srcs":      `["c.java"]`,
 				"target_compatible_with": `select({
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
index 2d2e5ff..d9049d4 100644
--- a/bp2build/java_plugin_conversion_test.go
+++ b/bp2build/java_plugin_conversion_test.go
@@ -54,7 +54,7 @@
     bazel_module: { bp2build_available: false },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_plugin", "java-plug-1", AttrNameToString{
+			MakeBazelTarget("java_plugin", "java-plug-1", AttrNameToString{
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
@@ -95,7 +95,7 @@
     bazel_module: { bp2build_available: false },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("java_plugin", "java-plug-1", AttrNameToString{
+			MakeBazelTarget("java_plugin", "java-plug-1", AttrNameToString{
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
index 6465641..df0df2f 100644
--- a/bp2build/java_proto_conversion_test.go
+++ b/bp2build/java_proto_conversion_test.go
@@ -70,7 +70,7 @@
     srcs: ["a.proto"],
 }`
 
-	protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", AttrNameToString{
+	protoLibrary := MakeBazelTarget("proto_library", "java-protos_proto", AttrNameToString{
 		"srcs": `["a.proto"]`,
 	})
 
@@ -82,13 +82,13 @@
 			Blueprint:   fmt.Sprintf(bp, tc.protoType),
 			ExpectedBazelTargets: []string{
 				protoLibrary,
-				makeBazelTarget(
+				MakeBazelTarget(
 					tc.javaLibraryType,
 					javaLibraryName,
 					AttrNameToString{
 						"deps": `[":java-protos_proto"]`,
 					}),
-				makeBazelTarget("java_library", "java-protos", AttrNameToString{
+				MakeBazelTarget("java_library", "java-protos", AttrNameToString{
 					"exports": fmt.Sprintf(`[":%s"]`, javaLibraryName),
 				}),
 			},
@@ -106,16 +106,16 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("proto_library", "java-protos_proto", AttrNameToString{
+			MakeBazelTarget("proto_library", "java-protos_proto", AttrNameToString{
 				"srcs": `["a.proto"]`,
 			}),
-			makeBazelTarget(
+			MakeBazelTarget(
 				"java_lite_proto_library",
 				"java-protos_java_proto_lite",
 				AttrNameToString{
 					"deps": `[":java-protos_proto"]`,
 				}),
-			makeBazelTarget("java_library", "java-protos", AttrNameToString{
+			MakeBazelTarget("java_library", "java-protos", AttrNameToString{
 				"exports":   `[":java-protos_java_proto_lite"]`,
 				"javacopts": `["-source 1.7 -target 1.7"]`,
 			}),
diff --git a/bp2build/linker_config_conversion_test.go b/bp2build/linker_config_conversion_test.go
index 3b8a363..5e7bcd4 100644
--- a/bp2build/linker_config_conversion_test.go
+++ b/bp2build/linker_config_conversion_test.go
@@ -25,7 +25,7 @@
 	t.Helper()
 	(&tc).ModuleTypeUnderTest = "linker_config"
 	(&tc).ModuleTypeUnderTestFactory = linkerconfig.LinkerConfigFactory
-	runBp2BuildTestCaseSimple(t, tc)
+	RunBp2BuildTestCaseSimple(t, tc)
 }
 
 func TestLinkerConfigConvertsSrc(t *testing.T) {
@@ -37,7 +37,7 @@
 	src: "a.json",
 }
 `,
-			ExpectedBazelTargets: []string{makeBazelTarget("linker_config", "foo", AttrNameToString{
+			ExpectedBazelTargets: []string{MakeBazelTarget("linker_config", "foo", AttrNameToString{
 				"src": `"a.json"`,
 			})},
 		})
diff --git a/bp2build/ndk_headers_conversion_test.go b/bp2build/ndk_headers_conversion_test.go
new file mode 100644
index 0000000..c7cc6b2
--- /dev/null
+++ b/bp2build/ndk_headers_conversion_test.go
@@ -0,0 +1,164 @@
+// 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 TestNdkHeaderFilepaths(t *testing.T) {
+	bpTemplate := `
+	ndk_headers {
+		name: "foo",
+		srcs: %v,
+		exclude_srcs: %v,
+	}
+	`
+	testCases := []struct {
+		desc         string
+		srcs         string
+		excludeSrcs  string
+		expectedHdrs string
+	}{
+		{
+			desc:         "Single header file",
+			srcs:         `["foo.h"]`,
+			excludeSrcs:  `[]`,
+			expectedHdrs: `["foo.h"]`,
+		},
+		{
+			desc:        "Multiple header files",
+			srcs:        `["foo.h", "foo_other.h"]`,
+			excludeSrcs: `[]`,
+			expectedHdrs: `[
+        "foo.h",
+        "foo_other.h",
+    ]`,
+		},
+		{
+			desc:         "Multiple header files with excludes",
+			srcs:         `["foo.h", "foo_other.h"]`,
+			excludeSrcs:  `["foo_other.h"]`,
+			expectedHdrs: `["foo.h"]`,
+		},
+		{
+			desc:        "Multiple header files via Soong-supported globs",
+			srcs:        `["*.h"]`,
+			excludeSrcs: `[]`,
+			expectedHdrs: `[
+        "foo.h",
+        "foo_other.h",
+    ]`,
+		},
+	}
+	for _, testCase := range testCases {
+		fs := map[string]string{
+			"foo.h":       "",
+			"foo_other.h": "",
+		}
+		expectedApiContributionTargetName := "foo.contribution"
+		expectedBazelTarget := MakeBazelTargetNoRestrictions(
+			"cc_api_headers",
+			expectedApiContributionTargetName,
+			AttrNameToString{
+				"hdrs": testCase.expectedHdrs,
+			},
+		)
+		RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+			Description:          testCase.desc,
+			Blueprint:            fmt.Sprintf(bpTemplate, testCase.srcs, testCase.excludeSrcs),
+			ExpectedBazelTargets: []string{expectedBazelTarget},
+			Filesystem:           fs,
+		})
+	}
+}
+
+func TestNdkHeaderIncludeDir(t *testing.T) {
+	bpTemplate := `
+	ndk_headers {
+		name: "foo",
+		from: %v,
+		to: "this/value/is/ignored",
+	}
+	`
+	testCases := []struct {
+		desc               string
+		from               string
+		expectedIncludeDir string
+	}{
+		{
+			desc:               "Empty `from` value",
+			from:               `""`,
+			expectedIncludeDir: `""`,
+		},
+		{
+			desc:               "Non-Empty `from` value",
+			from:               `"include"`,
+			expectedIncludeDir: `"include"`,
+		},
+	}
+	for _, testCase := range testCases {
+		expectedApiContributionTargetName := "foo.contribution"
+		expectedBazelTarget := MakeBazelTargetNoRestrictions(
+			"cc_api_headers",
+			expectedApiContributionTargetName,
+			AttrNameToString{
+				"include_dir": testCase.expectedIncludeDir,
+			},
+		)
+		RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+			Description:          testCase.desc,
+			Blueprint:            fmt.Sprintf(bpTemplate, testCase.from),
+			ExpectedBazelTargets: []string{expectedBazelTarget},
+		})
+	}
+}
+
+func TestVersionedNdkHeaderFilepaths(t *testing.T) {
+	bp := `
+	versioned_ndk_headers {
+		name: "common_libc",
+		from: "include"
+	}
+	`
+	fs := map[string]string{
+		"include/math.h":    "",
+		"include/stdio.h":   "",
+		"include/arm/arm.h": "",
+		"include/x86/x86.h": "",
+	}
+	expectedApiContributionTargetName := "common_libc.contribution"
+	expectedBazelTarget := MakeBazelTargetNoRestrictions(
+		"cc_api_headers",
+		expectedApiContributionTargetName,
+		AttrNameToString{
+			"include_dir": `"include"`,
+			"hdrs": `[
+        "include/math.h",
+        "include/stdio.h",
+        "include/arm/arm.h",
+        "include/x86/x86.h",
+    ]`,
+		},
+	)
+	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		Blueprint:            bp,
+		Filesystem:           fs,
+		ExpectedBazelTargets: []string{expectedBazelTarget},
+	})
+}
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index fce4c74..aa0a5b7 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -15,10 +15,11 @@
 package bp2build
 
 import (
+	"fmt"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/etc"
-
-	"testing"
 )
 
 func runPrebuiltEtcTestCase(t *testing.T, tc Bp2buildTestCase) {
@@ -45,7 +46,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
 				"filename":    `"tz_version"`,
 				"installable": `False`,
 				"src":         `"version/tz_version"`,
@@ -75,7 +76,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
 				"filename":    `"tz_version"`,
 				"installable": `False`,
 				"src": `select({
@@ -114,7 +115,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
 				"filename":    `"tz_version"`,
 				"installable": `False`,
 				"src": `select({
@@ -128,6 +129,32 @@
 				"dir": `"etc/tz"`,
 			})}})
 }
+func TestPrebuiltEtcProductVariables(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt etc - product variables",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "apex_tz_version",
+    src: "version/tz_version",
+    filename: "tz_version",
+    product_variables: {
+      native_coverage: {
+        src: "src1",
+      },
+    },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+				"filename": `"tz_version"`,
+				"src": `select({
+        "//build/bazel/product_variables:native_coverage": "src1",
+        "//conditions:default": "version/tz_version",
+    })`,
+				"dir": `"etc"`,
+			})}})
+}
 
 func runPrebuiltUsrShareTestCase(t *testing.T, tc Bp2buildTestCase) {
 	t.Helper()
@@ -153,7 +180,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
 				"filename":    `"tz_version"`,
 				"installable": `False`,
 				"src":         `"version/tz_version"`,
@@ -174,10 +201,148 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
 				"filename":    `"tz_version"`,
 				"installable": `False`,
 				"src":         `"version/tz_version"`,
 				"dir":         `"etc"`,
 			})}})
 }
+
+func TestFilenameAsProperty(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - filename is specified as a property ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    src: "fooSrc",
+    filename: "fooFileName",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"fooFileName"`,
+				"src":      `"fooSrc"`,
+				"dir":      `"etc"`,
+			})}})
+}
+
+func TestFileNameFromSrc(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - filename_from_src is true  ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename_from_src: true,
+    src: "fooSrc",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"fooSrc"`,
+				"src":      `"fooSrc"`,
+				"dir":      `"etc"`,
+			})}})
+}
+
+func TestFileNameFromSrcMultipleSrcs(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - filename_from_src is true but there are multiple srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename_from_src: true,
+		arch: {
+        arm: {
+            src: "barSrc",
+        },
+        arm64: {
+            src: "bazSrc",
+        },
+	  }
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename_from_src": `True`,
+				"dir":               `"etc"`,
+				"src": `select({
+        "//build/bazel/platforms/arch:arm": "barSrc",
+        "//build/bazel/platforms/arch:arm64": "bazSrc",
+        "//conditions:default": None,
+    })`,
+			})}})
+}
+
+func TestFilenameFromModuleName(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - neither filename nor filename_from_src are specified ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"foo"`,
+				"dir":      `"etc"`,
+			})}})
+}
+
+func TestPrebuiltEtcProductVariableArchSrcs(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt etc- SRcs from arch variant product variables",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename: "fooFilename",
+    arch: {
+      arm: {
+        src: "armSrc",
+        product_variables: {
+          native_coverage: {
+            src: "nativeCoverageArmSrc",
+          },
+        },
+      },
+    },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"fooFilename"`,
+				"dir":      `"etc"`,
+				"src": `select({
+        "//build/bazel/platforms/arch:arm": "armSrc",
+        "//build/bazel/product_variables:native_coverage-arm": "nativeCoverageArmSrc",
+        "//conditions:default": None,
+    })`,
+			})}})
+}
+
+func TestPrebuiltEtcProductVariableError(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename: "fooFilename",
+    arch: {
+      arm: {
+        src: "armSrc",
+      },
+    },
+    product_variables: {
+      native_coverage: {
+        src: "nativeCoverageArmSrc",
+      },
+    },
+}`,
+		ExpectedErr: fmt.Errorf("label attribute could not be collapsed"),
+	})
+}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 79da5d5..e8b9bc4 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -42,7 +42,7 @@
       bazel_module: { bp2build_available: false },
     }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("py_binary", "foo", AttrNameToString{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
 				"data":    `["files/data.txt"]`,
 				"deps":    `[":bar"]`,
 				"main":    `"a.py"`,
@@ -62,7 +62,7 @@
 }
 
 func TestPythonBinaryHostPy2(t *testing.T) {
-	runBp2BuildTestCaseSimple(t, Bp2buildTestCase{
+	RunBp2BuildTestCaseSimple(t, Bp2buildTestCase{
 		Description:                "py2 python_binary_host",
 		ModuleTypeUnderTest:        "python_binary_host",
 		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
@@ -82,7 +82,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("py_binary", "foo", AttrNameToString{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
 				"python_version": `"PY2"`,
 				"imports":        `["."]`,
 				"srcs":           `["a.py"]`,
@@ -96,7 +96,7 @@
 }
 
 func TestPythonBinaryHostPy3(t *testing.T) {
-	runBp2BuildTestCaseSimple(t, Bp2buildTestCase{
+	RunBp2BuildTestCaseSimple(t, Bp2buildTestCase{
 		Description:                "py3 python_binary_host",
 		ModuleTypeUnderTest:        "python_binary_host",
 		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
@@ -117,7 +117,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			// python_version is PY3 by default.
-			makeBazelTarget("py_binary", "foo", AttrNameToString{
+			MakeBazelTarget("py_binary", "foo", AttrNameToString{
 				"imports": `["."]`,
 				"srcs":    `["a.py"]`,
 				"target_compatible_with": `select({
@@ -130,7 +130,7 @@
 }
 
 func TestPythonBinaryHostArchVariance(t *testing.T) {
-	runBp2BuildTestCaseSimple(t, Bp2buildTestCase{
+	RunBp2BuildTestCaseSimple(t, Bp2buildTestCase{
 		Description:                "test arch variants",
 		ModuleTypeUnderTest:        "python_binary_host",
 		ModuleTypeUnderTestFactory: python.PythonBinaryHostFactory,
@@ -150,7 +150,7 @@
 					},
 				 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("py_binary", "foo-arm", AttrNameToString{
+			MakeBazelTarget("py_binary", "foo-arm", AttrNameToString{
 				"imports": `["."]`,
 				"srcs": `select({
         "//build/bazel/platforms/arch:arm": ["arm.py"],
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
index 1d6061b..a53371d 100644
--- a/bp2build/python_library_conversion_test.go
+++ b/bp2build/python_library_conversion_test.go
@@ -35,7 +35,7 @@
 func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) Bp2buildTestCase {
 	var bp2BuildTargets []string
 	for _, t := range tc.expectedBazelTargets {
-		bp2BuildTargets = append(bp2BuildTargets, makeBazelTarget(t.typ, t.name, t.attrs))
+		bp2BuildTargets = append(bp2BuildTargets, MakeBazelTarget(t.typ, t.name, t.attrs))
 	}
 	// Copy the filesystem so that we can change stuff in it later without it
 	// affecting the original pythonLibBp2BuildTestCase
@@ -66,7 +66,7 @@
 	testCase.ModuleTypeUnderTest = "python_library"
 	testCase.ModuleTypeUnderTestFactory = python.PythonLibraryFactory
 
-	runBp2BuildTestCaseSimple(t, testCase)
+	RunBp2BuildTestCaseSimple(t, testCase)
 }
 
 func runPythonLibraryHostTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index d8f701d..92b3a65 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -66,7 +66,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("sh_binary", "foo", AttrNameToString{
+			MakeBazelTarget("sh_binary", "foo", AttrNameToString{
 				"srcs":     `["foo.sh"]`,
 				"filename": `"foo.exe"`,
 				"sub_dir":  `"sub"`,
@@ -85,7 +85,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 		ExpectedBazelTargets: []string{
-			makeBazelTarget("sh_binary", "foo", AttrNameToString{
+			MakeBazelTarget("sh_binary", "foo", AttrNameToString{
 				"srcs": `["foo.sh"]`,
 			})},
 	})
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 78e7b0e..023ec96 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -1,12 +1,27 @@
+// 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"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"regexp"
 
+	"android/soong/android"
 	"android/soong/shared"
 )
 
@@ -58,6 +73,57 @@
 	return result
 }
 
+func mergeBuildFiles(output string, srcBuildFile string, generatedBuildFile string, verbose bool) error {
+
+	srcBuildFileContent, err := os.ReadFile(srcBuildFile)
+	if err != nil {
+		return err
+	}
+
+	generatedBuildFileContent, err := os.ReadFile(generatedBuildFile)
+	if err != nil {
+		return err
+	}
+
+	// There can't be a package() call in both the source and generated BUILD files.
+	// bp2build will generate a package() call for licensing information, but if
+	// there's no licensing information, it will still generate a package() call
+	// that just sets default_visibility=public. If the handcrafted build file
+	// also has a package() call, we'll allow it to override the bp2build
+	// generated one if it doesn't have any licensing information. If the bp2build
+	// one has licensing information and the handcrafted one exists, we'll leave
+	// them both in for bazel to throw an error.
+	packageRegex := regexp.MustCompile(`(?m)^package\s*\(`)
+	packageDefaultVisibilityRegex := regexp.MustCompile(`(?m)^package\s*\(\s*default_visibility\s*=\s*\[\s*"//visibility:public",?\s*]\s*\)`)
+	if packageRegex.Find(srcBuildFileContent) != nil {
+		if verbose && packageDefaultVisibilityRegex.Find(generatedBuildFileContent) != nil {
+			fmt.Fprintf(os.Stderr, "Both '%s' and '%s' have a package() target, removing the first one\n",
+				generatedBuildFile, srcBuildFile)
+		}
+		generatedBuildFileContent = packageDefaultVisibilityRegex.ReplaceAll(generatedBuildFileContent, []byte{})
+	}
+
+	outFile, err := os.Create(output)
+	if err != nil {
+		return err
+	}
+
+	_, err = outFile.Write(generatedBuildFileContent)
+	if err != nil {
+		return err
+	}
+
+	if generatedBuildFileContent[len(generatedBuildFileContent)-1] != '\n' {
+		_, err = outFile.WriteString("\n")
+		if err != nil {
+			return err
+		}
+	}
+
+	_, err = outFile.Write(srcBuildFileContent)
+	return err
+}
+
 // Calls readdir() and returns it as a map from the basename of the files in dir
 // to os.FileInfo.
 func readdirToMap(dir string) map[string]os.FileInfo {
@@ -125,6 +191,17 @@
 	srcDirMap := readdirToMap(shared.JoinPath(topdir, srcDir))
 	buildFilesMap := readdirToMap(shared.JoinPath(topdir, buildFilesDir))
 
+	renamingBuildFile := false
+	if _, ok := srcDirMap["BUILD"]; ok {
+		if _, ok := srcDirMap["BUILD.bazel"]; !ok {
+			if _, ok := buildFilesMap["BUILD.bazel"]; ok {
+				renamingBuildFile = true
+				srcDirMap["BUILD.bazel"] = srcDirMap["BUILD"]
+				delete(srcDirMap, "BUILD")
+			}
+		}
+	}
+
 	allEntries := make(map[string]bool)
 	for n := range srcDirMap {
 		allEntries[n] = true
@@ -148,21 +225,28 @@
 		// The full paths of children in the input trees and in the output tree
 		forestChild := shared.JoinPath(forestDir, f)
 		srcChild := shared.JoinPath(srcDir, f)
+		if f == "BUILD.bazel" && renamingBuildFile {
+			srcChild = shared.JoinPath(srcDir, "BUILD")
+		}
 		buildFilesChild := shared.JoinPath(buildFilesDir, f)
 
 		// Descend in the exclusion tree, if there are any excludes left
-		var excludeChild *node
-		if exclude == nil {
-			excludeChild = nil
-		} else {
-			excludeChild = exclude.children[f]
+		var excludeChild *node = nil
+		if exclude != nil {
+			if f == "BUILD.bazel" && renamingBuildFile {
+				excludeChild = exclude.children["BUILD"]
+			} else {
+				excludeChild = exclude.children[f]
+			}
 		}
 
 		srcChildEntry, sExists := srcDirMap[f]
 		buildFilesChildEntry, bExists := buildFilesMap[f]
-		excluded := excludeChild != nil && excludeChild.excluded
 
-		if excluded {
+		if excludeChild != nil && excludeChild.excluded {
+			if bExists {
+				symlinkIntoForest(topdir, forestChild, buildFilesChild)
+			}
 			continue
 		}
 
@@ -198,13 +282,15 @@
 			// Both are directories. Descend.
 			plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 		} else if !sDir && !bDir {
-			// Neither is a directory. Prioritize BUILD files generated by bp2build
-			// over any BUILD file imported into external/.
-			if cfg.IsEnvTrue("BP2BUILD_VERBOSE") {
-				fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
-					buildFilesChild, srcChild, forestChild)
+			// Neither is a directory. Merge them.
+			srcBuildFile := shared.JoinPath(topdir, srcChild)
+			generatedBuildFile := shared.JoinPath(topdir, buildFilesChild)
+			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",
+					srcBuildFile, generatedBuildFile, err)
+				*okay = false
 			}
-			symlinkIntoForest(topdir, forestChild, buildFilesChild)
 		} else {
 			// Both exist and one is a file. This is an error.
 			fmt.Fprintf(os.Stderr,
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 3ee5096..ac1268c 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -71,7 +71,7 @@
 	return false
 }
 
-func runBp2BuildTestCaseSimple(t *testing.T, tc Bp2buildTestCase) {
+func RunBp2BuildTestCaseSimple(t *testing.T, tc Bp2buildTestCase) {
 	t.Helper()
 	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
 }
@@ -173,11 +173,12 @@
 	Bool_prop     bool
 	Bool_ptr_prop *bool
 	// Ensure that properties tagged `blueprint:mutated` are omitted
-	Int_prop         int `blueprint:"mutated"`
-	Int64_ptr_prop   *int64
-	String_prop      string
-	String_ptr_prop  *string
-	String_list_prop []string
+	Int_prop            int `blueprint:"mutated"`
+	Int64_ptr_prop      *int64
+	String_prop         string
+	String_literal_prop *string `android:"arch_variant"`
+	String_ptr_prop     *string
+	String_list_prop    []string
 
 	Nested_props     nestedProps
 	Nested_props_ptr *nestedProps
@@ -305,23 +306,29 @@
 type customBazelModuleAttributes struct {
 	EmbeddedAttr
 	*OtherEmbeddedAttr
-	String_ptr_prop  *string
-	String_list_prop []string
-	Arch_paths       bazel.LabelListAttribute
+	String_literal_prop bazel.StringAttribute
+	String_ptr_prop     *string
+	String_list_prop    []string
+	Arch_paths          bazel.LabelListAttribute
 }
 
 func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	paths := bazel.LabelListAttribute{}
-
 	if p := m.props.One_to_many_prop; p != nil && *p {
 		customBp2buildOneToMany(ctx, m)
 		return
 	}
 
+	paths := bazel.LabelListAttribute{}
+	strAttr := bazel.StringAttribute{}
 	for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
 		for config, props := range configToProps {
-			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
-				paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
+			if custProps, ok := props.(*customProps); ok {
+				if custProps.Arch_paths != nil {
+					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, custProps.Arch_paths, custProps.Arch_paths_exclude))
+				}
+				if custProps.String_literal_prop != nil {
+					strAttr.SetSelectValue(axis, config, custProps.String_literal_prop)
+				}
 			}
 		}
 	}
@@ -329,10 +336,12 @@
 	paths.ResolveExcludes()
 
 	attrs := &customBazelModuleAttributes{
-		String_ptr_prop:  m.props.String_ptr_prop,
-		String_list_prop: m.props.String_list_prop,
-		Arch_paths:       paths,
+		String_literal_prop: strAttr,
+		String_ptr_prop:     m.props.String_ptr_prop,
+		String_list_prop:    m.props.String_list_prop,
+		Arch_paths:          paths,
 	}
+
 	attrs.Embedded_attr = m.props.Embedded_prop
 	if m.props.OtherEmbeddedProps != nil {
 		attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
@@ -375,6 +384,9 @@
 func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
 	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
 	res, err := GenerateBazelTargets(codegenCtx, false)
+	if err != nil {
+		return BazelTargets{}, err
+	}
 	return res.buildFileToTargets[dir], err
 }
 
@@ -435,6 +447,6 @@
 
 // makeBazelTargetNoRestrictions returns bazel target build file definition that is device specific
 // as this is the most common default in Soong.
-func makeBazelTarget(typ, name string, attrs AttrNameToString) string {
+func MakeBazelTarget(typ, name string, attrs AttrNameToString) string {
 	return makeBazelTargetHostOrDevice(typ, name, attrs, android.DeviceSupported)
 }
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 5d2533f..e89cc4e 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -69,12 +69,23 @@
 }
 
 type BpfProperties struct {
-	Srcs         []string `android:"path"`
-	Cflags       []string
+	// source paths to the files.
+	Srcs []string `android:"path"`
+
+	// additional cflags that should be used to build the bpf variant of
+	// the C/C++ module.
+	Cflags []string
+
+	// directories (relative to the root of the source tree) that will
+	// be added to the include paths using -I.
 	Include_dirs []string
-	Sub_dir      string
-	// If set to true, generate BTF debug info for maps & programs
-	Btf    *bool
+
+	// optional subdirectory under which this module is installed into.
+	Sub_dir string
+
+	// if set to true, generate BTF debug info for maps & programs.
+	Btf *bool
+
 	Vendor *bool
 
 	VendorInternal bool `blueprint:"mutated"`
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 94b28dc..ddaa98a 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -553,7 +553,9 @@
 
 // If a variable is LOCAL_MODULE, get its value from the 'name' attribute.
 // This handles the statement
-//    LOCAL_SRC_FILES := $(LOCAL_MODULE)
+//
+//	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+//
 // which occurs often.
 func resolveLocalModule(mod *parser.Module, val parser.Expression) parser.Expression {
 	if varLocalName, ok := val.(*parser.Variable); ok {
@@ -567,9 +569,9 @@
 }
 
 // etcPrebuiltModuleUpdate contains information on updating certain parts of a defined module such as:
-//    * changing the module type from prebuilt_etc to a different one
-//    * stripping the prefix of the install path based on the module type
-//    * appending additional boolean properties to the prebuilt module
+//   - changing the module type from prebuilt_etc to a different one
+//   - stripping the prefix of the install path based on the module type
+//   - appending additional boolean properties to the prebuilt module
 type etcPrebuiltModuleUpdate struct {
 	// The prefix of the install path defined in local_module_path. The prefix is removed from local_module_path
 	// before setting the 'filename' attribute.
diff --git a/cc/Android.bp b/cc/Android.bp
index ce94467..2963c77 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -99,10 +99,12 @@
         "library_headers_test.go",
         "library_stub_test.go",
         "library_test.go",
+        "ndk_test.go",
         "object_test.go",
         "prebuilt_test.go",
         "proto_test.go",
         "sanitize_test.go",
+        "sdk_test.go",
         "test_data_test.go",
         "tidy_test.go",
         "vendor_public_library_test.go",
diff --git a/cc/afdo.go b/cc/afdo.go
index 66e9732..fb66bbe 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -66,8 +66,9 @@
 }
 
 // Get list of profile file names, ordered by level of specialisation. For example:
-//   1. libfoo_arm64.afdo
-//   2. libfoo.afdo
+//  1. libfoo_arm64.afdo
+//  2. libfoo.afdo
+//
 // Add more specialisation as needed.
 func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string {
 	var files []string
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ff5ba45..a9ba1a9 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -23,6 +23,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/multitree"
 )
 
 var (
@@ -226,17 +227,27 @@
 	}
 }
 
-func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkEntries) {
-	if library.sAbiDiff.Valid() && !library.static() {
-		entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", library.sAbiDiff.String())
+func (library *libraryDecorator) getAbiDiffsForAndroidMkDeps() []string {
+	if library.static() {
+		return nil
 	}
+	var abiDiffs []string
+	if library.sAbiDiff.Valid() {
+		abiDiffs = append(abiDiffs, library.sAbiDiff.String())
+	}
+	if library.prevSAbiDiff.Valid() {
+		abiDiffs = append(abiDiffs, library.prevSAbiDiff.String())
+	}
+	return abiDiffs
+}
+
+func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkEntries) {
+	entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", library.getAbiDiffsForAndroidMkDeps()...)
 }
 
 // TODO(ccross): remove this once apex/androidmk.go is converted to AndroidMkEntries
 func (library *libraryDecorator) androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
-	if library.sAbiDiff.Valid() && !library.static() {
-		fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", library.sAbiDiff.String())
-	}
+	fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", strings.Join(library.getAbiDiffsForAndroidMkDeps(), " "))
 }
 
 func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -401,14 +412,13 @@
 			entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
 		}
 		entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", test.Properties.Test_mainline_modules...)
-		if Bool(test.Properties.Test_options.Unit_test) {
-			entries.SetBool("LOCAL_IS_UNIT_TEST", true)
-		}
 
 		entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
 		if len(test.Properties.Data_bins) > 0 {
 			entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
 		}
+
+		test.Properties.Test_options.CommonTestOptions.SetAndroidMkEntries(entries)
 	})
 
 	AndroidMkWriteTestData(test.data, entries)
@@ -616,6 +626,34 @@
 	androidMkWriteAllowUndefinedSymbols(p.baseLinker, entries)
 }
 
+func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	entries.Class = "SHARED_LIBRARIES"
+	entries.SubName += multitree.GetApiImportSuffix()
+
+	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+		a.libraryDecorator.androidMkWriteExportedFlags(entries)
+		src := *a.properties.Src
+		path, file := filepath.Split(src)
+		stem, suffix, ext := android.SplitFileExt(file)
+		entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
+		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+		entries.SetString("LOCAL_MODULE_STEM", stem)
+		entries.SetString("LOCAL_MODULE_PATH", path)
+		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+		entries.SetString("LOCAL_SOONG_TOC", a.toc().String())
+	})
+}
+
+func (a *apiHeadersDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	entries.Class = "HEADER_LIBRARIES"
+	entries.SubName += multitree.GetApiImportSuffix()
+
+	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+		a.libraryDecorator.androidMkWriteExportedFlags(entries)
+		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+	})
+}
+
 func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, entries *android.AndroidMkEntries) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
diff --git a/cc/binary.go b/cc/binary.go
index b2f2482..3351fd7 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"android/soong/bazel/cquery"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -591,7 +592,7 @@
 	handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
 }
 
-func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
+func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
 	baseAttrs := bp2BuildParseBaseProps(ctx, m)
 	binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
 
@@ -601,7 +602,7 @@
 		baseAttrs.implementationDeps.Add(baseAttrs.protoDependency)
 	}
 
-	attrs := &binaryAttributes{
+	attrs := binaryAttributes{
 		binaryLinkerAttrs: binaryLinkerAttrs,
 
 		Srcs:    baseAttrs.srcs,
@@ -617,6 +618,7 @@
 		Dynamic_deps:       baseAttrs.implementationDynamicDeps,
 		Whole_archive_deps: baseAttrs.wholeArchiveDeps,
 		System_deps:        baseAttrs.systemDynamicDeps,
+		Runtime_deps:       baseAttrs.runtimeDeps,
 
 		Local_includes:    baseAttrs.localIncludes,
 		Absolute_includes: baseAttrs.absoluteIncludes,
@@ -643,12 +645,19 @@
 		sdkAttributes: bp2BuildParseSdkAttributes(m),
 	}
 
+	return attrs
+}
+
+func binaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+	// shared with cc_test
+	binaryAttrs := binaryBp2buildAttrs(ctx, m)
+
 	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_binary",
 		Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
 	},
 		android.CommonAttributes{Name: m.Name()},
-		attrs)
+		&binaryAttrs)
 }
 
 // binaryAttributes contains Bazel attributes corresponding to a cc binary
@@ -667,6 +676,7 @@
 	Dynamic_deps       bazel.LabelListAttribute
 	Whole_archive_deps bazel.LabelListAttribute
 	System_deps        bazel.LabelListAttribute
+	Runtime_deps       bazel.LabelListAttribute
 
 	Local_includes    bazel.StringListAttribute
 	Absolute_includes bazel.StringListAttribute
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 61a55ee..e001d24 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -35,16 +35,20 @@
 	llSrcPartition    = "ll"
 	cppSrcPartition   = "cpp"
 	protoSrcPartition = "proto"
+	aidlSrcPartition  = "aidl"
+
+	stubsSuffix = "_stub_libs_current"
 )
 
 // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
 // properties which apply to either the shared or static version of a cc_library module.
 type staticOrSharedAttributes struct {
-	Srcs    bazel.LabelListAttribute
-	Srcs_c  bazel.LabelListAttribute
-	Srcs_as bazel.LabelListAttribute
-	Hdrs    bazel.LabelListAttribute
-	Copts   bazel.StringListAttribute
+	Srcs      bazel.LabelListAttribute
+	Srcs_c    bazel.LabelListAttribute
+	Srcs_as   bazel.LabelListAttribute
+	Srcs_aidl bazel.LabelListAttribute
+	Hdrs      bazel.LabelListAttribute
+	Copts     bazel.StringListAttribute
 
 	Deps                              bazel.LabelListAttribute
 	Implementation_deps               bazel.LabelListAttribute
@@ -52,6 +56,7 @@
 	Implementation_dynamic_deps       bazel.LabelListAttribute
 	Whole_archive_deps                bazel.LabelListAttribute
 	Implementation_whole_archive_deps bazel.LabelListAttribute
+	Runtime_deps                      bazel.LabelListAttribute
 
 	System_dynamic_deps bazel.LabelListAttribute
 
@@ -67,10 +72,17 @@
 	// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
 	// macro.
 	addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
-		return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
-			m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+		return func(otherModuleCtx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+
+			m, exists := otherModuleCtx.ModuleFromName(label.OriginalModuleName)
 			labelStr := label.Label
-			if !exists || !android.IsFilegroup(ctx, m) {
+			if !exists || !android.IsFilegroup(otherModuleCtx, m) {
+				return labelStr, false
+			}
+			// If the filegroup is already converted to aidl_library, skip creating
+			// _c_srcs, _as_srcs, _cpp_srcs filegroups
+			fg, _ := m.(android.Bp2buildAidlLibrary)
+			if fg.ShouldConvertToAidlLibrary(ctx) {
 				return labelStr, false
 			}
 			return labelStr + suffix, true
@@ -83,6 +95,7 @@
 		cSrcPartition:     bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
 		asSrcPartition:    bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
 		asmSrcPartition:   bazel.LabelPartition{Extensions: []string{".asm"}},
+		aidlSrcPartition:  android.AidlSrcLabelPartition,
 		// TODO(http://b/231968910): If there is ever a filegroup target that
 		// 		contains .l or .ll files we will need to find a way to add a
 		// 		LabelMapper for these that identifies these filegroups and
@@ -281,7 +294,10 @@
 	compilerAttributes
 	linkerAttributes
 
+	// A combination of compilerAttributes.features and linkerAttributes.features
+	features        bazel.StringListAttribute
 	protoDependency *bazel.LabelAttribute
+	aidlDependency  *bazel.LabelAttribute
 }
 
 // Convenience struct to hold all attributes parsed from compiler properties.
@@ -319,9 +335,14 @@
 	includes BazelIncludes
 
 	protoSrcs bazel.LabelListAttribute
+	aidlSrcs  bazel.LabelListAttribute
 
 	stubsSymbolFile *string
 	stubsVersions   bazel.StringListAttribute
+
+	features bazel.StringListAttribute
+
+	suffix bazel.StringAttribute
 }
 
 type filterOutFn func(string) bool
@@ -385,6 +406,13 @@
 	ca.absoluteIncludes.SetSelectValue(axis, config, props.Include_dirs)
 	ca.localIncludes.SetSelectValue(axis, config, localIncludeDirs)
 
+	instructionSet := proptools.StringDefault(props.Instruction_set, "")
+	if instructionSet == "arm" {
+		ca.features.SetSelectValue(axis, config, []string{"arm_isa_arm", "-arm_isa_thumb"})
+	} else if instructionSet != "" && instructionSet != "thumb" {
+		ctx.ModuleErrorf("Unknown value for instruction_set: %s", instructionSet)
+	}
+
 	// In Soong, cflags occur on the command line before -std=<val> flag, resulting in the value being
 	// 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
@@ -437,6 +465,7 @@
 	partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
 
 	ca.protoSrcs = partitionedSrcs[protoSrcPartition]
+	ca.aidlSrcs = partitionedSrcs[aidlSrcPartition]
 
 	for p, lla := range partitionedSrcs {
 		// if there are no sources, there is no need for headers
@@ -469,12 +498,27 @@
 	}
 
 	allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, props.Srcs, props.Exclude_srcs)
+
 	if len(props.Srcs) > 0 || len(props.Exclude_srcs) > 0 {
 		anySrcs = true
 	}
+
 	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
 }
 
+// Given a name in srcs prop, check to see if the name references a filegroup
+// and the filegroup is converted to aidl_library
+func isConvertedToAidlLibrary(ctx android.BazelConversionPathContext, name string) bool {
+	if module, ok := ctx.ModuleFromName(name); ok {
+		if android.IsFilegroup(ctx, module) {
+			if fg, ok := module.(android.Bp2buildAidlLibrary); ok {
+				return fg.ShouldConvertToAidlLibrary(ctx)
+			}
+		}
+	}
+	return false
+}
+
 func bp2buildStdVal(std *string, prefix string, useGnu bool) *string {
 	defaultVal := prefix + "_std_default"
 	// If c{,pp}std properties are not specified, don't generate them in the BUILD file.
@@ -641,9 +685,12 @@
 			includes, absoluteIncludes := includesFromLabelList(headers.implementation)
 			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
 			currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
+
 			compilerAttrs.absoluteIncludes.SetSelectValue(axis, config, currAbsoluteIncludes)
+
 			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, config)
 			currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
+
 			compilerAttrs.localIncludes.SetSelectValue(axis, config, currIncludes)
 
 			if libraryProps, ok := archVariantLibraryProperties[axis][config].(*LibraryProperties); ok {
@@ -651,9 +698,13 @@
 					compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
 					compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
 				}
+				if suffix := libraryProps.Suffix; suffix != nil {
+					compilerAttrs.suffix.SetSelectValue(axis, config, suffix)
+				}
 			}
 		}
 	}
+
 	compilerAttrs.convertStlProps(ctx, module)
 	(&linkerAttrs).convertStripProps(ctx, module)
 
@@ -681,17 +732,107 @@
 	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
 	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
 
+	aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs)
+	if aidlDep != nil {
+		if lib, ok := module.linker.(*libraryDecorator); ok {
+			if proptools.Bool(lib.Properties.Aidl.Export_aidl_headers) {
+				(&linkerAttrs).wholeArchiveDeps.Add(aidlDep)
+			} else {
+				(&linkerAttrs).implementationWholeArchiveDeps.Add(aidlDep)
+			}
+		}
+	}
+
 	convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
 	(&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
 	(&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName)
 
+	features := compilerAttrs.features.Clone().Append(linkerAttrs.features)
+	features.DeduplicateAxesFromBase()
+
 	return baseAttributes{
 		compilerAttrs,
 		linkerAttrs,
+		*features,
 		protoDep.protoDep,
+		aidlDep,
 	}
 }
 
+func bp2buildAidlLibraries(
+	ctx android.Bp2buildMutatorContext,
+	m *Module,
+	aidlSrcs bazel.LabelListAttribute,
+) bazel.LabelList {
+	var aidlLibraries bazel.LabelList
+	var directAidlSrcs bazel.LabelList
+
+	// Make a list of labels that correspond to filegroups that are already converted to aidl_library
+	for _, aidlSrc := range aidlSrcs.Value.Includes {
+		src := aidlSrc.OriginalModuleName
+		if isConvertedToAidlLibrary(ctx, src) {
+			module, _ := ctx.ModuleFromName(src)
+			fg, _ := module.(android.Bp2buildAidlLibrary)
+			aidlLibraries.Add(&bazel.Label{
+				Label: fg.GetAidlLibraryLabel(ctx),
+			})
+		} else {
+			directAidlSrcs.Add(&aidlSrc)
+		}
+	}
+
+	if len(directAidlSrcs.Includes) > 0 {
+		aidlLibraryLabel := m.Name() + "_aidl_library"
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "aidl_library",
+				Bzl_load_location: "//build/bazel/rules/aidl:library.bzl",
+			},
+			android.CommonAttributes{Name: aidlLibraryLabel},
+			&aidlLibraryAttributes{
+				Srcs: bazel.MakeLabelListAttribute(directAidlSrcs),
+			},
+		)
+		aidlLibraries.Add(&bazel.Label{
+			Label: ":" + aidlLibraryLabel,
+		})
+	}
+	return aidlLibraries
+}
+
+func bp2buildCcAidlLibrary(
+	ctx android.Bp2buildMutatorContext,
+	m *Module,
+	aidlSrcs bazel.LabelListAttribute,
+) *bazel.LabelAttribute {
+	suffix := "_cc_aidl_library"
+	ccAidlLibrarylabel := m.Name() + suffix
+
+	aidlLibraries := bp2buildAidlLibraries(ctx, m, aidlSrcs)
+
+	if aidlLibraries.IsEmpty() {
+		return nil
+	}
+
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_aidl_library",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
+		},
+		android.CommonAttributes{Name: ccAidlLibrarylabel},
+		&ccAidlLibraryAttributes{
+			Deps: bazel.MakeLabelListAttribute(aidlLibraries),
+		},
+	)
+
+	label := &bazel.LabelAttribute{
+		Value: &bazel.Label{
+			Label: ":" + ccAidlLibrarylabel,
+		},
+	}
+	return label
+}
+
 func bp2BuildParseSdkAttributes(module *Module) sdkAttributes {
 	return sdkAttributes{
 		Sdk_version:     module.Properties.Sdk_version,
@@ -710,6 +851,7 @@
 	implementationDeps               bazel.LabelListAttribute
 	dynamicDeps                      bazel.LabelListAttribute
 	implementationDynamicDeps        bazel.LabelListAttribute
+	runtimeDeps                      bazel.LabelListAttribute
 	wholeArchiveDeps                 bazel.LabelListAttribute
 	implementationWholeArchiveDeps   bazel.LabelListAttribute
 	systemDynamicDeps                bazel.LabelListAttribute
@@ -777,6 +919,45 @@
 	sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, sharedLibs, props.Exclude_shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
 	la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
 	la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
+	if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
+		// If a dependency in la.implementationDynamicDeps has stubs, its stub variant should be
+		// used when the dependency is linked in a APEX. The dependencies in NoConfigAxis and
+		// OsConfigurationAxis/OsAndroid are grouped by having stubs or not, so Bazel select()
+		// statement can be used to choose source/stub variants of them.
+		depsWithStubs := []bazel.Label{}
+		for _, l := range sharedDeps.implementation.Includes {
+			dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
+			if m, ok := dep.(*Module); ok && m.HasStubsVariants() {
+				depsWithStubs = append(depsWithStubs, l)
+			}
+		}
+		if len(depsWithStubs) > 0 {
+			implDynamicDeps := bazel.SubtractBazelLabelList(sharedDeps.implementation, bazel.MakeLabelList(depsWithStubs))
+			la.implementationDynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
+
+			stubLibLabels := []bazel.Label{}
+			for _, l := range depsWithStubs {
+				l.Label = l.Label + stubsSuffix
+				stubLibLabels = append(stubLibLabels, l)
+			}
+			inApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
+			nonApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
+			defaultSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
+			if axis == bazel.NoConfigAxis {
+				(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+				(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+				(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, inApexSelectValue)
+				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, nonApexSelectValue)
+				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, defaultSelectValue)
+			} else if config == bazel.OsAndroid {
+				(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+				(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, inApexSelectValue)
+				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, nonApexSelectValue)
+			}
+		}
+	}
 
 	if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
 		axisFeatures = append(axisFeatures, "disable_pack_relocations")
@@ -825,6 +1006,11 @@
 	if axisFeatures != nil {
 		la.features.SetSelectValue(axis, config, axisFeatures)
 	}
+
+	runtimeDeps := android.BazelLabelForModuleDepsExcludes(ctx, props.Runtime_libs, props.Exclude_runtime_libs)
+	if !runtimeDeps.IsEmpty() {
+		la.runtimeDeps.SetSelectValue(axis, config, runtimeDeps)
+	}
 }
 
 func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
@@ -850,11 +1036,17 @@
 		depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
 	}
 
+	// an intermediate attribute that holds Header_libs info, and will be appended to
+	// implementationDeps at the end, to solve the confliction that both header_libs
+	// and static_libs use implementationDeps.
+	var headerDeps bazel.LabelListAttribute
+
 	productVarToDepFields := map[string]productVarDep{
 		// product variables do not support exclude_shared_libs
 		"Shared_libs":       {attribute: &la.implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
 		"Static_libs":       {"Exclude_static_libs", &la.implementationDeps, bazelLabelForStaticDepsExcludes},
 		"Whole_static_libs": {"Exclude_static_libs", &la.wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
+		"Header_libs":       {attribute: &headerDeps, depResolutionFunc: bazelLabelForHeaderDepsExcludes},
 	}
 
 	for name, dep := range productVarToDepFields {
@@ -896,18 +1088,30 @@
 			)
 		}
 	}
+	la.implementationDeps.Append(headerDeps)
 }
 
 func (la *linkerAttributes) finalize(ctx android.BazelConversionPathContext) {
 	// if system dynamic deps have the default value, any use of a system dynamic library used will
 	// result in duplicate library errors for bionic OSes. Here, we explicitly exclude those libraries
-	// from bionic OSes.
+	// from bionic OSes and the no config case as these libraries only build for bionic OSes.
 	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
 		toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
+		la.dynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
 		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
 		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, toRemove)
+		stubsToRemove := make([]bazel.Label, 0, len(la.usedSystemDynamicDepAsDynamicDep))
+		for _, lib := range toRemove.Includes {
+			lib.Label += stubsSuffix
+			stubsToRemove = append(stubsToRemove, lib)
+		}
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.MakeLabelList(stubsToRemove))
 	}
 
 	la.deps.ResolveExcludes()
@@ -976,7 +1180,7 @@
 
 func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
 	label := android.BazelModuleLabel(ctx, m)
-	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(m.Name()) {
 		label += "_bp2build_cc_library_static"
 	}
 	return label
@@ -1024,12 +1228,19 @@
 	return bazelLabelForSharedDeps(ctx, modules)
 }
 
+func bazelLabelForHeaderDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+	// This is only used when product_variable header_libs is processed, to follow
+	// the pattern of depResolutionFunc
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
+}
+
 func bazelLabelForSharedDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
 }
 
 type binaryLinkerAttrs struct {
 	Linkshared *bool
+	Suffix     bazel.StringAttribute
 }
 
 func bp2buildBinaryLinkerProps(ctx android.BazelConversionPathContext, m *Module) binaryLinkerAttrs {
@@ -1046,6 +1257,9 @@
 			// nonconfigurable attribute. Only 4 AOSP modules use this feature, defer handling
 			ctx.ModuleErrorf("bp2build cannot migrate a module with arch/target-specific static_executable values")
 		}
+		if suffix := linkerProps.Suffix; suffix != nil {
+			attrs.Suffix.SetSelectValue(axis, config, suffix)
+		}
 	})
 
 	return attrs
diff --git a/cc/builder.go b/cc/builder.go
index ab2b80a..cb21b1f 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -282,7 +282,7 @@
 	sAbiDiff = pctx.RuleFunc("sAbiDiff",
 		func(ctx android.PackageRuleContext) blueprint.RuleParams {
 			commandStr := "($sAbiDiffer ${extraFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
-			commandStr += "|| (echo 'error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py ${createReferenceDumpFlags} -l ${libName}'"
+			commandStr += "|| (echo '${errorMessage}'"
 			commandStr += " && (mkdir -p $$DIST_DIR/abidiffs && cp ${out} $$DIST_DIR/abidiffs/)"
 			commandStr += " && exit 1)"
 			return blueprint.RuleParams{
@@ -290,7 +290,7 @@
 				CommandDeps: []string{"$sAbiDiffer"},
 			}
 		},
-		"extraFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
+		"extraFlags", "referenceDump", "libName", "arch", "errorMessage")
 
 	// Rule to unzip a reference abi dump.
 	unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
@@ -920,10 +920,15 @@
 
 // sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
 func sourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
-	baseName, exportedHeaderFlags string, diffFlags []string,
-	checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
+	baseName, exportedHeaderFlags string, diffFlags []string, prevVersion int,
+	checkAllApis, isLlndk, isNdk, isVndkExt, previousVersionDiff bool) android.OptionalPath {
 
-	outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
+	var outputFile android.ModuleOutPath
+	if previousVersionDiff {
+		outputFile = android.PathForModuleOut(ctx, baseName+"."+strconv.Itoa(prevVersion)+".abidiff")
+	} else {
+		outputFile = android.PathForModuleOut(ctx, baseName+".abidiff")
+	}
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 
 	var extraFlags []string
@@ -935,10 +940,24 @@
 			"-allow-unreferenced-elf-symbol-changes")
 	}
 
+	var errorMessage string
+	// When error occurs in previous version ABI diff, Developers can't just update ABI
+	// reference but need to follow instructions to ensure ABI backward compatibility.
+	if previousVersionDiff {
+		// TODO(b/241496591): Remove -advice-only after b/239792343 and b/239790286 are reolved.
+		extraFlags = append(extraFlags, "-advice-only")
+		errorMessage = "error: Please follow development/vndk/tools/header-checker/README.md to ensure the ABI compatibility between your source code and version " + strconv.Itoa(prevVersion) + "."
+		sourceVersion := prevVersion + 1
+		extraFlags = append(extraFlags, "-target-version", strconv.Itoa(sourceVersion))
+	} else {
+		errorMessage = "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
+		extraFlags = append(extraFlags, "-target-version", "current")
+	}
+
 	if isLlndk || isNdk {
 		extraFlags = append(extraFlags, "-consider-opaque-types-different")
 	}
-	if isVndkExt {
+	if isVndkExt || previousVersionDiff {
 		extraFlags = append(extraFlags, "-allow-extensions")
 	}
 	// TODO(b/232891473): Simplify the above logic with diffFlags.
@@ -951,11 +970,11 @@
 		Input:       inputDump,
 		Implicit:    referenceDump,
 		Args: map[string]string{
-			"referenceDump":            referenceDump.String(),
-			"libName":                  libName,
-			"arch":                     ctx.Arch().ArchType.Name,
-			"extraFlags":               strings.Join(extraFlags, " "),
-			"createReferenceDumpFlags": "",
+			"referenceDump": referenceDump.String(),
+			"libName":       libName,
+			"arch":          ctx.Arch().ArchType.Name,
+			"extraFlags":    strings.Join(extraFlags, " "),
+			"errorMessage":  errorMessage,
 		},
 	})
 	return android.OptionalPathForPath(outputFile)
diff --git a/cc/cc.go b/cc/cc.go
index c71fb34..3129160 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -485,6 +485,7 @@
 	static() bool
 	staticBinary() bool
 	testBinary() bool
+	testLibrary() bool
 	header() bool
 	binary() bool
 	object() bool
@@ -514,6 +515,7 @@
 	getVndkExtendsModuleName() string
 	isAfdoCompile() bool
 	isPgoCompile() bool
+	isCfi() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
 	isForPlatform() bool
@@ -1314,6 +1316,13 @@
 	return false
 }
 
+func (c *Module) isCfi() bool {
+	if sanitize := c.sanitize; sanitize != nil {
+		return Bool(sanitize.Properties.Sanitize.Cfi)
+	}
+	return false
+}
+
 func (c *Module) isNDKStubLibrary() bool {
 	if _, ok := c.compiler.(*stubDecorator); ok {
 		return true
@@ -1478,6 +1487,10 @@
 	return ctx.mod.testBinary()
 }
 
+func (ctx *moduleContextImpl) testLibrary() bool {
+	return ctx.mod.testLibrary()
+}
+
 func (ctx *moduleContextImpl) header() bool {
 	return ctx.mod.Header()
 }
@@ -1592,6 +1605,10 @@
 	return ctx.mod.isPgoCompile()
 }
 
+func (ctx *moduleContextImpl) isCfi() bool {
+	return ctx.mod.isCfi()
+}
+
 func (ctx *moduleContextImpl) isNDKStubLibrary() bool {
 	return ctx.mod.isNDKStubLibrary()
 }
@@ -1888,6 +1905,8 @@
 
 	if c.Properties.Clang != nil && *c.Properties.Clang == false {
 		ctx.PropertyErrorf("clang", "false (GCC) is no longer supported")
+	} else if c.Properties.Clang != nil && !ctx.DeviceConfig().BuildBrokenClangProperty() {
+		ctx.PropertyErrorf("clang", "property is deprecated, see Changes.md file")
 	}
 
 	flags := Flags{
@@ -2288,9 +2307,14 @@
 		deps.RuntimeLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
 	}
 
-	for idx, lib := range deps.HeaderLibs {
-		deps.HeaderLibs[idx] = GetReplaceModuleName(lib, apiImports.HeaderLibs)
+	for idx, lib := range deps.SystemSharedLibs {
+		deps.SystemSharedLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
 	}
+
+	for idx, lib := range deps.ReexportSharedLibHeaders {
+		deps.ReexportSharedLibHeaders[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
+	}
+
 	return deps
 }
 
@@ -2334,9 +2358,14 @@
 			depTag.reexportFlags = true
 		}
 
+		// Check header lib replacement from API surface first, and then check again with VSDK
+		lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
 		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
 
-		if c.IsStubs() {
+		if c.isNDKStubLibrary() {
+			// ndk_headers do not have any variations
+			actx.AddFarVariationDependencies([]blueprint.Variation{}, depTag, lib)
+		} else if c.IsStubs() && !c.isImportedApiLibrary() {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
 				depTag, lib)
 		} else {
@@ -3241,11 +3270,6 @@
 
 			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix()
 		}
-
-		// Remove API import suffix if exists
-		if _, ok := ccDepModule.linker.(*apiLibraryDecorator); ok {
-			libName = strings.TrimSuffix(libName, multitree.GetApiImportSuffix())
-		}
 	}
 
 	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() &&
@@ -3357,6 +3381,15 @@
 	return false
 }
 
+func (c *Module) testLibrary() bool {
+	if test, ok := c.linker.(interface {
+		testLibrary() bool
+	}); ok {
+		return test.testLibrary()
+	}
+	return false
+}
+
 func (c *Module) benchmarkBinary() bool {
 	if b, ok := c.linker.(interface {
 		benchmarkBinary() bool
@@ -3581,9 +3614,6 @@
 	if _, ok := c.linker.(prebuiltLinkerInterface); ok {
 		return nil
 	}
-	if _, ok := c.linker.(*apiLibraryDecorator); ok {
-		return nil
-	}
 
 	minSdkVersion := c.MinSdkVersion()
 	if minSdkVersion == "apex_inherit" {
@@ -3638,13 +3668,25 @@
 	staticLibrary
 	sharedLibrary
 	headerLibrary
+	testBin // testBinary already declared
 )
 
 func (c *Module) typ() moduleType {
-	if c.Binary() {
+	if c.testBinary() {
+		// testBinary is also a binary, so this comes before the c.Binary()
+		// conditional. A testBinary has additional implicit dependencies and
+		// other test-only semantics.
+		return testBin
+	} else if c.Binary() {
 		return binary
 	} else if c.Object() {
 		return object
+	} else if c.testLibrary() {
+		// TODO(b/244431896) properly convert cc_test_library to its own macro. This
+		// will let them add implicit compile deps on gtest, for example.
+		//
+		// For now, treat them as regular shared libraries.
+		return sharedLibrary
 	} else if c.CcLibrary() {
 		static := false
 		shared := false
@@ -3673,7 +3715,11 @@
 	switch c.typ() {
 	case binary:
 		if !prebuilt {
-			binaryBp2build(ctx, c, ctx.ModuleType())
+			binaryBp2build(ctx, c)
+		}
+	case testBin:
+		if !prebuilt {
+			testBinaryBp2build(ctx, c)
 		}
 	case object:
 		if !prebuilt {
@@ -3764,6 +3810,11 @@
 	return c.Properties.IsSdkVariant
 }
 
+func (c *Module) isImportedApiLibrary() bool {
+	_, ok := c.linker.(*apiLibraryDecorator)
+	return ok
+}
+
 func kytheExtractAllFactory() android.Singleton {
 	return &kytheExtractAllSingleton{}
 }
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 792a8e0..5d569cc 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4111,7 +4111,7 @@
 			name: "libfoo",
 			srcs: [
 				"foo.c",
-				"a.sysprop",
+				"path/to/a.sysprop",
 				"b.aidl",
 				"a.proto",
 			],
@@ -4124,11 +4124,11 @@
 			`),
 			expectedSystemIncludeDirs(``),
 			expectedGeneratedHeaders(`
-				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/path/to/a.sysprop.h
 			`),
 			expectedOrderOnlyDeps(`
-				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
-				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/a.sysprop.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/path/to/a.sysprop.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/path/to/a.sysprop.h
 			`),
 		)
 	})
@@ -4145,7 +4145,7 @@
 		"${config.ArmArmv7ANeonCflags}",
 		"${config.ArmGenericCflags}",
 		"-target",
-		"armv7a-linux-androideabi20",
+		"armv7a-linux-androideabi21",
 	}
 
 	expectedIncludes := []string{
@@ -4176,7 +4176,6 @@
 		"external/foo/lib32",
 		"external/foo/libandroid_arm",
 		"defaults/cc/common/ndk_libc++_shared",
-		"defaults/cc/common/ndk_libandroid_support",
 	}
 
 	conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
@@ -4269,20 +4268,20 @@
 				},
 			},
 			stl: "libc++",
-			sdk_version: "20",
+			sdk_version: "minimum",
 		}
 
 		cc_library_headers {
 			name: "libheader1",
 			export_include_dirs: ["libheader1"],
-			sdk_version: "20",
+			sdk_version: "minimum",
 			stl: "none",
 		}
 
 		cc_library_headers {
 			name: "libheader2",
 			export_include_dirs: ["libheader2"],
-			sdk_version: "20",
+			sdk_version: "minimum",
 			stl: "none",
 		}
 	`, tc.src)
@@ -4306,7 +4305,7 @@
 			cc_library {
 				name: "%s",
 				export_include_dirs: ["%s"],
-				sdk_version: "20",
+				sdk_version: "minimum",
 				stl: "none",
 			}
 		`, lib, lib)
@@ -4337,3 +4336,53 @@
 	}
 
 }
+
+func TestCcBuildBrokenClangProperty(t *testing.T) {
+	tests := []struct {
+		name                     string
+		clang                    bool
+		BuildBrokenClangProperty bool
+		err                      string
+	}{
+		{
+			name:  "error when clang is set to false",
+			clang: false,
+			err:   "is no longer supported",
+		},
+		{
+			name:  "error when clang is set to true",
+			clang: true,
+			err:   "property is deprecated, see Changes.md",
+		},
+		{
+			name:                     "no error when BuildBrokenClangProperty is explicitly set to true",
+			clang:                    true,
+			BuildBrokenClangProperty: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			bp := fmt.Sprintf(`
+			cc_library {
+			   name: "foo",
+			   clang: %t,
+			}`, test.clang)
+
+			if test.err == "" {
+				android.GroupFixturePreparers(
+					prepareForCcTest,
+					android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+						if test.BuildBrokenClangProperty {
+							variables.BuildBrokenClangProperty = test.BuildBrokenClangProperty
+						}
+					}),
+				).RunTestWithBp(t, bp)
+			} else {
+				prepareForCcTest.
+					ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(test.err)).
+					RunTestWithBp(t, bp)
+			}
+		})
+	}
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index 3c904b8..f9f7b6f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -565,7 +565,7 @@
 			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
 		}
 
-		if Bool(compiler.Properties.Aidl.Generate_traces) {
+		if proptools.BoolDefault(compiler.Properties.Aidl.Generate_traces, true) {
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
 
diff --git a/cc/config/bionic.go b/cc/config/bionic.go
index e87f571..a1e3851 100644
--- a/cc/config/bionic.go
+++ b/cc/config/bionic.go
@@ -15,6 +15,7 @@
 package config
 
 type toolchainBionic struct {
+	toolchainBase
 }
 
 var (
@@ -29,6 +30,12 @@
 
 func (toolchainBionic) DefaultSharedLibraries() []string { return bionicDefaultSharedLibraries }
 
+func (toolchainBionic) ShlibSuffix() string { return ".so" }
+
+func (toolchainBionic) ExecutableSuffix() string { return "" }
+
+func (toolchainBionic) AvailableLibraries() []string { return nil }
+
 func (toolchainBionic) CrtBeginStaticBinary() []string  { return bionicCrtBeginStaticBinary }
 func (toolchainBionic) CrtBeginSharedBinary() []string  { return bionicCrtBeginSharedBinary }
 func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary }
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 5e3f7c7..01b1e63 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -176,6 +176,8 @@
 type toolchainDarwin struct {
 	cFlags, ldFlags string
 	toolchain64Bit
+	toolchainNoCrt
+	toolchainBase
 }
 
 type toolchainDarwinX86 struct {
@@ -254,6 +256,10 @@
 	return ".dylib"
 }
 
+func (t *toolchainDarwin) ExecutableSuffix() string {
+	return ""
+}
+
 func (t *toolchainDarwin) AvailableLibraries() []string {
 	return darwinAvailableLibraries
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index 357ea44..f920471 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -290,7 +290,12 @@
 		"-Wno-deprecated-non-prototype",
 	}
 
-	llvmNextExtraCommonGlobalCflags = []string{}
+	llvmNextExtraCommonGlobalCflags = []string{
+		// New warnings to be fixed after clang-r468909
+		"-Wno-error=array-parameter",     // http://b/241941550
+		"-Wno-error=deprecated-builtins", // http://b/241601211
+		"-Wno-error=deprecated",          // in external/googletest/googletest
+	}
 
 	IllegalFlags = []string{
 		"-w",
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 674edad..23bda66 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -37,16 +37,22 @@
 		// http://b/216364337 - TODO: Follow-up after compiler update to
 		// disable or fix individual instances.
 		"-cert-err33-c",
+		// http://b/241125373
+		"-bugprone-unchecked-optional-access",
 	}
 
 	// Some clang-tidy checks are included in some tidy_checks_as_errors lists,
 	// but not all warnings are fixed/suppressed yet. These checks are not
 	// disabled in the TidyGlobalNoChecks list, so we can see them and fix/suppress them.
 	globalNoErrorCheckList = []string{
-		// http://b/155034563
-		"-bugprone-signed-char-misuse",
+		// http://b/241997913
+		"-bugprone-assignment-in-if-condition",
 		// http://b/155034972
 		"-bugprone-branch-clone",
+		// http://b/155034563
+		"-bugprone-signed-char-misuse",
+		// http://b/241819232
+		"-misc-const-correctness",
 	}
 )
 
@@ -80,8 +86,10 @@
 			"misc-*",
 			"performance-*",
 			"portability-*",
+			"-bugprone-assignment-in-if-condition",
 			"-bugprone-easily-swappable-parameters",
 			"-bugprone-narrowing-conversions",
+			"-misc-const-correctness",
 			"-misc-no-recursion",
 			"-misc-non-private-member-variables-in-classes",
 			"-misc-unused-parameters",
@@ -156,19 +164,21 @@
 const tidyDefaultNoAnalyzer = "${config.TidyDefaultGlobalChecks},-clang-analyzer-*"
 
 // This is a map of local path prefixes to the set of default clang-tidy checks
-// to be used.
+// to be used.  This is like android.IsThirdPartyPath, but with more patterns.
 // The last matched local_path_prefix should be the most specific to be used.
 var DefaultLocalTidyChecks = []PathBasedTidyCheck{
 	{"external/", tidyExternalVendor},
-	{"external/google", tidyDefault},
-	{"external/webrtc", tidyDefault},
-	{"external/googletest/", tidyExternalVendor},
 	{"frameworks/compile/mclinker/", tidyExternalVendor},
-	{"hardware/qcom", tidyExternalVendor},
+	{"hardware/", tidyExternalVendor},
+	{"hardware/google/", tidyDefault},
+	{"hardware/interfaces/", tidyDefault},
+	{"hardware/ril/", tidyDefault},
+	{"hardware/libhardware", tidyDefault}, // all 'hardware/libhardware*'
 	{"vendor/", tidyExternalVendor},
-	{"vendor/google", tidyDefault},
+	{"vendor/google", tidyDefault}, // all 'vendor/google*'
+	{"vendor/google/external/", tidyExternalVendor},
 	{"vendor/google_arc/libs/org.chromium.arc.mojom", tidyExternalVendor},
-	{"vendor/google_devices", tidyExternalVendor},
+	{"vendor/google_devices/", tidyExternalVendor}, // many have vendor code
 }
 
 var reversedDefaultLocalTidyChecks = reverseTidyChecks(DefaultLocalTidyChecks)
@@ -191,6 +201,13 @@
 	return tidyDefault
 }
 
+func NoClangTidyForDir(dir string) bool {
+	// This function depends on TidyChecksForDir, which selects tidyExternalVendor
+	// checks for external/vendor projects. For those projects we disable clang-tidy
+	// by default, unless some modules enable clang-tidy with tidy:true.
+	return TidyChecksForDir(dir) == tidyExternalVendor
+}
+
 // Returns a globally disabled tidy checks, overriding locally selected checks.
 func TidyGlobalNoChecks() string {
 	if len(globalNoCheckList) > 0 {
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 253bb06..d9eaf53 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -140,14 +140,6 @@
 	return ""
 }
 
-func (toolchainBase) ShlibSuffix() string {
-	return ".so"
-}
-
-func (toolchainBase) ExecutableSuffix() string {
-	return ""
-}
-
 func (toolchainBase) Asflags() string {
 	return ""
 }
@@ -160,16 +152,14 @@
 	return ""
 }
 
-func (toolchainBase) AvailableLibraries() []string {
-	return nil
-}
+type toolchainNoCrt struct{}
 
-func (toolchainBase) CrtBeginStaticBinary() []string  { return nil }
-func (toolchainBase) CrtBeginSharedBinary() []string  { return nil }
-func (toolchainBase) CrtBeginSharedLibrary() []string { return nil }
-func (toolchainBase) CrtEndStaticBinary() []string    { return nil }
-func (toolchainBase) CrtEndSharedBinary() []string    { return nil }
-func (toolchainBase) CrtEndSharedLibrary() []string   { return nil }
+func (toolchainNoCrt) CrtBeginStaticBinary() []string  { return nil }
+func (toolchainNoCrt) CrtBeginSharedBinary() []string  { return nil }
+func (toolchainNoCrt) CrtBeginSharedLibrary() []string { return nil }
+func (toolchainNoCrt) CrtEndStaticBinary() []string    { return nil }
+func (toolchainNoCrt) CrtEndSharedBinary() []string    { return nil }
+func (toolchainNoCrt) CrtEndSharedLibrary() []string   { return nil }
 
 func (toolchainBase) DefaultSharedLibraries() []string {
 	return nil
@@ -188,7 +178,6 @@
 }
 
 type toolchain64Bit struct {
-	toolchainBase
 }
 
 func (toolchain64Bit) Is64Bit() bool {
@@ -196,7 +185,6 @@
 }
 
 type toolchain32Bit struct {
-	toolchainBase
 }
 
 func (toolchain32Bit) Is64Bit() bool {
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 4e8fd77..1b126de 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -158,6 +158,7 @@
 }
 
 type toolchainLinux struct {
+	toolchainBase
 	cFlags, ldFlags string
 }
 
@@ -247,9 +248,18 @@
 	return linuxAvailableLibraries
 }
 
+func (toolchainLinux) ShlibSuffix() string {
+	return ".so"
+}
+
+func (toolchainLinux) ExecutableSuffix() string {
+	return ""
+}
+
 // glibc specialization of the linux toolchain
 
 type toolchainGlibc struct {
+	toolchainNoCrt
 }
 
 func (toolchainGlibc) Glibc() bool { return true }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 2c83211..a33606f 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -153,6 +153,8 @@
 
 type toolchainWindows struct {
 	cFlags, ldFlags string
+	toolchainBase
+	toolchainNoCrt
 }
 
 type toolchainWindowsX86 struct {
diff --git a/cc/fuzz.go b/cc/fuzz.go
index dfc718e..8a8c107 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -152,10 +152,10 @@
 // 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:
-//  - The module is not an installable shared library, or
-//  - The module is a header or stub, or
-//  - The module is a prebuilt and its source is available, or
-//  - The module is a versioned member of an SDK snapshot.
+//   - The module is not an installable shared library, or
+//   - The module is a header or stub, or
+//   - The module is a prebuilt and its source is available, or
+//   - The module is a versioned member of an SDK snapshot.
 func IsValidSharedDependency(dependency android.Module) bool {
 	// TODO(b/144090547): We should be parsing these modules using
 	// ModuleDependencyTag instead of the current brute-force checking.
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 40a5716..85df333 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -74,4 +74,26 @@
 
 	})
 
+	t.Run("sysprop", func(t *testing.T) {
+		ctx := testCc(t, `
+		cc_library {
+			name: "libsysprop",
+			srcs: [
+				"path/to/foo.sysprop",
+			],
+		}`)
+
+		outDir := "out/soong/.intermediates/libsysprop/android_arm64_armv8-a_static/gen"
+		syspropBuildParams := ctx.ModuleForTests("libsysprop", "android_arm64_armv8-a_static").Rule("sysprop")
+
+		android.AssertStringEquals(t, "header output directory does not match", outDir+"/sysprop/include/path/to", syspropBuildParams.Args["headerOutDir"])
+		android.AssertStringEquals(t, "public output directory does not match", outDir+"/sysprop/public/include/path/to", syspropBuildParams.Args["publicOutDir"])
+		android.AssertStringEquals(t, "src output directory does not match", outDir+"/sysprop/path/to", syspropBuildParams.Args["srcOutDir"])
+		android.AssertStringEquals(t, "output include name does not match", "path/to/foo.sysprop.h", syspropBuildParams.Args["includeName"])
+		android.AssertStringEquals(t, "Input file does not match", "path/to/foo.sysprop", syspropBuildParams.Input.String())
+		android.AssertStringEquals(t, "Output file does not match", outDir+"/sysprop/path/to/foo.sysprop.cpp", syspropBuildParams.Output.String())
+		android.AssertStringListContains(t, "Implicit outputs does not contain header file", syspropBuildParams.ImplicitOutputs.Strings(), outDir+"/sysprop/include/path/to/foo.sysprop.h")
+		android.AssertStringListContains(t, "Implicit outputs does not contain public header file", syspropBuildParams.ImplicitOutputs.Strings(), outDir+"/sysprop/public/include/path/to/foo.sysprop.h")
+		android.AssertIntEquals(t, "Implicit outputs contains the incorrect number of elements", 2, len(syspropBuildParams.ImplicitOutputs.Strings()))
+	})
 }
diff --git a/cc/genrule.go b/cc/genrule.go
index 239064f..4ef990c 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -41,13 +41,13 @@
 // variations.  The following environment variables will be set when the command
 // execute:
 //
-//   CC_ARCH           the name of the architecture the command is being executed for
+//	CC_ARCH           the name of the architecture the command is being executed for
 //
-//   CC_MULTILIB       "lib32" if the architecture the command is being executed for is 32-bit,
-//                     "lib64" if it is 64-bit.
+//	CC_MULTILIB       "lib32" if the architecture the command is being executed for is 32-bit,
+//	                  "lib64" if it is 64-bit.
 //
-//   CC_NATIVE_BRIDGE  the name of the subdirectory that native bridge libraries are stored in if
-//                     the architecture has native bridge enabled, empty if it is disabled.
+//	CC_NATIVE_BRIDGE  the name of the subdirectory that native bridge libraries are stored in if
+//	                  the architecture has native bridge enabled, empty if it is disabled.
 func GenRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
diff --git a/cc/image.go b/cc/image.go
index 921b2bb..e65a9aa 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/cc/library.go b/cc/library.go
index 546982b..8804bbb 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -270,6 +270,15 @@
 	Features bazel.StringListAttribute
 }
 
+type aidlLibraryAttributes struct {
+	Srcs        bazel.LabelListAttribute
+	Include_dir *string
+}
+
+type ccAidlLibraryAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
 type stripAttributes struct {
 	Keep_symbols                 bazel.BoolAttribute
 	Keep_symbols_and_debug_frame bazel.BoolAttribute
@@ -282,7 +291,7 @@
 	// For some cc_library modules, their static variants are ready to be
 	// converted, but not their shared variants. For these modules, delegate to
 	// the cc_library_static bp2build converter temporarily instead.
-	if android.GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
+	if android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
 		sharedOrStaticLibraryBp2Build(ctx, m, true)
 		return
 	}
@@ -319,6 +328,7 @@
 		Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
 		Whole_archive_deps:                *linkerAttrs.wholeArchiveDeps.Clone().Append(staticAttrs.Whole_archive_deps),
 		System_dynamic_deps:               *linkerAttrs.systemDynamicDeps.Clone().Append(staticAttrs.System_dynamic_deps),
+		Runtime_deps:                      linkerAttrs.runtimeDeps,
 		sdkAttributes:                     bp2BuildParseSdkAttributes(m),
 	}
 
@@ -335,6 +345,7 @@
 		Implementation_dynamic_deps: *linkerAttrs.implementationDynamicDeps.Clone().Append(sharedAttrs.Implementation_dynamic_deps),
 		Whole_archive_deps:          *linkerAttrs.wholeArchiveDeps.Clone().Append(sharedAttrs.Whole_archive_deps),
 		System_dynamic_deps:         *linkerAttrs.systemDynamicDeps.Clone().Append(sharedAttrs.System_dynamic_deps),
+		Runtime_deps:                linkerAttrs.runtimeDeps,
 		sdkAttributes:               bp2BuildParseSdkAttributes(m),
 	}
 
@@ -357,7 +368,7 @@
 		C_std:                    compilerAttrs.cStd,
 		Use_version_lib:          linkerAttrs.useVersionLib,
 
-		Features: linkerAttrs.features,
+		Features: baseAttributes.features,
 	}
 
 	sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
@@ -389,12 +400,16 @@
 			All:                          linkerAttrs.stripAll,
 			None:                         linkerAttrs.stripNone,
 		},
-		Features: linkerAttrs.features,
-
-		Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
-		Stubs_versions:    compilerAttrs.stubsVersions,
+		Features: baseAttributes.features,
 	}
 
+	if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
+		hasStubs := true
+		sharedTargetAttrs.Has_stubs.SetValue(&hasStubs)
+	}
+
+	sharedTargetAttrs.Suffix = compilerAttrs.suffix
+
 	for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
 		for config, props := range configToProps {
 			if props, ok := props.(*LibraryProperties); ok {
@@ -425,6 +440,25 @@
 	ctx.CreateBazelTargetModuleWithRestrictions(sharedProps,
 		android.CommonAttributes{Name: m.Name()},
 		sharedTargetAttrs, sharedAttrs.Enabled)
+
+	if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
+		stubSuitesProps := bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_stub_suite",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_stub_library.bzl",
+		}
+		soname := m.Name() + ".so"
+		stubSuitesAttrs := &bazelCcStubSuiteAttributes{
+			Symbol_file:     compilerAttrs.stubsSymbolFile,
+			Versions:        compilerAttrs.stubsVersions,
+			Export_includes: exportedIncludes.Includes,
+			Soname:          &soname,
+			Source_library:  *bazel.MakeLabelAttribute(":" + m.Name()),
+			Deps:            baseAttributes.deps,
+		}
+		ctx.CreateBazelTargetModule(stubSuitesProps,
+			android.CommonAttributes{Name: m.Name() + "_stub_libs"},
+			stubSuitesAttrs)
+	}
 }
 
 // cc_library creates both static and/or shared libraries for a device and/or
@@ -613,6 +647,9 @@
 	// Source Abi Diff
 	sAbiDiff android.OptionalPath
 
+	// Source Abi Diff against previous SDK version
+	prevSAbiDiff android.OptionalPath
+
 	// Location of the static library in the sysroot. Empty if the library is
 	// not included in the NDK.
 	ndkSysrootPath android.Path
@@ -771,6 +808,12 @@
 		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
 			continue
 		}
+
+		// Filter out the generated headers from bazel.
+		if strings.HasPrefix(dir, android.PathForBazelOut(ctx, "bazel-out").String()) {
+			continue
+		}
+
 		// libeigen wrongly exports the root directory "external/eigen". But only two
 		// subdirectories "Eigen" and "unsupported" contain exported header files. Even worse
 		// some of them have no extension. So we need special treatment for libeigen in order
@@ -1039,9 +1082,7 @@
 		if ctx.Module().(android.ApexModule).NotInPlatform() {
 			flag = "--apex"
 		} else {
-			// TODO(b/239274367) drop --apex when #apex is replaced with #systemapi
-			// in the map.txt files of platform libraries
-			flag = "--systemapi --apex"
+			flag = "--systemapi"
 		}
 		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
 			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
@@ -1591,13 +1632,48 @@
 	return library.coverageOutputFile
 }
 
+// pathForVndkRefAbiDump returns an OptionalPath representing the path of the
+// reference abi dump for the given module. This is not guaranteed to be valid.
+func pathForVndkRefAbiDump(ctx android.ModuleInstallPathContext, version, fileName string,
+	isNdk, isVndk, isGzip bool) android.OptionalPath {
+
+	currentArchType := ctx.Arch().ArchType
+	primaryArchType := ctx.Config().DevicePrimaryArchType()
+	archName := currentArchType.String()
+	if currentArchType != primaryArchType {
+		archName += "_" + primaryArchType.String()
+	}
+
+	var dirName string
+	if isNdk {
+		dirName = "ndk"
+	} else if isVndk {
+		dirName = "vndk"
+	} else {
+		dirName = "platform" // opt-in libs
+	}
+
+	binderBitness := ctx.DeviceConfig().BinderBitness()
+
+	var ext string
+	if isGzip {
+		ext = ".lsdump.gz"
+	} else {
+		ext = ".lsdump"
+	}
+
+	return android.ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
+		version, binderBitness, archName, "source-based",
+		fileName+ext)
+}
+
 func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
 	// The logic must be consistent with classifySourceAbiDump.
 	isNdk := ctx.isNdk(ctx.Config())
 	isVndk := ctx.useVndk() && ctx.isVndk()
 
-	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, false)
-	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, true)
+	refAbiDumpTextFile := pathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, false)
+	refAbiDumpGzipFile := pathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, true)
 
 	if refAbiDumpTextFile.Valid() {
 		if refAbiDumpGzipFile.Valid() {
@@ -1614,9 +1690,39 @@
 	return nil
 }
 
+func prevDumpRefVersion(ctx ModuleContext) int {
+	sdkVersionInt := ctx.Config().PlatformSdkVersion().FinalInt()
+	sdkVersionStr := ctx.Config().PlatformSdkVersion().String()
+
+	if ctx.Config().PlatformSdkFinal() {
+		return sdkVersionInt - 1
+	} else {
+		var dirName string
+
+		isNdk := ctx.isNdk(ctx.Config())
+		if isNdk {
+			dirName = "ndk"
+		} else {
+			dirName = "platform"
+		}
+
+		// The platform SDK version can be upgraded before finalization while the corresponding abi dumps hasn't
+		// been generated. Thus the Cross-Version Check chooses PLATFORM_SDK_VERION - 1 as previous version.
+		// This situation could be identified by checking the existence of the PLATFORM_SDK_VERION dump directory.
+		refDumpDir := android.ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName, sdkVersionStr)
+		if refDumpDir.Valid() {
+			return sdkVersionInt
+		} else {
+			return sdkVersionInt - 1
+		}
+	}
+}
+
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
 	if library.sabi.shouldCreateSourceAbiDump() {
 		var version string
+		var prevVersion int
+
 		if ctx.useVndk() {
 			// For modules linking against vndk, follow its vndk version
 			version = ctx.Module().(*Module).VndkVersion()
@@ -1628,6 +1734,7 @@
 			} else {
 				version = "current"
 			}
+			prevVersion = prevDumpRefVersion(ctx)
 		}
 
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
@@ -1646,13 +1753,26 @@
 
 		addLsdumpPath(classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
 
+		// If NDK or PLATFORM library, check against previous version ABI.
+		if !ctx.useVndk() {
+			prevRefAbiDumpFile := getRefAbiDumpFile(ctx, strconv.Itoa(prevVersion), fileName)
+			if prevRefAbiDumpFile != nil {
+				library.prevSAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
+					prevRefAbiDumpFile, fileName, exportedHeaderFlags,
+					library.Properties.Header_abi_checker.Diff_flags, prevVersion,
+					Bool(library.Properties.Header_abi_checker.Check_all_apis),
+					ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt(), true)
+			}
+		}
+
 		refAbiDumpFile := getRefAbiDumpFile(ctx, version, fileName)
 		if refAbiDumpFile != nil {
 			library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
 				refAbiDumpFile, fileName, exportedHeaderFlags,
 				library.Properties.Header_abi_checker.Diff_flags,
+				/* unused if not previousVersionDiff */ 0,
 				Bool(library.Properties.Header_abi_checker.Check_all_apis),
-				ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
+				ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt(), false)
 		}
 	}
 }
@@ -2501,6 +2621,7 @@
 		Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
 		System_dynamic_deps:               linkerAttrs.systemDynamicDeps,
 		sdkAttributes:                     bp2BuildParseSdkAttributes(module),
+		Runtime_deps:                      linkerAttrs.runtimeDeps,
 	}
 
 	var attrs interface{}
@@ -2527,12 +2648,12 @@
 			Conlyflags: compilerAttrs.conlyFlags,
 			Asflags:    asFlags,
 
-			Features: linkerAttrs.features,
+			Features: baseAttributes.features,
 		}
 	} else {
 		commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
 
-		attrs = &bazelCcLibrarySharedAttributes{
+		sharedLibAttrs := &bazelCcLibrarySharedAttributes{
 			staticOrSharedAttributes: commonAttrs,
 
 			Cppflags:   compilerAttrs.cppFlags,
@@ -2564,11 +2685,15 @@
 				None:                         linkerAttrs.stripNone,
 			},
 
-			Features: linkerAttrs.features,
+			Features: baseAttributes.features,
 
-			Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
-			Stubs_versions:    compilerAttrs.stubsVersions,
+			Suffix: compilerAttrs.suffix,
 		}
+		if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
+			hasStubs := true
+			sharedLibAttrs.Has_stubs.SetValue(&hasStubs)
+		}
+		attrs = sharedLibAttrs
 	}
 
 	var modType string
@@ -2642,7 +2767,18 @@
 
 	Features bazel.StringListAttribute
 
-	Stubs_symbol_file *string
-	Stubs_versions    bazel.StringListAttribute
-	Inject_bssl_hash  bazel.BoolAttribute
+	Has_stubs bazel.BoolAttribute
+
+	Inject_bssl_hash bazel.BoolAttribute
+
+	Suffix bazel.StringAttribute
+}
+
+type bazelCcStubSuiteAttributes struct {
+	Symbol_file     *string
+	Versions        bazel.StringListAttribute
+	Export_includes bazel.StringListAttribute
+	Source_library  bazel.LabelAttribute
+	Soname          *string
+	Deps            bazel.LabelListAttribute
 }
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 970d8d1..a683f58 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -126,6 +126,7 @@
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &baseAttributes.includes)
 	linkerAttrs := baseAttributes.linkerAttributes
 	(&linkerAttrs.deps).Append(linkerAttrs.dynamicDeps)
+	(&linkerAttrs.deps).Append(linkerAttrs.wholeArchiveDeps)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
 		Export_includes:          exportedIncludes.Includes,
diff --git a/cc/library_stub.go b/cc/library_stub.go
index 14d1b96..2ebb6ef 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -17,7 +17,6 @@
 import (
 	"android/soong/android"
 	"android/soong/multitree"
-	"strings"
 )
 
 func init() {
@@ -25,8 +24,10 @@
 }
 
 func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
-	// cc_api_stub_library shares a lot of ndk_library, and this will be refactored later
 	ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
+	ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
+
+	// cc_api_stub_library shares a lot of ndk_library, and this will be refactored later
 	ctx.RegisterModuleType("cc_api_stub_library", CcApiStubLibraryFactory)
 	ctx.RegisterModuleType("cc_api_contribution", CcApiContributionFactory)
 }
@@ -68,6 +69,9 @@
 		apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
 	}
 
+	apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
+	apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
+
 	module.Init()
 
 	return module
@@ -78,6 +82,14 @@
 }
 
 func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
+	// Export headers as system include dirs if specified. Mostly for libc
+	if Bool(d.libraryDecorator.Properties.Llndk.Export_headers_as_system) {
+		d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
+			d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
+			d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+		d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+	}
+
 	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
 	d.libraryDecorator.flagExporter.exportIncludes(ctx)
 	d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
@@ -111,9 +123,51 @@
 	return true
 }
 
-func (d *apiLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	d.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), multitree.GetApiImportSuffix())
-	return d.libraryDecorator.linkerFlags(ctx, flags)
+// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
+// header libraries. The module will replace any dependencies to existing
+// original header libraries.
+type apiHeadersDecorator struct {
+	*libraryDecorator
+}
+
+func CcApiHeadersFactory() android.Module {
+	module, decorator := NewLibrary(android.DeviceSupported)
+	apiHeadersDecorator := &apiHeadersDecorator{
+		libraryDecorator: decorator,
+	}
+	apiHeadersDecorator.HeaderOnly()
+
+	module.stl = nil
+	module.sanitize = nil
+	decorator.disableStripping()
+
+	module.compiler = nil
+	module.linker = apiHeadersDecorator
+	module.installer = nil
+
+	// Mark module as stub, so APEX would not include this stub in the package.
+	module.library.setBuildStubs(true)
+
+	// Prevent default system libs (libc, libm, and libdl) from being linked
+	if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
+		apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
+	}
+
+	apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
+	apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+	module.Init()
+
+	return module
+}
+
+func (d *apiHeadersDecorator) Name(basename string) string {
+	return basename + multitree.GetApiImportSuffix()
+}
+
+func (d *apiHeadersDecorator) availableFor(what string) bool {
+	// Stub from API surface should be available for any APEX.
+	return true
 }
 
 func CcApiStubLibraryFactory() android.Module {
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
index c2ac941..cd06172 100644
--- a/cc/library_stub_test.go
+++ b/cc/library_stub_test.go
@@ -216,3 +216,112 @@
 	android.AssertBoolEquals(t, "original library should be linked", true, hasDirectDependency(t, ctx, libfoo, libbar))
 	android.AssertBoolEquals(t, "Stub library from API surface should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
 }
+
+func TestApiHeaderReplacesExistingModule(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			header_libs: ["libfoo_headers"],
+		}
+
+		cc_api_library {
+			name: "libfoo",
+			header_libs: ["libfoo_headers"],
+			src: "libfoo.so",
+		}
+
+		cc_library_headers {
+			name: "libfoo_headers",
+		}
+
+		cc_api_headers {
+			name: "libfoo_headers",
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libfoo",
+			],
+			header_libs: [
+				"libfoo_headers",
+			],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libfooApiImport := ctx.ModuleForTests("libfoo.apiimport", "android_arm64_armv8-a_shared").Module()
+	libfooHeader := ctx.ModuleForTests("libfoo_headers", "android_arm64_armv8-a").Module()
+	libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
+
+	android.AssertBoolEquals(t, "original header should not be used for original library", false, hasDirectDependency(t, ctx, libfoo, libfooHeader))
+	android.AssertBoolEquals(t, "Header from API surface should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
+	android.AssertBoolEquals(t, "original header should not be used for library imported from API surface", false, hasDirectDependency(t, ctx, libfooApiImport, libfooHeader))
+	android.AssertBoolEquals(t, "Header from API surface should be used for library imported from API surface", true, hasDirectDependency(t, ctx, libfooApiImport, libfooHeaderApiImport))
+}
+
+func TestApiHeadersDoNotRequireOriginalModule(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libfoo",
+		header_libs: ["libfoo_headers"],
+	}
+
+	cc_api_headers {
+		name: "libfoo_headers",
+	}
+
+	api_imports {
+		name: "api_imports",
+		shared_libs: [
+			"libfoo",
+		],
+		header_libs: [
+			"libfoo_headers",
+		],
+	}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
+
+	android.AssertBoolEquals(t, "Header from API surface should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
+}
+
+func TestApiHeadersShouldNotReplaceWithoutApiImport(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			header_libs: ["libfoo_headers"],
+		}
+
+		cc_library_headers {
+			name: "libfoo_headers",
+		}
+
+		cc_api_headers {
+			name: "libfoo_headers",
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libfoo",
+			],
+			header_libs: [],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libfooHeader := ctx.ModuleForTests("libfoo_headers", "android_arm64_armv8-a").Module()
+	libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
+
+	android.AssertBoolEquals(t, "original header should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeader))
+	android.AssertBoolEquals(t, "Header from API surface should not be used for original library", false, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
+}
diff --git a/cc/lto.go b/cc/lto.go
index 2c274bd..b3e5e74 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -101,6 +101,7 @@
 		}
 
 		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlag)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag)
 
@@ -136,9 +137,17 @@
 }
 
 func (lto *lto) DefaultThinLTO(ctx BaseModuleContext) bool {
+	// LP32 has many subtle issues and less test coverage.
+	lib32 := ctx.Arch().ArchType.Multilib == "lib32"
+	// CFI enables full LTO.
+	cfi := ctx.isCfi()
+	// Performance and binary size are less important for host binaries and tests.
 	host := ctx.Host()
-	vndk := ctx.isVndk() // b/169217596
-	return GlobalThinLTO(ctx) && !lto.Never() && !host && !vndk
+	test := ctx.testBinary() || ctx.testLibrary()
+	// FIXME: ThinLTO for VNDK produces different output.
+	// b/169217596
+	vndk := ctx.isVndk()
+	return GlobalThinLTO(ctx) && !lto.Never() && !lib32 && !cfi && !host && !test && !vndk
 }
 
 func (lto *lto) FullLTO() bool {
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 56fd5fc..5e06948 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -19,8 +19,10 @@
 	"path/filepath"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 )
 
 var (
@@ -79,6 +81,7 @@
 
 type headerModule struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties headerProperties
 
@@ -144,20 +147,62 @@
 	}
 }
 
+const (
+	apiContributionSuffix = ".contribution"
+)
+
+// apiContributionTargetName returns the name of the cc_api(headers|contribution) bp2build target of ndk modules
+// A suffix is necessary to prevent a name collision with the base ndk_(library|header) target in the same bp2build bazel package
+func apiContributionTargetName(moduleName string) string {
+	return moduleName + apiContributionSuffix
+}
+
+// TODO(b/243196151): Populate `system` and `arch` metadata
+type bazelCcApiHeadersAttributes struct {
+	Hdrs        bazel.LabelListAttribute
+	Include_dir *string
+}
+
+func createCcApiHeadersTarget(ctx android.TopDownMutatorContext, includes []string, excludes []string, include_dir *string) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_api_headers",
+		Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+	}
+	attrs := &bazelCcApiHeadersAttributes{
+		Hdrs: bazel.MakeLabelListAttribute(
+			android.BazelLabelForModuleSrcExcludes(
+				ctx,
+				includes,
+				excludes,
+			),
+		),
+		Include_dir: include_dir,
+	}
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: apiContributionTargetName(ctx.ModuleName()),
+	}, attrs)
+}
+
+func (h *headerModule) ConvertWithBp2build(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)
+}
+
 // ndk_headers installs the sets of ndk headers defined in the srcs property
 // to the sysroot base + "usr/include" + to directory + directory component.
 // ndk_headers requires the license file to be specified. Example:
 //
-//    Given:
-//    sysroot base = "ndk/sysroot"
-//    from = "include/foo"
-//    to = "bar"
-//    header = "include/foo/woodly/doodly.h"
-//    output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
+//	Given:
+//	sysroot base = "ndk/sysroot"
+//	from = "include/foo"
+//	to = "bar"
+//	header = "include/foo/woodly/doodly.h"
+//	output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
 func ndkHeadersFactory() android.Module {
 	module := &headerModule{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -190,6 +235,7 @@
 // Note that this is really only built to handle bionic/libc/include.
 type versionedHeaderModule struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties versionedHeaderProperties
 
@@ -197,6 +243,11 @@
 	licensePath  android.Path
 }
 
+// Return the glob pattern to find all .h files beneath `dir`
+func headerGlobPattern(dir string) string {
+	return filepath.Join(dir, "**", "*.h")
+}
+
 func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if String(m.properties.License) == "" {
 		ctx.PropertyErrorf("license", "field is required")
@@ -206,7 +257,7 @@
 
 	fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From))
 	toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
-	srcFiles := ctx.GlobFiles(filepath.Join(fromSrcPath.String(), "**/*.h"), nil)
+	srcFiles := ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
 	var installPaths []android.WritablePath
 	for _, header := range srcFiles {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
@@ -222,6 +273,13 @@
 	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
 }
 
+func (h *versionedHeaderModule) ConvertWithBp2build(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
+	createCcApiHeadersTarget(ctx, []string{includePattern}, []string{}, h.properties.From)
+}
+
 func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
 	srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
 	// The versioner depends on a dependencies directory to simplify determining include paths
@@ -271,19 +329,24 @@
 	module.AddProperties(&module.properties)
 
 	android.InitAndroidModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
 
 // preprocessed_ndk_header {
-//     name: "foo",
-//     preprocessor: "foo.sh",
-//     srcs: [...],
-//     to: "android",
+//
+//	name: "foo",
+//	preprocessor: "foo.sh",
+//	srcs: [...],
+//	to: "android",
+//
 // }
 //
 // Will invoke the preprocessor as:
-//     $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src
+//
+//	$preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src
+//
 // For each src in srcs.
 type preprocessedHeadersProperties struct {
 	// The preprocessor to run. Must be a program inside the source directory
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 0879257..e2b9682 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -85,11 +85,12 @@
 // Example:
 //
 // ndk_library {
-//     name: "libfoo",
-//     symbol_file: "libfoo.map.txt",
-//     first_version: "9",
-// }
 //
+//	name: "libfoo",
+//	symbol_file: "libfoo.map.txt",
+//	first_version: "9",
+//
+// }
 type libraryProperties struct {
 	// Relative path to the symbol map.
 	// An example file can be seen here: TODO(danalbert): Make an example.
@@ -110,6 +111,9 @@
 	// where it is enabled pending a fix for http://b/190554910 (no debug info
 	// for asm implemented symbols).
 	Allow_untyped_symbols *bool
+
+	// Headers presented by this library to the Public API Surface
+	Export_header_libs []string
 }
 
 type stubDecorator struct {
@@ -484,8 +488,11 @@
 	return objs
 }
 
+// Add a dependency on the header modules of this ndk_library
 func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	return Deps{}
+	return Deps{
+		HeaderLibs: linker.properties.Export_header_libs,
+	}
 }
 
 func (linker *stubDecorator) Name(name string) string {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 6c200f5..622558e 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -57,15 +57,18 @@
 )
 
 func init() {
-	android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
-	android.RegisterModuleType("ndk_library", NdkLibraryFactory)
-	android.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
-	android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
-	android.RegisterSingletonType("ndk", NdkSingleton)
-
+	RegisterNdkModuleTypes(android.InitRegistrationContext)
 	pctx.Import("android/soong/android")
 }
 
+func RegisterNdkModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("ndk_headers", ndkHeadersFactory)
+	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
+	ctx.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
+	ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
+	ctx.RegisterSingletonType("ndk", NdkSingleton)
+}
+
 func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
 	return android.PathForNdkInstall(ctx)
 }
diff --git a/cc/ndk_test.go b/cc/ndk_test.go
new file mode 100644
index 0000000..f20d3c6
--- /dev/null
+++ b/cc/ndk_test.go
@@ -0,0 +1,56 @@
+// 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 cc
+
+import (
+	"testing"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+func TestNdkHeaderDependency(t *testing.T) {
+	isDep := func(ctx *android.TestResult, from, toExpected android.Module) bool {
+		foundDep := false
+		ctx.VisitDirectDeps(from, func(toActual blueprint.Module) {
+			if toExpected.Name() == toActual.Name() {
+				foundDep = true
+			}
+		})
+		return foundDep
+	}
+	bp := `
+	ndk_library {
+		name: "libfoo",
+		first_version: "29",
+		symbol_file: "libfoo.map.txt",
+		export_header_libs: ["libfoo_headers"],
+	}
+	ndk_headers {
+		name: "libfoo_headers",
+		srcs: ["foo.h"],
+		license: "NOTICE",
+	}
+	//This module is needed since Soong creates a dep edge on source
+	cc_library {
+		name: "libfoo",
+	}
+	`
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+	libfoo := ctx.ModuleForTests("libfoo.ndk", "android_arm64_armv8-a_sdk_shared")
+	libfoo_headers := ctx.ModuleForTests("libfoo_headers", "")
+	android.AssertBoolEquals(t, "Could not find headers of ndk_library", true, isDep(ctx, libfoo.Module(), libfoo_headers.Module()))
+}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 8c404d3..867c36c 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -51,6 +51,9 @@
 	// symbols, etc), default true.
 	Check_elf_files *bool
 
+	// if set, add an extra objcopy --prefix-symbols= step
+	Prefix_symbols *string
+
 	// Optionally provide an import library if this is a Windows PE DLL prebuilt.
 	// This is needed only if this library is linked by other modules in build time.
 	// Only makes sense for the Windows target.
@@ -130,6 +133,13 @@
 
 		in := android.PathForModuleSrc(ctx, srcs[0])
 
+		if String(p.prebuiltLinker.properties.Prefix_symbols) != "" {
+			prefixed := android.PathForModuleOut(ctx, "prefixed", srcs[0])
+			transformBinaryPrefixSymbols(ctx, String(p.prebuiltLinker.properties.Prefix_symbols),
+				in, flagsToBuilderFlags(flags), prefixed)
+			in = prefixed
+		}
+
 		if p.static() {
 			depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
 			ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
@@ -348,10 +358,10 @@
 
 // TODO(b/228623543): The below is not entirely true until the bug is fixed. For now, both targets are always generated
 // Implements bp2build for cc_prebuilt_library modules. This will generate:
-// * Only a prebuilt_library_static if the shared.enabled property is set to false across all variants.
-// * Only a prebuilt_library_shared if the static.enabled property is set to false across all variants
-// * Both a prebuilt_library_static and prebuilt_library_shared if the aforementioned properties are not false across
-//   all variants
+//   - Only a prebuilt_library_static if the shared.enabled property is set to false across all variants.
+//   - Only a prebuilt_library_shared if the static.enabled property is set to false across all variants
+//   - Both a prebuilt_library_static and prebuilt_library_shared if the aforementioned properties are not false across
+//     all variants
 //
 // In all cases, prebuilt_library_static target names will be appended with "_bp2build_cc_library_static".
 func prebuiltLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 86472a2..436b149 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -77,6 +77,7 @@
 		"-fno-sanitize-recover=integer,undefined"}
 	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
 		"export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
+	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
 )
 
 type SanitizerType int
@@ -89,6 +90,7 @@
 	scs
 	Fuzzer
 	Memtag_heap
+	Memtag_stack
 	cfi // cfi is last to prevent it running before incompatible mutators
 )
 
@@ -100,6 +102,7 @@
 	scs,
 	Fuzzer,
 	Memtag_heap,
+	Memtag_stack,
 	cfi, // cfi is last to prevent it running before incompatible mutators
 }
 
@@ -120,6 +123,8 @@
 		return "scs"
 	case Memtag_heap:
 		return "memtag_heap"
+	case Memtag_stack:
+		return "memtag_stack"
 	case Fuzzer:
 		return "fuzzer"
 	default:
@@ -136,6 +141,8 @@
 		return "hwaddress"
 	case Memtag_heap:
 		return "memtag_heap"
+	case Memtag_stack:
+		return "memtag_stack"
 	case tsan:
 		return "thread"
 	case intOverflow:
@@ -157,7 +164,7 @@
 		sanitizer := &sanitizerSplitMutator{t}
 		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
-	case Memtag_heap, intOverflow:
+	case Memtag_heap, Memtag_stack, intOverflow:
 		// do nothing
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
@@ -182,6 +189,8 @@
 		return true
 	case Memtag_heap:
 		return true
+	case Memtag_stack:
+		return true
 	default:
 		return false
 	}
@@ -233,6 +242,9 @@
 	// Memory-tagging, only available on arm64
 	// if diag.memtag unset or false, enables async memory tagging
 	Memtag_heap *bool `android:"arch_variant"`
+	// Memory-tagging stack instrumentation, only available on arm64
+	// Adds instrumentation to detect stack buffer overflows and use-after-scope using MTE.
+	Memtag_stack *bool `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -318,7 +330,7 @@
 		return
 	}
 
-	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
+	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap: false}).
 	if ctx.testBinary() {
 		if s.Memtag_heap == nil {
 			s.Memtag_heap = proptools.BoolPtr(true)
@@ -404,6 +416,10 @@
 			}
 		}
 
+		if found, globalSanitizers = removeFromList("memtag_stack", globalSanitizers); found && s.Memtag_stack == nil {
+			s.Memtag_stack = proptools.BoolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -470,14 +486,19 @@
 	}
 
 	// Memtag_heap is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
+	// Memtag ABI is Android specific for now, so disable for host.
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() || ctx.Host() {
 		s.Memtag_heap = nil
+		s.Memtag_stack = nil
 	}
 
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
 		s.Cfi = nil
 		s.Diag.Cfi = nil
+		// HWASAN and ASAN win against MTE.
+		s.Memtag_heap = nil
+		s.Memtag_stack = nil
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
@@ -534,7 +555,7 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
@@ -691,6 +712,25 @@
 		}
 	}
 
+	if Bool(sanitize.Properties.Sanitize.Memtag_stack) {
+		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
+		// TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain.
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than")
+		flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
+		// This works around LLD complaining about the stack frame size.
+		// TODO(fmayer): remove once https://reviews.llvm.org/D127917 is in Android toolchain.
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
+	}
+
+	if (Bool(sanitize.Properties.Sanitize.Memtag_heap) || Bool(sanitize.Properties.Sanitize.Memtag_stack)) && ctx.binary() {
+		if Bool(sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
+		} else {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=async")
+		}
+	}
+
 	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
 		flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 	}
@@ -804,6 +844,8 @@
 		return sanitize.Properties.Sanitize.Scs
 	case Memtag_heap:
 		return sanitize.Properties.Sanitize.Memtag_heap
+	case Memtag_stack:
+		return sanitize.Properties.Sanitize.Memtag_stack
 	case Fuzzer:
 		return sanitize.Properties.Sanitize.Fuzzer
 	default:
@@ -819,6 +861,7 @@
 		!sanitize.isSanitizerEnabled(cfi) &&
 		!sanitize.isSanitizerEnabled(scs) &&
 		!sanitize.isSanitizerEnabled(Memtag_heap) &&
+		!sanitize.isSanitizerEnabled(Memtag_stack) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
@@ -850,6 +893,8 @@
 		sanitize.Properties.Sanitize.Scs = bPtr
 	case Memtag_heap:
 		sanitize.Properties.Sanitize.Memtag_heap = bPtr
+	case Memtag_stack:
+		sanitize.Properties.Sanitize.Memtag_stack = bPtr
 	case Fuzzer:
 		sanitize.Properties.Sanitize.Fuzzer = bPtr
 	default:
@@ -1311,23 +1356,11 @@
 		}
 
 		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() {
-			noteDep := "note_memtag_heap_async"
-			if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
-				noteDep = "note_memtag_heap_sync"
-			}
-			// If we're using snapshots, redirect to snapshot whenever possible
-			// TODO(b/178470649): clean manual snapshot redirections
-			snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
-			if lib, ok := snapshot.StaticLibs[noteDep]; ok {
-				noteDep = lib
-			}
-			depTag := StaticDepTag(true)
-			variations := append(mctx.Target().Variations(),
-				blueprint.Variation{Mutator: "link", Variation: "static"})
-			if c.Device() {
-				variations = append(variations, c.ImageVariation())
-			}
-			mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+			sanitizers = append(sanitizers, "memtag-heap")
+		}
+
+		if Bool(c.sanitize.Properties.Sanitize.Memtag_stack) {
+			sanitizers = append(sanitizers, "memtag-stack")
 		}
 
 		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 5d7e7d8..48ac650 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -344,19 +344,13 @@
 
 func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
 	t.Helper()
-	note_async := "note_memtag_heap_async"
-	note_sync := "note_memtag_heap_sync"
 
 	found := None
-	implicits := m.Rule("ld").Implicits
-	for _, lib := range implicits {
-		if strings.Contains(lib.Rel(), note_async) {
-			found = Async
-			break
-		} else if strings.Contains(lib.Rel(), note_sync) {
-			found = Sync
-			break
-		}
+	ldFlags := m.Rule("ld").Args["ldFlags"]
+	if strings.Contains(ldFlags, "-fsanitize-memtag-mode=async") {
+		found = Async
+	} else if strings.Contains(ldFlags, "-fsanitize-memtag-mode=sync") {
+		found = Sync
 	}
 
 	if found != expected {
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 9d40ad0..792ffe3 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
@@ -263,12 +263,12 @@
 // version, snapshot arch, etc. It also adds a special suffix to Soong module name, so it doesn't
 // collide with source modules. e.g. the following example module,
 //
-// vendor_snapshot_static {
-//     name: "libbase",
-//     arch: "arm64",
-//     version: 30,
-//     ...
-// }
+//	vendor_snapshot_static {
+//	    name: "libbase",
+//	    arch: "arm64",
+//	    version: 30,
+//	    ...
+//	}
 //
 // will be seen as "libbase.vendor_static.30.arm64" by Soong.
 type BaseSnapshotDecorator struct {
@@ -370,7 +370,6 @@
 	}
 }
 
-//
 // Module definitions for snapshots of libraries (shared, static, header).
 //
 // Modules (vendor|recovery)_snapshot_(shared|static|header) are defined here. Shared libraries and
@@ -630,7 +629,6 @@
 
 var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
 
-//
 // Module definitions for snapshots of executable binaries.
 //
 // Modules (vendor|recovery)_snapshot_binary are defined here. They have their prebuilt executable
@@ -728,7 +726,6 @@
 	return module.Init()
 }
 
-//
 // Module definitions for snapshots of object files (*.o).
 //
 // Modules (vendor|recovery)_snapshot_object are defined here. They have their prebuilt object
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index de50ef5..cf4617d 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/cc/test.go b/cc/test.go
index 5703571..28a0e5e 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/tradefed"
 )
 
@@ -43,6 +44,8 @@
 
 // Test option struct.
 type TestOptions struct {
+	android.CommonTestOptions
+
 	// The UID that you want to run the test as on a device.
 	Run_test_as *string
 
@@ -52,9 +55,6 @@
 	// a list of extra test configuration files that should be installed with the module.
 	Extra_test_configs []string `android:"path,arch_variant"`
 
-	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
-	Unit_test *bool
-
 	// Add ShippingApiLevelModuleController to auto generated test config. If the device properties
 	// for the shipping api level is less than the min_shipping_api_level, skip this module.
 	Min_shipping_api_level *int64
@@ -134,7 +134,7 @@
 // specific functionality on a device. The executable binary gets an implicit
 // static_libs dependency on libgtests unless the gtest flag is set to false.
 func TestFactory() android.Module {
-	module := NewTest(android.HostAndDeviceSupported)
+	module := NewTest(android.HostAndDeviceSupported, true)
 	return module.Init()
 }
 
@@ -157,7 +157,7 @@
 
 // cc_test_host compiles a test host binary.
 func TestHostFactory() android.Module {
-	module := NewTest(android.HostSupported)
+	module := NewTest(android.HostSupported, true)
 	return module.Init()
 }
 
@@ -205,6 +205,10 @@
 	test.binaryDecorator.Properties.Stem = StringPtr("")
 }
 
+func (test *testBinary) testBinary() bool {
+	return true
+}
+
 var _ testPerSrc = (*testBinary)(nil)
 
 func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
@@ -258,16 +262,10 @@
 }
 
 func (test *testDecorator) isolated(ctx BaseModuleContext) bool {
-	if !ctx.Windows() {
-		return BoolDefault(test.LinkerProperties.Isolated, false)
-	}
 	return BoolDefault(test.LinkerProperties.Isolated, false)
 }
 
-func (test *testDecorator) testBinary() bool {
-	return true
-}
-
+// NOTE: Keep this in sync with cc/cc_test.bzl#gtest_copts
 func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	if !test.gtest() {
 		return flags
@@ -481,8 +479,8 @@
 	test.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
-func NewTest(hod android.HostOrDeviceSupported) *Module {
-	module, binary := newBinary(hod, false)
+func NewTest(hod android.HostOrDeviceSupported, bazelable bool) *Module {
+	module, binary := newBinary(hod, bazelable)
 	module.multilib = android.MultilibBoth
 	binary.baseInstaller = NewTestInstaller()
 
@@ -505,6 +503,10 @@
 	*libraryDecorator
 }
 
+func (test *testLibrary) testLibrary() bool {
+	return true
+}
+
 func (test *testLibrary) linkerProps() []interface{} {
 	var props []interface{}
 	props = append(props, test.testDecorator.linkerProps()...)
@@ -544,6 +546,7 @@
 	}
 	module.linker = test
 	module.installer = test
+	module.bazelable = true
 	return module
 }
 
@@ -633,3 +636,60 @@
 	module.installer = benchmark
 	return module
 }
+
+// binaryAttributes contains Bazel attributes corresponding to a cc test
+type testBinaryAttributes struct {
+	binaryAttributes
+
+	Gtest    bool
+	Isolated bool
+}
+
+// testBinaryBp2build is the bp2build converter for cc_test modules. A cc_test's
+// dependency graph and compilation/linking steps are functionally similar to a
+// cc_binary, but has additional dependencies on test deps like gtest, and
+// produces additional runfiles like XML plans for Tradefed orchestration
+//
+// TODO(b/244432609): handle `isolated` property.
+// TODO(b/244432134): handle custom runpaths for tests that assume runfile layouts not
+// default to bazel. (see linkerInit function)
+// TODO(b/244432500): handle test.testConfig generation (see install function)
+func testBinaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+	var testBinaryAttrs testBinaryAttributes
+	testBinaryAttrs.binaryAttributes = binaryBp2buildAttrs(ctx, m)
+
+	var data bazel.LabelListAttribute
+
+	testBinaryProps := m.GetArchVariantProperties(ctx, &TestBinaryProperties{})
+	for axis, configToProps := range testBinaryProps {
+		for config, props := range configToProps {
+			if p, ok := props.(*TestBinaryProperties); ok {
+				// Combine data, data_bins and data_libs into a single 'data' attribute.
+				var combinedData bazel.LabelList
+				combinedData.Append(android.BazelLabelForModuleSrc(ctx, p.Data))
+				combinedData.Append(android.BazelLabelForModuleDeps(ctx, p.Data_bins))
+				combinedData.Append(android.BazelLabelForModuleDeps(ctx, p.Data_libs))
+				data.SetSelectValue(axis, config, combinedData)
+			}
+		}
+	}
+
+	for _, propIntf := range m.GetProperties() {
+		if testLinkerProps, ok := propIntf.(*TestLinkerProperties); ok {
+			testBinaryAttrs.Gtest = proptools.BoolDefault(testLinkerProps.Gtest, true)
+			testBinaryAttrs.Isolated = proptools.BoolDefault(testLinkerProps.Isolated, true)
+			break
+		}
+	}
+
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_test",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_test.bzl",
+		},
+		android.CommonAttributes{
+			Name: m.Name(),
+			Data: data,
+		},
+		&testBinaryAttrs)
+}
diff --git a/cc/testing.go b/cc/testing.go
index 44a865d..e42b9fa 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -41,6 +41,7 @@
 	ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
+	ctx.RegisterModuleType("ndk_headers", ndkHeadersFactory)
 }
 
 func GatherRequiredDepsForTest(oses ...android.OsType) string {
diff --git a/cc/tidy.go b/cc/tidy.go
index 6b5d572..082cf88 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -76,7 +76,11 @@
 	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) {
+		return flags
+	}
 	// If not explicitly disabled, set flags.Tidy to generate .tidy rules.
 	// Note that libraries and binaries will depend on .tidy files ONLY if
 	// the global WITH_TIDY or module 'tidy' property is true.
diff --git a/cc/vendor_public_library.go b/cc/vendor_public_library.go
index 65a2b0c..f2c8545 100644
--- a/cc/vendor_public_library.go
+++ b/cc/vendor_public_library.go
@@ -28,17 +28,16 @@
 //
 // Example:
 //
-// vendor_public_library {
-//     name: "libfoo",
-//     symbol_file: "libfoo.map.txt",
-//     export_public_headers: ["libfoo_headers"],
-// }
+//	vendor_public_library {
+//	    name: "libfoo",
+//	    symbol_file: "libfoo.map.txt",
+//	    export_public_headers: ["libfoo_headers"],
+//	}
 //
-// cc_headers {
-//     name: "libfoo_headers",
-//     export_include_dirs: ["include"],
-// }
-//
+//	cc_headers {
+//	    name: "libfoo_headers",
+//	    export_include_dirs: ["include"],
+//	}
 type vendorPublicLibraryProperties struct {
 	// Relative path to the symbol map.
 	Symbol_file *string
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index e7c05ac..77e6f6f 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 31b6d10..37819a4 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -29,26 +29,25 @@
 //
 // Example:
 //
-// vndk_prebuilt_shared {
-//     name: "libfoo",
-//     version: "27",
-//     target_arch: "arm64",
-//     vendor_available: true,
-//     product_available: true,
-//     vndk: {
-//         enabled: true,
-//     },
-//     export_include_dirs: ["include/external/libfoo/vndk_include"],
-//     arch: {
-//         arm64: {
-//             srcs: ["arm/lib64/libfoo.so"],
-//         },
-//         arm: {
-//             srcs: ["arm/lib/libfoo.so"],
-//         },
-//     },
-// }
-//
+//	vndk_prebuilt_shared {
+//	    name: "libfoo",
+//	    version: "27",
+//	    target_arch: "arm64",
+//	    vendor_available: true,
+//	    product_available: true,
+//	    vndk: {
+//	        enabled: true,
+//	    },
+//	    export_include_dirs: ["include/external/libfoo/vndk_include"],
+//	    arch: {
+//	        arm64: {
+//	            srcs: ["arm/lib64/libfoo.so"],
+//	        },
+//	        arm: {
+//	            srcs: ["arm/lib/libfoo.so"],
+//	        },
+//	    },
+//	}
 type vndkPrebuiltProperties struct {
 	// VNDK snapshot version.
 	Version *string
@@ -250,25 +249,25 @@
 // vndk_prebuilt_shared installs Vendor Native Development kit (VNDK) snapshot
 // shared libraries for system build. Example:
 //
-//    vndk_prebuilt_shared {
-//        name: "libfoo",
-//        version: "27",
-//        target_arch: "arm64",
-//        vendor_available: true,
-//        product_available: true,
-//        vndk: {
-//            enabled: true,
-//        },
-//        export_include_dirs: ["include/external/libfoo/vndk_include"],
-//        arch: {
-//            arm64: {
-//                srcs: ["arm/lib64/libfoo.so"],
-//            },
-//            arm: {
-//                srcs: ["arm/lib/libfoo.so"],
-//            },
-//        },
-//    }
+//	vndk_prebuilt_shared {
+//	    name: "libfoo",
+//	    version: "27",
+//	    target_arch: "arm64",
+//	    vendor_available: true,
+//	    product_available: true,
+//	    vndk: {
+//	        enabled: true,
+//	    },
+//	    export_include_dirs: ["include/external/libfoo/vndk_include"],
+//	    arch: {
+//	        arm64: {
+//	            srcs: ["arm/lib64/libfoo.so"],
+//	        },
+//	        arm: {
+//	            srcs: ["arm/lib/libfoo.so"],
+//	        },
+//	    },
+//	}
 func VndkPrebuiltSharedFactory() android.Module {
 	module := vndkPrebuiltSharedLibrary()
 	return module.Init()
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index f8844fc..d4a57bf 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -824,6 +824,7 @@
 
 	var regen string
 	var pom2build bool
+	var prepend string
 
 	flag.Var(&excludes, "exclude", "Exclude module")
 	flag.Var(&extraStaticLibs, "extra-static-libs", "Extra static dependencies needed when depending on a module")
@@ -839,6 +840,7 @@
 	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")
+	flag.StringVar(&prepend, "prepend", "", "Path to a file containing text to insert at the beginning of the generated build file")
 	flag.Parse()
 
 	if regen != "" {
@@ -965,6 +967,15 @@
 	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)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "Error reading", prepend, err)
+			os.Exit(1)
+		}
+		fmt.Fprintln(buf, string(contents))
+	}
+
 	depsTemplate := bpDepsTemplate
 	template := bpTemplate
 	if pom2build {
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 4f7451d..91e3540 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -208,7 +208,6 @@
 //
 // returns an exec.Cmd that can be ran from within sbox context if no error, or nil if error.
 // caller must ensure script is cleaned up if function succeeds.
-//
 func createCommandScript(rawCommand, scriptPath, scriptPathInSandbox string) (*exec.Cmd, error) {
 	err := os.WriteFile(scriptPath, []byte(rawCommand), 0644)
 	if err != nil {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 53422cd..0b8cc88 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -84,6 +84,8 @@
 	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")
+	flag.BoolVar(&cmdlineArgs.BazelMode, "bazel-mode", false, "use bazel for analysis of certain modules")
+	flag.BoolVar(&cmdlineArgs.BazelModeDev, "bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
 
 	// Flags that probably shouldn't be flags of soong_build but we haven't found
 	// the time to remove them yet
@@ -121,7 +123,25 @@
 }
 
 func newConfig(availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, runGoTests, outDir, soongOutDir, availableEnv)
+	var buildMode android.SoongBuildMode
+
+	if bp2buildMarker != "" {
+		buildMode = android.Bp2build
+	} else if bazelQueryViewDir != "" {
+		buildMode = android.GenerateQueryView
+	} else if moduleGraphFile != "" {
+		buildMode = android.GenerateModuleGraph
+	} else if docFile != "" {
+		buildMode = android.GenerateDocFile
+	} else if cmdlineArgs.BazelModeDev {
+		buildMode = android.BazelDevMode
+	} else if cmdlineArgs.BazelMode {
+		buildMode = android.BazelProdMode
+	} else {
+		buildMode = android.AnalysisNoBazel
+	}
+
+	configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, buildMode, runGoTests, outDir, soongOutDir, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -220,53 +240,41 @@
 // doChosenActivity runs Soong for a specific activity, like bp2build, queryview
 // or the actual Soong build for the build.ninja file. Returns the top level
 // output file of the specific activity.
-func doChosenActivity(ctx *android.Context, configuration android.Config, extraNinjaDeps []string, logDir string) string {
-	mixedModeBuild := configuration.BazelContext.BazelEnabled()
-	generateBazelWorkspace := bp2buildMarker != ""
-	generateQueryView := bazelQueryViewDir != ""
-	generateModuleGraphFile := moduleGraphFile != ""
-	generateDocFile := docFile != ""
-
-	if generateBazelWorkspace {
+func doChosenActivity(ctx *android.Context, configuration android.Config, extraNinjaDeps []string) string {
+	if configuration.BuildMode == android.Bp2build {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
 		runBp2Build(configuration, extraNinjaDeps)
 		return bp2buildMarker
-	}
-
-	blueprintArgs := cmdlineArgs
-
-	if mixedModeBuild {
+	} else if configuration.IsMixedBuildsEnabled() {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
 	} else {
 		var stopBefore bootstrap.StopBefore
-		if generateModuleGraphFile {
+		if configuration.BuildMode == android.GenerateModuleGraph {
 			stopBefore = bootstrap.StopBeforeWriteNinja
-		} else if generateQueryView {
-			stopBefore = bootstrap.StopBeforePrepareBuildActions
-		} else if generateDocFile {
+		} else if configuration.BuildMode == android.GenerateQueryView || configuration.BuildMode == android.GenerateDocFile {
 			stopBefore = bootstrap.StopBeforePrepareBuildActions
 		} else {
 			stopBefore = bootstrap.DoEverything
 		}
 
-		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
+		ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, stopBefore, ctx.Context, configuration)
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 		globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
 		ninjaDeps = append(ninjaDeps, globListFiles...)
 
 		// Convert the Soong module graph into Bazel BUILD files.
-		if generateQueryView {
+		if configuration.BuildMode == android.GenerateQueryView {
 			queryviewMarkerFile := bazelQueryViewDir + ".marker"
 			runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
 			writeDepFile(queryviewMarkerFile, *ctx.EventHandler, ninjaDeps)
 			return queryviewMarkerFile
-		} else if generateModuleGraphFile {
+		} else if configuration.BuildMode == android.GenerateModuleGraph {
 			writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
 			writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps)
 			return moduleGraphFile
-		} else if generateDocFile {
+		} else if configuration.BuildMode == android.GenerateDocFile {
 			// TODO: we could make writeDocs() return the list of documentation files
 			// written and add them to the .d file. Then soong_docs would be re-run
 			// whenever one is deleted.
@@ -345,7 +353,7 @@
 	ctx := newContext(configuration)
 	ctx.EventHandler.Begin("soong_build")
 
-	finalOutputFile := doChosenActivity(ctx, configuration, extraNinjaDeps, logDir)
+	finalOutputFile := doChosenActivity(ctx, configuration, extraNinjaDeps)
 
 	ctx.EventHandler.End("soong_build")
 	writeMetrics(configuration, *ctx.EventHandler, logDir)
@@ -398,14 +406,10 @@
 	}
 }
 
-// Find BUILD files in the srcDir which...
-//
-// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir())
-//
-// - won't be overwritten by corresponding bp2build generated files
-//
-// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
-func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string {
+// Find BUILD files in the srcDir which are not in the allowlist
+// (android.Bp2BuildConversionAllowlist#ShouldKeepExistingBuildFileForDir)
+// and return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
+func getPathsToIgnoredBuildFiles(config android.Bp2BuildConversionAllowlist, topDir string, srcDirBazelFiles []string, verbose bool) []string {
 	paths := make([]string, 0)
 
 	for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
@@ -420,21 +424,14 @@
 			// Don't ignore entire directories
 			continue
 		}
-		if !(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
+		if fileInfo.Name() != "BUILD" && fileInfo.Name() != "BUILD.bazel" {
 			// Don't ignore this file - it is not a build file
 			continue
 		}
-		srcDirBazelFileDir := filepath.Dir(srcDirBazelFileRelativePath)
-		if android.ShouldKeepExistingBuildFileForDir(srcDirBazelFileDir) {
+		if config.ShouldKeepExistingBuildFileForDir(filepath.Dir(srcDirBazelFileRelativePath)) {
 			// Don't ignore this existing build file
 			continue
 		}
-		correspondingBp2BuildFile := shared.JoinPath(topDir, generatedRoot, srcDirBazelFileRelativePath)
-		if _, err := os.Stat(correspondingBp2BuildFile); err == nil {
-			// If bp2build generated an alternate BUILD file, don't exclude this workspace path
-			// BUILD file clash resolution happens later in the symlink forest creation
-			continue
-		}
 		if verbose {
 			fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
 		}
@@ -491,11 +488,6 @@
 		// conversion for Bazel conversion.
 		bp2buildCtx := android.NewContext(configuration)
 
-		// Soong internals like LoadHooks behave differently when running as
-		// bp2build. This is the bit to differentiate between Soong-as-Soong and
-		// Soong-as-bp2build.
-		bp2buildCtx.SetRunningAsBp2build()
-
 		// Propagate "allow misssing dependencies" bit. This is normally set in
 		// newContext(), but we create bp2buildCtx without calling that method.
 		bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
@@ -550,7 +542,7 @@
 			os.Exit(1)
 		}
 
-		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
+		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
 		excludes = append(excludes, pathsToIgnoredBuildFiles...)
 
 		excludes = append(excludes, getTemporaryExcludes()...)
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 3d18849..b06e4fe 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -176,10 +176,6 @@
 
 	build.SetupOutDir(buildCtx, config)
 
-	if config.UseBazel() && config.Dist() {
-		defer populateExternalDistDir(buildCtx, config)
-	}
-
 	// Set up files to be outputted in the log directory.
 	logsDir := config.LogsDir()
 
@@ -268,6 +264,8 @@
 
 func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
 	flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
+	flags.SetOutput(ctx.Writer)
+
 	flags.Usage = func() {
 		fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
 		fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
@@ -318,6 +316,8 @@
 
 func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
 	flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
+	flags.SetOutput(ctx.Writer)
+
 	flags.Usage = func() {
 		fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
 		fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
@@ -401,6 +401,8 @@
 
 func buildActionConfig(ctx build.Context, args ...string) build.Config {
 	flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
+	flags.SetOutput(ctx.Writer)
+
 	flags.Usage = func() {
 		fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
 		fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
@@ -453,21 +455,32 @@
 	const numBuildActionFlags = 2
 	if len(args) < numBuildActionFlags {
 		flags.Usage()
-		ctx.Fatalln("Improper build action arguments.")
+		ctx.Fatalln("Improper build action arguments: too few arguments")
 	}
-	flags.Parse(args[0:numBuildActionFlags])
+	parseError := flags.Parse(args[0:numBuildActionFlags])
 
 	// The next block of code is to validate that exactly one build action is set and the dir flag
 	// is specified.
-	buildActionCount := 0
+	buildActionFound := false
 	var buildAction build.BuildAction
-	for _, flag := range buildActionFlags {
-		if flag.set {
-			buildActionCount++
-			buildAction = flag.action
+	for _, f := range buildActionFlags {
+		if f.set {
+			if buildActionFound {
+				if parseError == nil {
+					//otherwise Parse() already called Usage()
+					flags.Usage()
+				}
+				ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
+			}
+			buildActionFound = true
+			buildAction = f.action
 		}
 	}
-	if buildActionCount != 1 {
+	if !buildActionFound {
+		if parseError == nil {
+			//otherwise Parse() already called Usage()
+			flags.Usage()
+		}
 		ctx.Fatalln("Build action not defined.")
 	}
 	if *dir == "" {
@@ -509,8 +522,16 @@
 // getCommand finds the appropriate command based on args[1] flag. args[0]
 // is the soong_ui filename.
 func getCommand(args []string) (*command, []string, error) {
+	listFlags := func() []string {
+		flags := make([]string, len(commands))
+		for i, c := range commands {
+			flags[i] = c.flag
+		}
+		return flags
+	}
+
 	if len(args) < 2 {
-		return nil, nil, fmt.Errorf("Too few arguments: %q", args)
+		return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
 	}
 
 	for _, c := range commands {
@@ -518,13 +539,7 @@
 			return &c, args[2:], nil
 		}
 	}
-
-	// command not found
-	flags := make([]string, len(commands))
-	for i, c := range commands {
-		flags[i] = c.flag
-	}
-	return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args, flags)
+	return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args[1], listFlags())
 }
 
 // For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 7bc9ab2..afb3de3 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -25,11 +25,11 @@
 )
 
 // This comment describes the following:
-//   1. the concept of class loader context (CLC) and its relation to classpath
-//   2. how PackageManager constructs CLC from shared libraries and their dependencies
-//   3. build-time vs. run-time CLC and why this matters for dexpreopt
-//   4. manifest fixer: a tool that adds missing <uses-library> tags to the manifests
-//   5. build system support for CLC
+//  1. the concept of class loader context (CLC) and its relation to classpath
+//  2. how PackageManager constructs CLC from shared libraries and their dependencies
+//  3. build-time vs. run-time CLC and why this matters for dexpreopt
+//  4. manifest fixer: a tool that adds missing <uses-library> tags to the manifests
+//  5. build system support for CLC
 //
 // 1. Class loader context
 // -----------------------
@@ -59,15 +59,16 @@
 // loaders are not duplicated (at runtime there is a single class loader instance for each library).
 //
 // Example: A has <uses-library> tags B, C and D; C has <uses-library tags> B and D;
-//          D has <uses-library> E; B and E have no <uses-library> dependencies. The CLC is:
-//    A
-//    ├── B
-//    ├── C
-//    │   ├── B
-//    │   └── D
-//    │       └── E
-//    └── D
-//        └── E
+//
+//	      D has <uses-library> E; B and E have no <uses-library> dependencies. The CLC is:
+//	A
+//	├── B
+//	├── C
+//	│   ├── B
+//	│   └── D
+//	│       └── E
+//	└── D
+//	    └── E
 //
 // CLC defines the lookup order of libraries when resolving Java classes used by the library/app.
 // The lookup order is important because libraries may contain duplicate classes, and the class is
@@ -188,7 +189,6 @@
 // rule generation phase.
 //
 // ClassLoaderContext is a structure that represents CLC.
-//
 type ClassLoaderContext struct {
 	// The name of the library.
 	Name string
@@ -249,7 +249,6 @@
 // generates a build rule that includes conditional CLC for all versions, extracts the target SDK
 // version from the manifest, and filters the CLCs based on that version. Exact final CLC that is
 // passed to dex2oat is unknown to the build system, and gets known only at Ninja stage.
-//
 type ClassLoaderContextMap map[int][]*ClassLoaderContext
 
 // Compatibility libraries. Some are optional, and some are required: this is the default that
@@ -485,7 +484,6 @@
 // constructs class loader context on device.
 //
 // TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
-//
 func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
 	required, optional := clcMap.UsesLibs()
 	usesLibs := append(required, optional...)
diff --git a/docs/perf.md b/docs/perf.md
index 86a27b4..694dcf1 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -221,6 +221,18 @@
 various .ninja files. The files are (mostly) human-readable, but a (slow) web
 interface can be used by running `NINJA_ARGS="-t browse <target>" m`.
 
+There is also `SOONG_UI_NINJA_ARGS`, which passes ninja arguments to soong ui's
+ninja invocations, e.g. to emit $OUT_DIR/soong/build.ninja, $OUT_DIR/soong/module-graph.json, etc.
+
+```bash
+$ m nothing
+$ touch Android.bp
+$ SOONG_UI_NINJA_ARGS="-d explain" m nothing
+...
+ninja explain: restat of output out/soong/build.ninja older than most recent input Android.bp
+...
+```
+
 #### Builds take a long time
 
 If the long part in the trace view of a build is a relatively solid block, then
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 719771f..7520f58 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -31,6 +31,7 @@
 	"encoding/json"
 	"fmt"
 	"path/filepath"
+	"reflect"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -670,10 +671,11 @@
 // For Bazel / bp2build
 
 type bazelPrebuiltFileAttributes struct {
-	Src         bazel.LabelAttribute
-	Filename    string
-	Dir         string
-	Installable bazel.BoolAttribute
+	Src               bazel.LabelAttribute
+	Filename          bazel.LabelAttribute
+	Dir               string
+	Installable       bazel.BoolAttribute
+	Filename_from_src bazel.BoolAttribute
 }
 
 // ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
@@ -691,11 +693,37 @@
 				src.SetSelectValue(axis, config, label)
 			}
 		}
+
+		for propName, productConfigProps := range android.ProductVariableProperties(ctx) {
+			for configProp, propVal := range productConfigProps {
+				if propName == "Src" {
+					props, ok := propVal.(*string)
+					if !ok {
+						ctx.PropertyErrorf(" Expected Property to have type string, but was %s\n", reflect.TypeOf(propVal).String())
+						continue
+					}
+					if props != nil {
+						label := android.BazelLabelForModuleSrcSingle(ctx, *props)
+						src.SetSelectValue(configProp.ConfigurationAxis(), configProp.SelectKey(), label)
+					}
+				}
+			}
+		}
 	}
 
 	var filename string
-	if module.properties.Filename != nil {
-		filename = *module.properties.Filename
+	var filenameFromSrc bool
+	moduleProps := module.properties
+
+	if moduleProps.Filename != nil && *moduleProps.Filename != "" {
+		filename = *moduleProps.Filename
+	} else if moduleProps.Filename_from_src != nil && *moduleProps.Filename_from_src {
+		if moduleProps.Src != nil {
+			filename = *moduleProps.Src
+		}
+		filenameFromSrc = true
+	} else {
+		filename = ctx.ModuleName()
 	}
 
 	var dir = module.installDirBase
@@ -714,11 +742,16 @@
 
 	attrs := &bazelPrebuiltFileAttributes{
 		Src:         src,
-		Filename:    filename,
 		Dir:         dir,
 		Installable: installable,
 	}
 
+	if filename != "" {
+		attrs.Filename = bazel.LabelAttribute{Value: &bazel.Label{Label: filename}}
+	} else if filenameFromSrc {
+		attrs.Filename_from_src = bazel.BoolAttribute{Value: moduleProps.Filename_from_src}
+	}
+
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "prebuilt_file",
 		Bzl_load_location: "//build/bazel/rules:prebuilt_file.bzl",
diff --git a/etc/snapshot_etc.go b/etc/snapshot_etc.go
index b54a8a6..0d65ab6 100644
--- a/etc/snapshot_etc.go
+++ b/etc/snapshot_etc.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/finder/finder.go b/finder/finder.go
index c5196c8..054ccd6 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -736,15 +736,15 @@
 // because we know this separator won't appear in the json that we're parsing.
 //
 // The newline byte can only appear in a UTF-8 stream if the newline character appears, because:
-// - The newline character is encoded as "0000 1010" in binary ("0a" in hex)
-// - UTF-8 dictates that bytes beginning with a "0" bit are never emitted as part of a multibyte
-//   character.
+//   - The newline character is encoded as "0000 1010" in binary ("0a" in hex)
+//   - UTF-8 dictates that bytes beginning with a "0" bit are never emitted as part of a multibyte
+//     character.
 //
 // We know that the newline character will never appear in our json string, because:
-// - If a newline character appears as part of a data string, then json encoding will
-//   emit two characters instead: '\' and 'n'.
-// - The json encoder that we use doesn't emit the optional newlines between any of its
-//   other outputs.
+//   - If a newline character appears as part of a data string, then json encoding will
+//     emit two characters instead: '\' and 'n'.
+//   - The json encoder that we use doesn't emit the optional newlines between any of its
+//     other outputs.
 const lineSeparator = byte('\n')
 
 func (f *Finder) readLine(reader *bufio.Reader) ([]byte, error) {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b796877..7a0dac3 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -26,6 +26,7 @@
 	"strings"
 
 	"android/soong/bazel/cquery"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
@@ -468,6 +469,7 @@
 				return "SOONG_ERROR", nil
 			}
 
+			// Apply shell escape to each cases to prevent source file paths containing $ from being evaluated in shell
 			switch name {
 			case "location":
 				if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
@@ -481,15 +483,15 @@
 					return reportError("default label %q has multiple files, use $(locations %s) to reference it",
 						firstLabel, firstLabel)
 				}
-				return paths[0], nil
+				return proptools.ShellEscape(paths[0]), nil
 			case "in":
-				return strings.Join(cmd.PathsForInputs(srcFiles), " "), nil
+				return strings.Join(proptools.ShellEscapeList(cmd.PathsForInputs(srcFiles)), " "), nil
 			case "out":
 				var sandboxOuts []string
 				for _, out := range task.out {
 					sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
 				}
-				return strings.Join(sandboxOuts, " "), nil
+				return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil
 			case "depfile":
 				referencedDepfile = true
 				if !Bool(g.properties.Depfile) {
@@ -497,7 +499,7 @@
 				}
 				return "__SBOX_DEPFILE__", nil
 			case "genDir":
-				return cmd.PathForOutput(task.genDir), nil
+				return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil
 			default:
 				if strings.HasPrefix(name, "location ") {
 					label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -509,7 +511,7 @@
 							return reportError("label %q has multiple files, use $(locations %s) to reference it",
 								label, label)
 						}
-						return paths[0], nil
+						return proptools.ShellEscape(paths[0]), nil
 					} else {
 						return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label)
 					}
@@ -520,7 +522,7 @@
 						if len(paths) == 0 {
 							return reportError("label %q has no files", label)
 						}
-						return strings.Join(paths, " "), nil
+						return proptools.ShellEscape(strings.Join(paths, " ")), nil
 					} else {
 						return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label)
 					}
@@ -976,9 +978,7 @@
 var Bool = proptools.Bool
 var String = proptools.String
 
-//
 // Defaults
-//
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index b9be1f7..cd941cc 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -422,7 +422,7 @@
 
 			allowMissingDependencies: true,
 
-			expect: "cat ***missing srcs :missing*** > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "cat '***missing srcs :missing***' > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool allow missing dependencies",
@@ -434,7 +434,7 @@
 
 			allowMissingDependencies: true,
 
-			expect: "***missing tool :missing*** > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "'***missing tool :missing***' > __SBOX_SANDBOX_DIR__/out/out",
 		},
 	}
 
@@ -878,6 +878,92 @@
 	android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
 }
 
+func TestGenruleWithGlobPaths(t *testing.T) {
+	testcases := []struct {
+		name            string
+		bp              string
+		additionalFiles android.MockFS
+		expectedCmd     string
+	}{
+		{
+			name: "single file in directory with $ sign",
+			bp: `
+				genrule {
+					name: "gen",
+					srcs: ["inn*.txt"],
+					out: ["out.txt"],
+					cmd: "cp $(in) $(out)",
+				}
+				`,
+			additionalFiles: android.MockFS{"inn$1.txt": nil},
+			expectedCmd:     "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
+		},
+		{
+			name: "multiple file in directory with $ sign",
+			bp: `
+				genrule {
+					name: "gen",
+					srcs: ["inn*.txt"],
+					out: ["."],
+					cmd: "cp $(in) $(out)",
+				}
+				`,
+			additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
+			expectedCmd:     "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
+		},
+		{
+			name: "file in directory with other shell unsafe character",
+			bp: `
+				genrule {
+					name: "gen",
+					srcs: ["inn*.txt"],
+					out: ["out.txt"],
+					cmd: "cp $(in) $(out)",
+				}
+				`,
+			additionalFiles: android.MockFS{"inn@1.txt": nil},
+			expectedCmd:     "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
+		},
+		{
+			name: "glob location param with filepath containing $",
+			bp: `
+				genrule {
+					name: "gen",
+					srcs: ["**/inn*"],
+					out: ["."],
+					cmd: "cp $(in) $(location **/inn*)",
+				}
+				`,
+			additionalFiles: android.MockFS{"a/inn$1.txt": nil},
+			expectedCmd:     "cp 'a/inn$1.txt' 'a/inn$1.txt'",
+		},
+		{
+			name: "glob locations param with filepath containing $",
+			bp: `
+				genrule {
+					name: "gen",
+					tool_files: ["**/inn*"],
+					out: ["out.txt"],
+					cmd: "cp $(locations  **/inn*) $(out)",
+				}
+				`,
+			additionalFiles: android.MockFS{"a/inn$1.txt": nil},
+			expectedCmd:     "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				prepareForGenRuleTest,
+				android.FixtureMergeMockFs(test.additionalFiles),
+			).RunTestWithBp(t, test.bp)
+			gen := result.Module("gen", "").(*Module)
+			android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
+		})
+	}
+}
+
 type testTool struct {
 	android.ModuleBase
 	outputFile android.Path
diff --git a/java/androidmk.go b/java/androidmk.go
index 82ef413..75ac0e7 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -167,9 +167,8 @@
 			entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
 		}
 		entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", j.testProperties.Test_mainline_modules...)
-		if Bool(j.testProperties.Test_options.Unit_test) {
-			entries.SetBool("LOCAL_IS_UNIT_TEST", true)
-		}
+
+		j.testProperties.Test_options.CommonTestOptions.SetAndroidMkEntries(entries)
 	})
 
 	return entriesList
@@ -419,7 +418,15 @@
 				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
 			},
 		},
-	}}
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string) {
+				if app.javaApiUsedByOutputFile.String() != "" {
+					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s/$(notdir %s))\n",
+						app.installApkName, app.javaApiUsedByOutputFile.String(), "java_apis_used_by_apex", app.javaApiUsedByOutputFile.String())
+				}
+			},
+		}},
+	}
 }
 
 func (a *AndroidApp) getOverriddenPackages() []string {
diff --git a/java/app.go b/java/app.go
index 3c8fcd3..bccd37f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -117,6 +117,9 @@
 	// Name of the signing certificate lineage file or filegroup module.
 	Lineage *string `android:"path"`
 
+	// For overriding the --rotation-min-sdk-version property of apksig
+	RotationMinSdkVersion *string
+
 	// the package name of this app. The package name in the manifest file is used if one was not given.
 	Package_name *string
 
@@ -165,6 +168,8 @@
 	overriddenManifestPackageName string
 
 	android.ApexBundleDepsInfo
+
+	javaApiUsedByOutputFile android.ModuleOutPath
 }
 
 func (a *AndroidApp) IsInstallable() bool {
@@ -273,6 +278,7 @@
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.checkAppSdkVersions(ctx)
 	a.generateAndroidBuildActions(ctx)
+	a.generateJavaUsedByApex(ctx)
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
@@ -661,7 +667,10 @@
 	if lineage := String(a.overridableAppProperties.Lineage); lineage != "" {
 		lineageFile = android.PathForModuleSrc(ctx, lineage)
 	}
-	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile)
+
+	rotationMinSdkVersion := String(a.overridableAppProperties.RotationMinSdkVersion)
+
+	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 	a.outputFile = packageFile
 	if v4SigningRequested {
 		a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -690,7 +699,7 @@
 		if v4SigningRequested {
 			v4SignatureFile = android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk.idsig")
 		}
-		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile)
+		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 		if v4SigningRequested {
 			a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -1055,7 +1064,7 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.test = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
@@ -1108,7 +1117,7 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.test = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
diff --git a/java/app_builder.go b/java/app_builder.go
index 4348644..18a9751 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) {
+	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) {
 
 	unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
 	unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
@@ -73,10 +73,10 @@
 		Implicits: deps,
 	})
 
-	SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile)
+	SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 }
 
-func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate, v4SignatureFile android.WritablePath, lineageFile android.Path) {
+func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) {
 
 	var certificateArgs []string
 	var deps android.Paths
@@ -97,6 +97,10 @@
 		deps = append(deps, lineageFile)
 	}
 
+	if rotationMinSdkVersion != "" {
+		flags = append(flags, "--rotation-min-sdk-version", rotationMinSdkVersion)
+	}
+
 	rule := Signapk
 	args := map[string]string{
 		"certificates": strings.Join(certificateArgs, " "),
@@ -267,6 +271,18 @@
 	})
 }
 
+func (a *AndroidApp) generateJavaUsedByApex(ctx android.ModuleContext) {
+	javaApiUsedByOutputFile := android.PathForModuleOut(ctx, a.installApkName+"_using.xml")
+	javaUsedByRule := android.NewRuleBuilder(pctx, ctx)
+	javaUsedByRule.Command().
+		Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")).
+		BuiltTool("dexdeps").
+		Output(javaApiUsedByOutputFile).
+		Input(a.Library.Module.outputFile)
+	javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex")
+	a.javaApiUsedByOutputFile = javaApiUsedByOutputFile
+}
+
 func targetToJniDir(target android.Target) string {
 	return filepath.Join("lib", target.Arch.Abi[0])
 }
diff --git a/java/app_import.go b/java/app_import.go
index 9d199d6..d6dca38 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -80,6 +80,9 @@
 	// Name of the signing certificate lineage file or filegroup module.
 	Lineage *string `android:"path"`
 
+	// For overriding the --rotation-min-sdk-version property of apksig
+	RotationMinSdkVersion *string
+
 	// Sign with the default system dev certificate. Must be used judiciously. Most imported apps
 	// need to either specify a specific certificate or be presigned.
 	Default_dev_cert *bool
@@ -333,7 +336,10 @@
 		if lineage := String(a.properties.Lineage); lineage != "" {
 			lineageFile = android.PathForModuleSrc(ctx, lineage)
 		}
-		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile)
+
+		rotationMinSdkVersion := String(a.properties.RotationMinSdkVersion)
+
+		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion)
 		a.outputFile = signed
 	} else {
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
@@ -455,19 +461,19 @@
 // android_app_import imports a prebuilt apk with additional processing specified in the module.
 // DPI-specific apk source files can be specified using dpi_variants. Example:
 //
-//     android_app_import {
-//         name: "example_import",
-//         apk: "prebuilts/example.apk",
-//         dpi_variants: {
-//             mdpi: {
-//                 apk: "prebuilts/example_mdpi.apk",
-//             },
-//             xhdpi: {
-//                 apk: "prebuilts/example_xhdpi.apk",
-//             },
-//         },
-//         presigned: true,
-//     }
+//	android_app_import {
+//	    name: "example_import",
+//	    apk: "prebuilts/example.apk",
+//	    dpi_variants: {
+//	        mdpi: {
+//	            apk: "prebuilts/example_mdpi.apk",
+//	        },
+//	        xhdpi: {
+//	            apk: "prebuilts/example_xhdpi.apk",
+//	        },
+//	    },
+//	    presigned: true,
+//	}
 func AndroidAppImportFactory() android.Module {
 	module := &AndroidAppImport{}
 	module.AddProperties(&module.properties)
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 8f6c75f..41be092 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -129,6 +129,7 @@
 			certificate: "platform",
 			additional_certificates: [":additional_certificate"],
 			lineage: "lineage.bin",
+			rotationMinSdkVersion: "32",
 		}
 
 		android_app_certificate {
@@ -148,11 +149,12 @@
 	if expected != certificatesFlag {
 		t.Errorf("Incorrect certificates flags, expected: %q, got: %q", expected, certificatesFlag)
 	}
-	// Check cert signing lineage flag.
-	signingFlag := signedApk.Args["flags"]
-	expected = "--lineage lineage.bin"
-	if expected != signingFlag {
-		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+
+	// Check cert signing flags.
+	actualCertSigningFlags := signedApk.Args["flags"]
+	expectedCertSigningFlags := "--lineage lineage.bin --rotation-min-sdk-version 32"
+	if expectedCertSigningFlags != actualCertSigningFlags {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expectedCertSigningFlags, actualCertSigningFlags)
 	}
 
 	rule := variant.Rule("genProvenanceMetaData")
diff --git a/java/app_test.go b/java/app_test.go
index 0f973ba..23635b9 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1488,11 +1488,11 @@
 
 func TestCertificates(t *testing.T) {
 	testCases := []struct {
-		name                string
-		bp                  string
-		certificateOverride string
-		expectedLineage     string
-		expectedCertificate string
+		name                     string
+		bp                       string
+		certificateOverride      string
+		expectedCertSigningFlags string
+		expectedCertificate      string
 	}{
 		{
 			name: "default",
@@ -1503,9 +1503,9 @@
 					sdk_version: "current",
 				}
 			`,
-			certificateOverride: "",
-			expectedLineage:     "",
-			expectedCertificate: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8",
+			certificateOverride:      "",
+			expectedCertSigningFlags: "",
+			expectedCertificate:      "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8",
 		},
 		{
 			name: "module certificate property",
@@ -1522,9 +1522,9 @@
 					certificate: "cert/new_cert",
 				}
 			`,
-			certificateOverride: "",
-			expectedLineage:     "",
-			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+			certificateOverride:      "",
+			expectedCertSigningFlags: "",
+			expectedCertificate:      "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
 		{
 			name: "path certificate property",
@@ -1536,9 +1536,9 @@
 					sdk_version: "current",
 				}
 			`,
-			certificateOverride: "",
-			expectedLineage:     "",
-			expectedCertificate: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certificateOverride:      "",
+			expectedCertSigningFlags: "",
+			expectedCertificate:      "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 		},
 		{
 			name: "certificate overrides",
@@ -1555,18 +1555,19 @@
 					certificate: "cert/new_cert",
 				}
 			`,
-			certificateOverride: "foo:new_certificate",
-			expectedLineage:     "",
-			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+			certificateOverride:      "foo:new_certificate",
+			expectedCertSigningFlags: "",
+			expectedCertificate:      "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
 		{
-			name: "certificate lineage",
+			name: "certificate signing flags",
 			bp: `
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
 					certificate: ":new_certificate",
 					lineage: "lineage.bin",
+					rotationMinSdkVersion: "32",
 					sdk_version: "current",
 				}
 
@@ -1575,18 +1576,19 @@
 					certificate: "cert/new_cert",
 				}
 			`,
-			certificateOverride: "",
-			expectedLineage:     "--lineage lineage.bin",
-			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+			certificateOverride:      "",
+			expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
+			expectedCertificate:      "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
 		{
-			name: "lineage from filegroup",
+			name: "cert signing flags from filegroup",
 			bp: `
 				android_app {
 					name: "foo",
 					srcs: ["a.java"],
 					certificate: ":new_certificate",
 					lineage: ":lineage_bin",
+					rotationMinSdkVersion: "32",
 					sdk_version: "current",
 				}
 
@@ -1600,9 +1602,9 @@
 					srcs: ["lineage.bin"],
 				}
 			`,
-			certificateOverride: "",
-			expectedLineage:     "--lineage lineage.bin",
-			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+			certificateOverride:      "",
+			expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
+			expectedCertificate:      "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
 	}
 
@@ -1623,8 +1625,8 @@
 			signCertificateFlags := signapk.Args["certificates"]
 			android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags)
 
-			signFlags := signapk.Args["flags"]
-			android.AssertStringEquals(t, "signing flags", test.expectedLineage, signFlags)
+			certSigningFlags := signapk.Args["flags"]
+			android.AssertStringEquals(t, "cert signing flags", test.expectedCertSigningFlags, certSigningFlags)
 		})
 	}
 }
@@ -1819,6 +1821,7 @@
 			base: "foo",
 			certificate: ":new_certificate",
 			lineage: "lineage.bin",
+			rotationMinSdkVersion: "32",
 			logging_parent: "bah",
 		}
 
@@ -1864,89 +1867,89 @@
 		`)
 
 	expectedVariants := []struct {
-		name            string
-		moduleName      string
-		variantName     string
-		apkName         string
-		apkPath         string
-		certFlag        string
-		lineageFlag     string
-		overrides       []string
-		packageFlag     string
-		renameResources bool
-		logging_parent  string
+		name             string
+		moduleName       string
+		variantName      string
+		apkName          string
+		apkPath          string
+		certFlag         string
+		certSigningFlags string
+		overrides        []string
+		packageFlag      string
+		renameResources  bool
+		logging_parent   string
 	}{
 		{
-			name:            "foo",
-			moduleName:      "foo",
-			variantName:     "android_common",
-			apkPath:         "out/soong/target/product/test_device/system/app/foo/foo.apk",
-			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
-			lineageFlag:     "",
-			overrides:       []string{"qux"},
-			packageFlag:     "",
-			renameResources: false,
-			logging_parent:  "",
+			name:             "foo",
+			moduleName:       "foo",
+			variantName:      "android_common",
+			apkPath:          "out/soong/target/product/test_device/system/app/foo/foo.apk",
+			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certSigningFlags: "",
+			overrides:        []string{"qux"},
+			packageFlag:      "",
+			renameResources:  false,
+			logging_parent:   "",
 		},
 		{
-			name:            "foo",
-			moduleName:      "bar",
-			variantName:     "android_common_bar",
-			apkPath:         "out/soong/target/product/test_device/system/app/bar/bar.apk",
-			certFlag:        "cert/new_cert.x509.pem cert/new_cert.pk8",
-			lineageFlag:     "--lineage lineage.bin",
-			overrides:       []string{"qux", "foo"},
-			packageFlag:     "",
-			renameResources: false,
-			logging_parent:  "bah",
+			name:             "foo",
+			moduleName:       "bar",
+			variantName:      "android_common_bar",
+			apkPath:          "out/soong/target/product/test_device/system/app/bar/bar.apk",
+			certFlag:         "cert/new_cert.x509.pem cert/new_cert.pk8",
+			certSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
+			overrides:        []string{"qux", "foo"},
+			packageFlag:      "",
+			renameResources:  false,
+			logging_parent:   "bah",
 		},
 		{
-			name:            "foo",
-			moduleName:      "baz",
-			variantName:     "android_common_baz",
-			apkPath:         "out/soong/target/product/test_device/system/app/baz/baz.apk",
-			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
-			lineageFlag:     "",
-			overrides:       []string{"qux", "foo"},
-			packageFlag:     "org.dandroid.bp",
-			renameResources: true,
-			logging_parent:  "",
+			name:             "foo",
+			moduleName:       "baz",
+			variantName:      "android_common_baz",
+			apkPath:          "out/soong/target/product/test_device/system/app/baz/baz.apk",
+			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certSigningFlags: "",
+			overrides:        []string{"qux", "foo"},
+			packageFlag:      "org.dandroid.bp",
+			renameResources:  true,
+			logging_parent:   "",
 		},
 		{
-			name:            "foo",
-			moduleName:      "baz_no_rename_resources",
-			variantName:     "android_common_baz_no_rename_resources",
-			apkPath:         "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
-			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
-			lineageFlag:     "",
-			overrides:       []string{"qux", "foo"},
-			packageFlag:     "org.dandroid.bp",
-			renameResources: false,
-			logging_parent:  "",
+			name:             "foo",
+			moduleName:       "baz_no_rename_resources",
+			variantName:      "android_common_baz_no_rename_resources",
+			apkPath:          "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
+			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certSigningFlags: "",
+			overrides:        []string{"qux", "foo"},
+			packageFlag:      "org.dandroid.bp",
+			renameResources:  false,
+			logging_parent:   "",
 		},
 		{
-			name:            "foo_no_rename_resources",
-			moduleName:      "baz_base_no_rename_resources",
-			variantName:     "android_common_baz_base_no_rename_resources",
-			apkPath:         "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
-			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
-			lineageFlag:     "",
-			overrides:       []string{"qux", "foo_no_rename_resources"},
-			packageFlag:     "org.dandroid.bp",
-			renameResources: false,
-			logging_parent:  "",
+			name:             "foo_no_rename_resources",
+			moduleName:       "baz_base_no_rename_resources",
+			variantName:      "android_common_baz_base_no_rename_resources",
+			apkPath:          "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
+			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certSigningFlags: "",
+			overrides:        []string{"qux", "foo_no_rename_resources"},
+			packageFlag:      "org.dandroid.bp",
+			renameResources:  false,
+			logging_parent:   "",
 		},
 		{
-			name:            "foo_no_rename_resources",
-			moduleName:      "baz_override_base_rename_resources",
-			variantName:     "android_common_baz_override_base_rename_resources",
-			apkPath:         "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
-			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
-			lineageFlag:     "",
-			overrides:       []string{"qux", "foo_no_rename_resources"},
-			packageFlag:     "org.dandroid.bp",
-			renameResources: true,
-			logging_parent:  "",
+			name:             "foo_no_rename_resources",
+			moduleName:       "baz_override_base_rename_resources",
+			variantName:      "android_common_baz_override_base_rename_resources",
+			apkPath:          "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
+			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certSigningFlags: "",
+			overrides:        []string{"qux", "foo_no_rename_resources"},
+			packageFlag:      "org.dandroid.bp",
+			renameResources:  true,
+			logging_parent:   "",
 		},
 	}
 	for _, expected := range expectedVariants {
@@ -1960,9 +1963,9 @@
 		certFlag := signapk.Args["certificates"]
 		android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag)
 
-		// Check the lineage flags
-		lineageFlag := signapk.Args["flags"]
-		android.AssertStringEquals(t, "signing flags", expected.lineageFlag, lineageFlag)
+		// Check the cert signing flags
+		certSigningFlags := signapk.Args["flags"]
+		android.AssertStringEquals(t, "cert signing flags", expected.certSigningFlags, certSigningFlags)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidApp)
diff --git a/java/base.go b/java/base.go
index 94daf37..23b4d46 100644
--- a/java/base.go
+++ b/java/base.go
@@ -169,6 +169,8 @@
 		Output_params []string
 	}
 
+	// If true, then jacocoagent is automatically added as a libs dependency so that
+	// r8 will not strip instrumentation classes out of dexed libraries.
 	Instrument bool `blueprint:"mutated"`
 	// If true, then the module supports statically including the jacocoagent
 	// into the library.
@@ -267,6 +269,9 @@
 	// Only for libraries created by a sysprop_library module, SyspropPublicStub is the name of the
 	// public stubs library.
 	SyspropPublicStub string `blueprint:"mutated"`
+
+	HiddenAPIPackageProperties
+	HiddenAPIFlagFileProperties
 }
 
 // Device properties that can be overridden by overriding module (e.g. override_android_app)
@@ -564,6 +569,20 @@
 	)
 }
 
+// provideHiddenAPIPropertyInfo populates a HiddenAPIPropertyInfo from hidden API properties and
+// makes it available through the hiddenAPIPropertyInfoProvider.
+func (j *Module) provideHiddenAPIPropertyInfo(ctx android.ModuleContext) {
+	hiddenAPIInfo := newHiddenAPIPropertyInfo()
+
+	// Populate with flag file paths from the properties.
+	hiddenAPIInfo.extractFlagFilesFromProperties(ctx, &j.deviceProperties.HiddenAPIFlagFileProperties)
+
+	// Populate with package rules from the properties.
+	hiddenAPIInfo.extractPackageRulesFromProperties(&j.deviceProperties.HiddenAPIPackageProperties)
+
+	ctx.SetProvider(hiddenAPIPropertyInfoProvider, hiddenAPIInfo)
+}
+
 func (j *Module) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
@@ -631,6 +650,10 @@
 	return false
 }
 
+func (j *Module) setInstrument(value bool) {
+	j.properties.Instrument = value
+}
+
 func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version))
 }
@@ -803,7 +826,7 @@
 }
 
 func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
-	aidlIncludeDirs android.Paths) (string, android.Paths) {
+	aidlIncludeDirs android.Paths, aidlSrcs android.Paths) (string, android.Paths) {
 
 	aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs)
 	aidlIncludes = append(aidlIncludes,
@@ -813,6 +836,7 @@
 
 	var flags []string
 	var deps android.Paths
+	var includeDirs android.Paths
 
 	flags = append(flags, j.deviceProperties.Aidl.Flags...)
 
@@ -820,23 +844,28 @@
 		flags = append(flags, "-p"+aidlPreprocess.String())
 		deps = append(deps, aidlPreprocess.Path())
 	} else if len(aidlIncludeDirs) > 0 {
-		flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
+		includeDirs = append(includeDirs, aidlIncludeDirs...)
 	}
 
 	if len(j.exportAidlIncludeDirs) > 0 {
-		flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
+		includeDirs = append(includeDirs, j.exportAidlIncludeDirs...)
 	}
 
 	if len(aidlIncludes) > 0 {
-		flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+		includeDirs = append(includeDirs, aidlIncludes...)
 	}
 
-	flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
+	includeDirs = append(includeDirs, android.PathForModuleSrc(ctx))
 	if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
-		flags = append(flags, "-I"+src.String())
+		includeDirs = append(includeDirs, src.Path())
 	}
+	flags = append(flags, android.JoinWithPrefix(includeDirs.Strings(), "-I"))
+	// add flags for dirs containing AIDL srcs that haven't been specified yet
+	flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs))
 
-	if Bool(j.deviceProperties.Aidl.Generate_traces) {
+	sdkVersion := (j.SdkVersion(ctx)).Kind
+	defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform))
+	if proptools.BoolDefault(j.deviceProperties.Aidl.Generate_traces, defaultTrace) {
 		flags = append(flags, "-t")
 	}
 
@@ -918,9 +947,6 @@
 	// systemModules
 	flags.systemModules = deps.systemModules
 
-	// aidl flags.
-	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
-
 	return flags
 }
 
@@ -1029,6 +1055,9 @@
 		ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
 	}
 
+	aidlSrcs := srcFiles.FilterByExt(".aidl")
+	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs, aidlSrcs)
+
 	nonGeneratedSrcJars := srcFiles.FilterByExt(".srcjar")
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
@@ -1178,12 +1207,21 @@
 		}
 	}
 	if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
+		hasErrorproneableFiles := false
+		for _, ext := range j.sourceExtensions {
+			if ext != ".proto" && ext != ".aidl" {
+				// Skip running errorprone on pure proto or pure aidl modules. Some modules take a long time to
+				// compile, and it's not useful to have warnings on these generated sources.
+				hasErrorproneableFiles = true
+				break
+			}
+		}
 		var extraJarDeps android.Paths
 		if Bool(j.properties.Errorprone.Enabled) {
 			// If error-prone is enabled, enable errorprone flags on the regular
 			// build.
 			flags = enableErrorproneFlags(flags)
-		} else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
+		} else if hasErrorproneableFiles && ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
 			// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
 			// a new jar file just for compiling with the errorprone compiler to.
 			// This is because we don't want to cause the java files to get completely
@@ -1400,10 +1438,6 @@
 		j.headerJarFile = j.implementationJarFile
 	}
 
-	if j.shouldInstrumentInApex(ctx) {
-		j.properties.Instrument = true
-	}
-
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
 	specs := j.jacocoModuleToZipCommand(ctx)
 	if ctx.Failed() {
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f08b64b..9316807 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -128,7 +128,7 @@
 	Coverage BootclasspathFragmentCoverageAffectedProperties
 
 	// Hidden API related properties.
-	Hidden_api HiddenAPIFlagFileProperties
+	HiddenAPIFlagFileProperties
 
 	// The list of additional stub libraries which this fragment's contents use but which are not
 	// provided by another bootclasspath_fragment.
@@ -145,7 +145,7 @@
 	BootclasspathFragmentsDepsProperties
 }
 
-type HiddenApiPackageProperties struct {
+type HiddenAPIPackageProperties struct {
 	Hidden_api struct {
 		// Contains prefixes of a package hierarchy that is provided solely by this
 		// bootclasspath_fragment.
@@ -222,8 +222,8 @@
 }
 
 type SourceOnlyBootclasspathProperties struct {
-	HiddenApiPackageProperties
-	Coverage HiddenApiPackageProperties
+	HiddenAPIPackageProperties
+	Coverage HiddenAPIPackageProperties
 }
 
 type BootclasspathFragmentModule struct {
@@ -293,7 +293,7 @@
 				return
 			}
 
-			err = proptools.AppendProperties(&m.sourceOnlyProperties.HiddenApiPackageProperties, &m.sourceOnlyProperties.Coverage, nil)
+			err = proptools.AppendProperties(&m.sourceOnlyProperties.HiddenAPIPackageProperties, &m.sourceOnlyProperties.Coverage, nil)
 			if err != nil {
 				ctx.PropertyErrorf("coverage", "error trying to append hidden api coverage specific properties: %s", err)
 				return
@@ -723,6 +723,10 @@
 	} else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
 		unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
 		_, unknown = android.RemoveFromList("core-icu4j", unknown)
+		// This module only exists in car products.
+		// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
+		// TODO(b/202896428): Add better way to handle this.
+		_, unknown = android.RemoveFromList("android.car-module", unknown)
 		if len(unknown) > 0 {
 			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
 		}
@@ -821,7 +825,12 @@
 	input.gatherStubLibInfo(ctx, contents)
 
 	// Populate with flag file paths from the properties.
-	input.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api)
+	input.extractFlagFilesFromProperties(ctx, &b.properties.HiddenAPIFlagFileProperties)
+
+	// Populate with package rules from the properties.
+	input.extractPackageRulesFromProperties(&b.sourceOnlyProperties.HiddenAPIPackageProperties)
+
+	input.gatherPropertyInfo(ctx, contents)
 
 	// Add the stub dex jars from this module's fragment dependencies.
 	input.DependencyStubDexJarsByScope.addStubDexJarsByModule(dependencyHiddenApiInfo.TransitiveStubDexJarsByScope)
@@ -858,9 +867,9 @@
 
 	// If the module specifies split_packages or package_prefixes then use those to generate the
 	// signature patterns.
-	splitPackages := b.sourceOnlyProperties.Hidden_api.Split_packages
-	packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes
-	singlePackages := b.sourceOnlyProperties.Hidden_api.Single_packages
+	splitPackages := input.SplitPackages
+	packagePrefixes := input.PackagePrefixes
+	singlePackages := input.SinglePackages
 	if splitPackages != nil || packagePrefixes != nil || singlePackages != nil {
 		output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
 			ctx, output.AllFlagsPath, splitPackages, packagePrefixes, singlePackages)
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 83beb6d..c63df59 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -95,11 +96,6 @@
 }
 
 func TestBootclasspathFragment_Coverage(t *testing.T) {
-	prepareForTestWithFrameworkCoverage := android.FixtureMergeEnv(map[string]string{
-		"EMMA_INSTRUMENT":           "true",
-		"EMMA_INSTRUMENT_FRAMEWORK": "true",
-	})
-
 	prepareWithBp := android.FixtureWithRootAndroidBp(`
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -178,7 +174,7 @@
 
 	t.Run("with coverage", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
-			prepareForTestWithFrameworkCoverage,
+			prepareForTestWithFrameworkJacocoInstrumentation,
 			preparer,
 		).RunTest(t)
 		checkContents(t, result, "mybootlib", "coveragelib")
@@ -285,6 +281,119 @@
 	android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
 }
 
+func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary", "mynewlibrary"),
+		FixtureConfigureApexBootJars("myapex:mybootlib", "myapex:mynewlibrary"),
+		android.MockFS{
+			"my-blocked.txt":                   nil,
+			"my-max-target-o-low-priority.txt": nil,
+			"my-max-target-p.txt":              nil,
+			"my-max-target-q.txt":              nil,
+			"my-max-target-r-low-priority.txt": nil,
+			"my-removed.txt":                   nil,
+			"my-unsupported-packages.txt":      nil,
+			"my-unsupported.txt":               nil,
+			"my-new-max-target-q.txt":          nil,
+		}.AddToFixture(),
+		android.FixtureWithRootAndroidBp(`
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				apex_available: ["myapex"],
+				contents: ["mybootlib", "mynewlibrary"],
+				hidden_api: {
+					unsupported: [
+							"my-unsupported.txt",
+					],
+					removed: [
+							"my-removed.txt",
+					],
+					max_target_r_low_priority: [
+							"my-max-target-r-low-priority.txt",
+					],
+					max_target_q: [
+							"my-max-target-q.txt",
+					],
+					max_target_p: [
+							"my-max-target-p.txt",
+					],
+					max_target_o_low_priority: [
+							"my-max-target-o-low-priority.txt",
+					],
+					blocked: [
+							"my-blocked.txt",
+					],
+					unsupported_packages: [
+							"my-unsupported-packages.txt",
+					],
+					split_packages: ["sdklibrary"],
+					package_prefixes: ["sdklibrary.all.mine"],
+					single_packages: ["sdklibrary.mine"],
+				},
+			}
+
+			java_library {
+				name: "mybootlib",
+				apex_available: ["myapex"],
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				min_sdk_version: "1",
+				compile_dex: true,
+				permitted_packages: ["mybootlib"],
+			}
+
+			java_sdk_library {
+				name: "mynewlibrary",
+				apex_available: ["myapex"],
+				srcs: ["Test.java"],
+				min_sdk_version: "10",
+				compile_dex: true,
+				public: {enabled: true},
+				permitted_packages: ["mysdklibrary"],
+				hidden_api: {
+					max_target_q: [
+							"my-new-max-target-q.txt",
+					],
+					split_packages: ["sdklibrary", "newlibrary"],
+					package_prefixes: ["newlibrary.all.mine"],
+					single_packages: ["newlibrary.mine"],
+				},
+			}
+		`),
+	).RunTest(t)
+
+	// Make sure that the library exports hidden API properties for use by the bootclasspath_fragment.
+	library := result.Module("mynewlibrary", "android_common")
+	info := result.ModuleProvider(library, hiddenAPIPropertyInfoProvider).(HiddenAPIPropertyInfo)
+	android.AssertArrayString(t, "split packages", []string{"sdklibrary", "newlibrary"}, info.SplitPackages)
+	android.AssertArrayString(t, "package prefixes", []string{"newlibrary.all.mine"}, info.PackagePrefixes)
+	android.AssertArrayString(t, "single packages", []string{"newlibrary.mine"}, info.SinglePackages)
+	for _, c := range HiddenAPIFlagFileCategories {
+		expectedMaxTargetQPaths := []string(nil)
+		if c.PropertyName == "max_target_q" {
+			expectedMaxTargetQPaths = []string{"my-new-max-target-q.txt"}
+		}
+		android.AssertPathsRelativeToTopEquals(t, c.PropertyName, expectedMaxTargetQPaths, info.FlagFilesByCategory[c])
+	}
+
+	// Make sure that the signature-patterns.csv is passed all the appropriate package properties
+	// from the bootclasspath_fragment and its contents.
+	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common")
+	rule := fragment.Output("modular-hiddenapi/signature-patterns.csv")
+	expectedCommand := strings.Join([]string{
+		"--split-package newlibrary",
+		"--split-package sdklibrary",
+		"--package-prefix newlibrary.all.mine",
+		"--package-prefix sdklibrary.all.mine",
+		"--single-package newlibrary.mine",
+		"--single-package sdklibrary",
+	}, " ")
+	android.AssertStringDoesContain(t, "signature patterns command", rule.RuleParams.Command, expectedCommand)
+}
+
 func TestBootclasspathFragment_Test(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
diff --git a/java/classpath_element.go b/java/classpath_element.go
index 753e7f8..4962916 100644
--- a/java/classpath_element.go
+++ b/java/classpath_element.go
@@ -97,11 +97,11 @@
 // the list with its Contents field.
 //
 // Requirements/Assumptions:
-// * A fragment can be associated with more than one apex but each apex must only be associated with
-//   a single fragment from the fragments list.
-// * All of a fragment's contents must appear as a contiguous block in the same order in the
-//   libraries list.
-// * Each library must only appear in a single fragment.
+//   - A fragment can be associated with more than one apex but each apex must only be associated with
+//     a single fragment from the fragments list.
+//   - All of a fragment's contents must appear as a contiguous block in the same order in the
+//     libraries list.
+//   - Each library must only appear in a single fragment.
 //
 // The apex is used to identify which libraries belong to which fragment. First a mapping is created
 // from apex to fragment. Then the libraries are iterated over and any library in an apex is
@@ -109,13 +109,15 @@
 // standalone and have their own element.
 //
 // e.g. Given the following input:
-//     libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
-//     fragments: com.android.art:art-bootclasspath-fragment
+//
+//	libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
+//	fragments: com.android.art:art-bootclasspath-fragment
 //
 // Then this will return:
-//     ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
-//     ClasspathLibraryElement(framework),
-//     ClasspathLibraryElement(ext),
+//
+//	ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
+//	ClasspathLibraryElement(framework),
+//	ClasspathLibraryElement(ext),
 func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
 	// Create a map from apex name to the fragment module. This makes it easy to find the fragment
 	// associated with a particular apex.
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index ca27528..259e977 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -131,14 +131,14 @@
 					// TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
 					if s.minSdkVersion.Specified() {
 						if s.minSdkVersion.ApiLevel.IsCurrent() {
-							jar.minSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
+							jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 						} else {
 							jar.minSdkVersion = s.minSdkVersion.ApiLevel.String()
 						}
 					}
 					if s.maxSdkVersion.Specified() {
 						if s.maxSdkVersion.ApiLevel.IsCurrent() {
-							jar.maxSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
+							jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 						} else {
 							jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String()
 						}
diff --git a/java/config/config.go b/java/config/config.go
index 3ca9bad..b026d73 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -124,6 +124,10 @@
 		// This is set up and guaranteed by soong_ui
 		return ctx.Config().Getenv("ANDROID_JAVA_HOME")
 	})
+	pctx.VariableFunc("Java11Home", func(ctx android.PackageVarContext) string {
+		// This is set up and guaranteed by soong_ui
+		return ctx.Config().Getenv("ANDROID_JAVA11_HOME")
+	})
 	pctx.VariableFunc("JlinkVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
 			return override
@@ -137,18 +141,19 @@
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
+	pctx.SourcePathVariable("Java11Toolchain", "${Java11Home}/bin")
 	pctx.SourcePathVariableWithEnvOverride("JavacCmd",
 		"${JavaToolchain}/javac", "ALTERNATE_JAVAC")
 	pctx.SourcePathVariable("JavaCmd", "${JavaToolchain}/java")
 	pctx.SourcePathVariable("JarCmd", "${JavaToolchain}/jar")
-	pctx.SourcePathVariable("JavadocCmd", "${JavaToolchain}/javadoc")
+	pctx.SourcePathVariable("JavadocCmd", "${Java11Toolchain}/javadoc")
 	pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
 	pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
 	pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
 	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
-	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file.py")
+	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
@@ -267,7 +272,7 @@
 
 // JavadocCmd returns a SourcePath object with the path to the java command.
 func JavadocCmd(ctx android.PathContext) android.SourcePath {
-	return javaTool(ctx, "javadoc")
+	return java11Tool(ctx, "javadoc")
 }
 
 func javaTool(ctx android.PathContext, tool string) android.SourcePath {
@@ -281,6 +286,17 @@
 
 }
 
+func java11Tool(ctx android.PathContext, tool string) android.SourcePath {
+	type javaToolKey string
+
+	key := android.NewCustomOnceKey(javaToolKey(tool))
+
+	return ctx.Config().OnceSourcePath(key, func() android.SourcePath {
+		return java11Toolchain(ctx).Join(ctx, tool)
+	})
+
+}
+
 var javaToolchainKey = android.NewOnceKey("javaToolchain")
 
 func javaToolchain(ctx android.PathContext) android.SourcePath {
@@ -289,6 +305,14 @@
 	})
 }
 
+var java11ToolchainKey = android.NewOnceKey("java11Toolchain")
+
+func java11Toolchain(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(java11ToolchainKey, func() android.SourcePath {
+		return java11Home(ctx).Join(ctx, "bin")
+	})
+}
+
 var javaHomeKey = android.NewOnceKey("javaHome")
 
 func javaHome(ctx android.PathContext) android.SourcePath {
@@ -297,3 +321,12 @@
 		return android.PathForSource(ctx, ctx.Config().Getenv("ANDROID_JAVA_HOME"))
 	})
 }
+
+var java11HomeKey = android.NewOnceKey("java11Home")
+
+func java11Home(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(java11HomeKey, func() android.SourcePath {
+		// This is set up and guaranteed by soong_ui
+		return android.PathForSource(ctx, ctx.Config().Getenv("ANDROID_JAVA11_HOME"))
+	})
+}
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 513c606..bfd5cf8 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -84,12 +84,36 @@
     ],
 }
 
+// Same as core-current-stubs-for-system-modules, but android annotations are
+// stripped.
+java_library {
+    name: "core-current-stubs-for-system-modules-no-annotations",
+    visibility: ["//development/sdk"],
+    static_libs: [
+        "core-current-stubs-for-system-modules",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    dists: [
+        {
+            // Legacy dist location for the public file.
+            dest: "core-for-system-modules-no-annotations.jar",
+            targets: dist_targets,
+        },
+        {
+            dest: "system-modules/public/core-for-system-modules-no-annotations.jar",
+            targets: dist_targets,
+        },
+    ],
+    jarjar_rules: "jarjar-strip-annotations-rules.txt",
+}
+
 // Used when compiling higher-level code against core.current.stubs.
 java_system_modules {
     name: "core-public-stubs-system-modules",
     visibility: ["//visibility:public"],
     libs: [
-        "core-current-stubs-for-system-modules",
+        "core-current-stubs-for-system-modules-no-annotations",
     ],
 }
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index b4cd07a..4e416fc 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -418,7 +418,6 @@
 //
 // The location is passed as an argument to the ART tools like dex2oat instead of the real path.
 // ART tools will then reconstruct the architecture-specific real path.
-//
 func (image *bootImageVariant) imageLocations() (imageLocationsOnHost []string, imageLocationsOnDevice []string) {
 	if image.extends != nil {
 		imageLocationsOnHost, imageLocationsOnDevice = image.extends.getVariant(image.target).imageLocations()
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 9663922..fc95184 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -158,9 +158,7 @@
 	Compat_config *string `android:"path"`
 }
 
-//
 // Common flags passed down to build rule
-//
 type droiddocBuilderFlags struct {
 	bootClasspathArgs  string
 	classpathArgs      string
@@ -193,9 +191,7 @@
 	return false
 }
 
-//
 // Javadoc
-//
 type Javadoc struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -318,7 +314,7 @@
 	outSrcFiles := make(android.Paths, 0, len(srcFiles))
 	var aidlSrcs android.Paths
 
-	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
+	aidlIncludeFlags := genAidlIncludeFlags(ctx, srcFiles, android.Paths{})
 
 	for _, srcFile := range srcFiles {
 		switch srcFile.Ext() {
@@ -548,9 +544,7 @@
 	rule.Build("javadoc", "javadoc")
 }
 
-//
 // Droiddoc
-//
 type Droiddoc struct {
 	Javadoc
 
@@ -761,6 +755,7 @@
 	return rule.Command().
 		BuiltTool("dokka").
 		Flag(config.JavacVmFlags).
+		Flag("-J--add-opens=java.base/java.lang=ALL-UNNAMED").
 		Flag(srcJarDir.String()).
 		FlagWithInputList("-classpath ", dokkaClasspath, ":").
 		FlagWithArg("-format ", "dac").
@@ -827,9 +822,7 @@
 	rule.Build("javadoc", desc)
 }
 
-//
 // Exported Droiddoc Directory
-//
 var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
 
 type ExportedDroiddocDirProperties struct {
@@ -862,9 +855,7 @@
 	d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
 }
 
-//
 // Defaults
-//
 type DocDefaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 932fb19..d9efb40 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -42,19 +43,14 @@
 	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
 }
 
-//
 // Droidstubs
-//
 type Droidstubs struct {
 	Javadoc
 	android.SdkBase
 
 	properties              DroidstubsProperties
-	apiFile                 android.WritablePath
-	apiXmlFile              android.WritablePath
-	lastReleasedApiXmlFile  android.WritablePath
-	privateApiFile          android.WritablePath
-	removedApiFile          android.WritablePath
+	apiFile                 android.Path
+	removedApiFile          android.Path
 	nullabilityWarningsFile android.WritablePath
 
 	checkCurrentApiTimestamp      android.WritablePath
@@ -68,9 +64,6 @@
 	annotationsZip android.WritablePath
 	apiVersionsXml android.WritablePath
 
-	apiFilePath        android.Path
-	removedApiFilePath android.Path
-
 	metadataZip android.WritablePath
 	metadataDir android.WritablePath
 }
@@ -150,6 +143,10 @@
 	// if set to true, collect the values used by the Dev tools and
 	// write them in files packaged with the SDK. Defaults to false.
 	Write_sdk_values *bool
+
+	// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
+	// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
+	Extensions_info_file *string `android:"path"`
 }
 
 // Used by xsd_config
@@ -206,9 +203,9 @@
 		return android.Paths{d.docZip}, nil
 	case ".api.txt", android.DefaultDistTag:
 		// This is the default dist path for dist properties that have no tag property.
-		return android.Paths{d.apiFilePath}, nil
+		return android.Paths{d.apiFile}, nil
 	case ".removed-api.txt":
-		return android.Paths{d.removedApiFilePath}, nil
+		return android.Paths{d.removedApiFile}, nil
 	case ".annotations.zip":
 		return android.Paths{d.annotationsZip}, nil
 	case ".api_versions.xml":
@@ -223,11 +220,11 @@
 }
 
 func (d *Droidstubs) ApiFilePath() android.Path {
-	return d.apiFilePath
+	return d.apiFile
 }
 
 func (d *Droidstubs) RemovedApiFilePath() android.Path {
-	return d.removedApiFilePath
+	return d.removedApiFile
 }
 
 func (d *Droidstubs) StubsSrcJar() android.Path {
@@ -270,24 +267,24 @@
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Api_filename) != "" {
 		filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
-		d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
-		cmd.FlagWithOutput("--api ", d.apiFile)
-		d.apiFilePath = d.apiFile
+		uncheckedApiFile := android.PathForModuleOut(ctx, "metalava", filename)
+		cmd.FlagWithOutput("--api ", uncheckedApiFile)
+		d.apiFile = uncheckedApiFile
 	} else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
 		// If check api is disabled then make the source file available for export.
-		d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
+		d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile)
 	}
 
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Removed_api_filename) != "" {
 		filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
-		d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
-		cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
-		d.removedApiFilePath = d.removedApiFile
+		uncheckedRemovedFile := android.PathForModuleOut(ctx, "metalava", filename)
+		cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile)
+		d.removedApiFile = uncheckedRemovedFile
 	} else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
 		// If check api is disabled then make the source removed api file available for export.
-		d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
+		d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
 	}
 
 	if Bool(d.properties.Write_sdk_values) {
@@ -406,9 +403,20 @@
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
 
 	var dirs []string
+	var extensions_dir string
 	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
+			extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`)
+
+			// Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
+			// ideally this should be read from prebuiltApis.properties.Extensions_*
 			for _, dep := range t.deps {
+				if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
+					if extensions_dir == "" {
+						extensions_dir = t.dir.String() + "/extensions"
+					}
+					cmd.Implicit(dep)
+				}
 				if dep.Base() == filename {
 					cmd.Implicit(dep)
 				}
@@ -453,6 +461,16 @@
 			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
 		}
 	}
+
+	if d.properties.Extensions_info_file != nil {
+		if extensions_dir == "" {
+			ctx.ModuleErrorf("extensions_info_file set, but no SDK extension dirs found")
+		}
+		info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file)
+		cmd.Implicit(info_file)
+		cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir)
+		cmd.FlagWithArg("--sdk-extensions-info ", info_file.String())
+	}
 }
 
 func metalavaUseRbe(ctx android.ModuleContext) bool {
@@ -697,16 +715,87 @@
 
 	zipSyncCleanupCmd(rule, srcJarDir)
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
-		d.generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx)
-
-		// Make sure that whenever the API stubs are generated that the current checked in API files are
-		// checked to make sure that they are up-to-date.
-		cmd.Validation(d.checkCurrentApiTimestamp)
-	}
-
 	rule.Build("metalava", "metalava merged")
 
+	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
+
+		if len(d.Javadoc.properties.Out) > 0 {
+			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+		}
+
+		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+
+		if baselineFile.Valid() {
+			ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
+		}
+
+		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
+
+		rule := android.NewRuleBuilder(pctx, ctx)
+
+		// Diff command line.
+		// -F matches the closest "opening" line, such as "package android {"
+		// and "  public class Intent {".
+		diff := `diff -u -F '{ *$'`
+
+		rule.Command().Text("( true")
+		rule.Command().
+			Text(diff).
+			Input(apiFile).Input(d.apiFile)
+
+		rule.Command().
+			Text(diff).
+			Input(removedApiFile).Input(d.removedApiFile)
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`You have tried to change the API from what has been previously approved.\n\n`+
+			`To make these errors go away, you have two choices:\n`+
+			`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
+			`      to the new methods, etc. shown in the above diff.\n\n`+
+			`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
+			`         m %s-update-current-api\n\n`+
+			`      To submit the revised current.txt to the main Android repository,\n`+
+			`      you will need approval.\n`+
+			`******************************\n`, ctx.ModuleName())
+
+		rule.Command().
+			Text("touch").Output(d.checkCurrentApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build("metalavaCurrentApiCheck", "check current API")
+
+		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
+
+		// update API rule
+		rule = android.NewRuleBuilder(pctx, ctx)
+
+		rule.Command().Text("( true")
+
+		rule.Command().
+			Text("cp").Flag("-f").
+			Input(d.apiFile).Flag(apiFile.String())
+
+		rule.Command().
+			Text("cp").Flag("-f").
+			Input(d.removedApiFile).Flag(removedApiFile.String())
+
+		msg = "failed to update public API"
+
+		rule.Command().
+			Text("touch").Output(d.updateCurrentApiTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build("metalavaCurrentApiUpdate", "update current API")
+	}
+
 	if String(d.properties.Check_nullability_warnings) != "" {
 		if d.nullabilityWarningsFile == nil {
 			ctx.PropertyErrorf("check_nullability_warnings",
@@ -743,84 +832,6 @@
 	}
 }
 
-func (d *Droidstubs) generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx android.ModuleContext) {
-	if len(d.Javadoc.properties.Out) > 0 {
-		ctx.PropertyErrorf("out", "out property may not be combined with check_api")
-	}
-
-	apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
-	removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
-	baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
-
-	if baselineFile.Valid() {
-		ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
-	}
-
-	d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	// Diff command line.
-	// -F matches the closest "opening" line, such as "package android {"
-	// and "  public class Intent {".
-	diff := `diff -u -F '{ *$'`
-
-	rule.Command().Text("( true")
-	rule.Command().
-		Text(diff).
-		Input(apiFile).Input(d.apiFile)
-
-	rule.Command().
-		Text(diff).
-		Input(removedApiFile).Input(d.removedApiFile)
-
-	msg := fmt.Sprintf(`\n******************************\n`+
-		`You have tried to change the API from what has been previously approved.\n\n`+
-		`To make these errors go away, you have two choices:\n`+
-		`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
-		`      to the new methods, etc. shown in the above diff.\n\n`+
-		`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
-		`         m %s-update-current-api\n\n`+
-		`      To submit the revised current.txt to the main Android repository,\n`+
-		`      you will need approval.\n`+
-		`******************************\n`, ctx.ModuleName())
-
-	rule.Command().
-		Text("touch").Output(d.checkCurrentApiTimestamp).
-		Text(") || (").
-		Text("echo").Flag("-e").Flag(`"` + msg + `"`).
-		Text("; exit 38").
-		Text(")")
-
-	rule.Build("metalavaCurrentApiCheck", "check current API")
-
-	d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
-
-	// update API rule
-	rule = android.NewRuleBuilder(pctx, ctx)
-
-	rule.Command().Text("( true")
-
-	rule.Command().
-		Text("cp").Flag("-f").
-		Input(d.apiFile).Flag(apiFile.String())
-
-	rule.Command().
-		Text("cp").Flag("-f").
-		Input(d.removedApiFile).Flag(removedApiFile.String())
-
-	msg = "failed to update public API"
-
-	rule.Command().
-		Text("touch").Output(d.updateCurrentApiTimestamp).
-		Text(") || (").
-		Text("echo").Flag("-e").Flag(`"` + msg + `"`).
-		Text("; exit 38").
-		Text(")")
-
-	rule.Build("metalavaCurrentApiUpdate", "update current API")
-}
-
 func StubsDefaultsFactory() android.Module {
 	module := &DocDefaults{}
 
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 9fdfdde..2443692 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -259,3 +259,33 @@
 		t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
 	}
 }
+
+func TestDroidstubsWithSdkExtensions(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droiddoc_exported_dir {
+			name: "sdk-dir",
+			path: "sdk",
+		}
+
+		droidstubs {
+			name: "baz-stubs",
+			api_levels_annotations_dirs: ["sdk-dir"],
+			api_levels_annotations_enabled: true,
+			extensions_info_file: ":info-file",
+		}
+
+		filegroup {
+			name: "info-file",
+			srcs: ["sdk/extensions/info.txt"],
+		}
+		`,
+		map[string][]byte{
+			"sdk/extensions/1/public/some-mainline-module-stubs.jar": nil,
+			"sdk/extensions/info.txt":                                nil,
+		})
+	m := ctx.ModuleForTests("baz-stubs", "android_common")
+	manifest := m.Output("metalava.sbox.textproto")
+	cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
+	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
+}
diff --git a/java/gen.go b/java/gen.go
index 1572bf0..638da25 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -116,12 +117,31 @@
 	return javaFile
 }
 
-func genAidlIncludeFlags(srcFiles android.Paths) string {
+// genAidlIncludeFlags returns additional include flags based on the relative path
+// of each .aidl file passed in srcFiles. excludeDirs is a list of paths relative to
+// the Android checkout root that should not be included in the returned flags.
+func genAidlIncludeFlags(ctx android.PathContext, srcFiles android.Paths, excludeDirs android.Paths) string {
 	var baseDirs []string
+	excludeDirsStrings := excludeDirs.Strings()
 	for _, srcFile := range srcFiles {
 		if srcFile.Ext() == ".aidl" {
 			baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel())
-			if baseDir != "" && !android.InList(baseDir, baseDirs) {
+			baseDir = filepath.Clean(baseDir)
+			baseDirSeen := android.InList(baseDir, baseDirs) || android.InList(baseDir, excludeDirsStrings)
+
+			// For go/bp2build mixed builds, a file may be listed under a
+			// directory in the Bazel output tree that is symlinked to a
+			// directory under the android source tree. We should only
+			// include one copy of this directory so that the AIDL tool
+			// doesn't find multiple definitions of the same AIDL class.
+			// This code comes into effect when filegroups are used in mixed builds.
+			bazelPathPrefix := android.PathForBazelOut(ctx, "").String()
+			bazelBaseDir, err := filepath.Rel(bazelPathPrefix, baseDir)
+			bazelBaseDirSeen := err == nil &&
+				android.InList(bazelBaseDir, baseDirs) ||
+				android.InList(bazelBaseDir, excludeDirsStrings)
+
+			if baseDir != "" && !baseDirSeen && !bazelBaseDirSeen {
 				baseDirs = append(baseDirs, baseDir)
 			}
 		}
@@ -136,8 +156,6 @@
 	var protoSrcs android.Paths
 	var aidlSrcs android.Paths
 
-	aidlIncludeFlags := genAidlIncludeFlags(srcFiles)
-
 	for _, srcFile := range srcFiles {
 		switch srcFile.Ext() {
 		case ".aidl":
@@ -168,7 +186,7 @@
 				individualFlags[aidlSrc.String()] = flags
 			}
 		}
-		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps)
+		srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags, individualFlags, flags.aidlDeps)
 		outSrcFiles = append(outSrcFiles, srcJarFiles...)
 	}
 
diff --git a/java/genrule.go b/java/genrule.go
index 5047c41..208e1f4 100644
--- a/java/genrule.go
+++ b/java/genrule.go
@@ -43,23 +43,23 @@
 //
 // Use a java_genrule to package generated java resources:
 //
-//     java_genrule {
-//         name: "generated_resources",
-//         tools: [
-//             "generator",
-//             "soong_zip",
-//         ],
-//         srcs: ["generator_inputs/**/*"],
-//         out: ["generated_android_icu4j_resources.jar"],
-//         cmd: "$(location generator) $(in) -o $(genDir) " +
-//             "&& $(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
-//     }
+//	java_genrule {
+//	    name: "generated_resources",
+//	    tools: [
+//	        "generator",
+//	        "soong_zip",
+//	    ],
+//	    srcs: ["generator_inputs/**/*"],
+//	    out: ["generated_android_icu4j_resources.jar"],
+//	    cmd: "$(location generator) $(in) -o $(genDir) " +
+//	        "&& $(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+//	}
 //
-//     java_library {
-//         name: "lib_with_generated_resources",
-//         srcs: ["src/**/*.java"],
-//         static_libs: ["generated_resources"],
-//     }
+//	java_library {
+//	    name: "lib_with_generated_resources",
+//	    srcs: ["src/**/*.java"],
+//	    static_libs: ["generated_resources"],
+//	}
 func GenRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index c90b2ff..7b67803 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -378,32 +378,37 @@
 // with one Java package per line. All members of all classes within that package (but not nested
 // packages) will be updated in a property specific way.
 type HiddenAPIFlagFileProperties struct {
-	// Marks each signature in the referenced files as being unsupported.
-	Unsupported []string `android:"path"`
+	Hidden_api struct {
+		// Marks each signature in the referenced files as being unsupported.
+		Unsupported []string `android:"path"`
 
-	// Marks each signature in the referenced files as being unsupported because it has been removed.
-	// Any conflicts with other flags are ignored.
-	Removed []string `android:"path"`
+		// Marks each signature in the referenced files as being unsupported because it has been
+		// removed. Any conflicts with other flags are ignored.
+		Removed []string `android:"path"`
 
-	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= R
-	// and low priority.
-	Max_target_r_low_priority []string `android:"path"`
+		// Marks each signature in the referenced files as being supported only for
+		// targetSdkVersion <= R and low priority.
+		Max_target_r_low_priority []string `android:"path"`
 
-	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= Q.
-	Max_target_q []string `android:"path"`
+		// Marks each signature in the referenced files as being supported only for
+		// targetSdkVersion <= Q.
+		Max_target_q []string `android:"path"`
 
-	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= P.
-	Max_target_p []string `android:"path"`
+		// Marks each signature in the referenced files as being supported only for
+		// targetSdkVersion <= P.
+		Max_target_p []string `android:"path"`
 
-	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= O
-	// and low priority. Any conflicts with other flags are ignored.
-	Max_target_o_low_priority []string `android:"path"`
+		// Marks each signature in the referenced files as being supported only for
+		// targetSdkVersion <= O
+		// and low priority. Any conflicts with other flags are ignored.
+		Max_target_o_low_priority []string `android:"path"`
 
-	// Marks each signature in the referenced files as being blocked.
-	Blocked []string `android:"path"`
+		// Marks each signature in the referenced files as being blocked.
+		Blocked []string `android:"path"`
 
-	// Marks each signature in every package in the referenced files as being unsupported.
-	Unsupported_packages []string `android:"path"`
+		// Marks each signature in every package in the referenced files as being unsupported.
+		Unsupported_packages []string `android:"path"`
+	}
 }
 
 type hiddenAPIFlagFileCategory struct {
@@ -428,19 +433,30 @@
 	// See HiddenAPIFlagFileProperties.Removed
 	PropertyName: "removed",
 	propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-		return properties.Removed
+		return properties.Hidden_api.Removed
 	},
 	commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 		command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
 	},
 }
 
-var HiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
+type hiddenAPIFlagFileCategories []*hiddenAPIFlagFileCategory
+
+func (c hiddenAPIFlagFileCategories) byProperty(name string) *hiddenAPIFlagFileCategory {
+	for _, category := range c {
+		if category.PropertyName == name {
+			return category
+		}
+	}
+	panic(fmt.Errorf("no category exists with property name %q in %v", name, c))
+}
+
+var HiddenAPIFlagFileCategories = hiddenAPIFlagFileCategories{
 	// See HiddenAPIFlagFileProperties.Unsupported
 	{
 		PropertyName: "unsupported",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Unsupported
+			return properties.Hidden_api.Unsupported
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--unsupported ", path)
@@ -451,7 +467,7 @@
 	{
 		PropertyName: "max_target_r_low_priority",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Max_target_r_low_priority
+			return properties.Hidden_api.Max_target_r_low_priority
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
@@ -461,7 +477,7 @@
 	{
 		PropertyName: "max_target_q",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Max_target_q
+			return properties.Hidden_api.Max_target_q
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--max-target-q ", path)
@@ -471,7 +487,7 @@
 	{
 		PropertyName: "max_target_p",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Max_target_p
+			return properties.Hidden_api.Max_target_p
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--max-target-p ", path)
@@ -481,7 +497,7 @@
 	{
 		PropertyName: "max_target_o_low_priority",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Max_target_o_low_priority
+			return properties.Hidden_api.Max_target_o_low_priority
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
@@ -491,7 +507,7 @@
 	{
 		PropertyName: "blocked",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Blocked
+			return properties.Hidden_api.Blocked
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--blocked ", path)
@@ -501,7 +517,7 @@
 	{
 		PropertyName: "unsupported_packages",
 		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Unsupported_packages
+			return properties.Hidden_api.Unsupported_packages
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
 			command.FlagWithInput("--unsupported ", path).Flag("--packages ")
@@ -512,13 +528,20 @@
 // FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
 type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths
 
-// append appends the supplied flags files to the corresponding category in this map.
+// append the supplied flags files to the corresponding category in this map.
 func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
 	for _, category := range HiddenAPIFlagFileCategories {
 		s[category] = append(s[category], other[category]...)
 	}
 }
 
+// sort the paths for each category in this map.
+func (s FlagFilesByCategory) sort() {
+	for category, value := range s {
+		s[category] = android.SortedUniquePaths(value)
+	}
+}
+
 // HiddenAPIInfo contains information provided by the hidden API processing.
 //
 // That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
@@ -686,13 +709,70 @@
 	return stubDexJars
 }
 
-// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
-// needed for hidden API flag generation.
-type HiddenAPIFlagInput struct {
+type HiddenAPIPropertyInfo struct {
 	// FlagFilesByCategory contains the flag files that override the initial flags that are derived
 	// from the stub dex files.
 	FlagFilesByCategory FlagFilesByCategory
 
+	// See HiddenAPIFlagFileProperties.Package_prefixes
+	PackagePrefixes []string
+
+	// See HiddenAPIFlagFileProperties.Single_packages
+	SinglePackages []string
+
+	// See HiddenAPIFlagFileProperties.Split_packages
+	SplitPackages []string
+}
+
+var hiddenAPIPropertyInfoProvider = blueprint.NewProvider(HiddenAPIPropertyInfo{})
+
+// newHiddenAPIPropertyInfo creates a new initialized HiddenAPIPropertyInfo struct.
+func newHiddenAPIPropertyInfo() HiddenAPIPropertyInfo {
+	return HiddenAPIPropertyInfo{
+		FlagFilesByCategory: FlagFilesByCategory{},
+	}
+}
+
+// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
+// supplied properties and stores them in this struct.
+func (i *HiddenAPIPropertyInfo) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
+	for _, category := range HiddenAPIFlagFileCategories {
+		paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
+		i.FlagFilesByCategory[category] = paths
+	}
+}
+
+// extractPackageRulesFromProperties extracts the package rules that are specified in the supplied
+// properties and stores them in this struct.
+func (i *HiddenAPIPropertyInfo) extractPackageRulesFromProperties(p *HiddenAPIPackageProperties) {
+	i.PackagePrefixes = p.Hidden_api.Package_prefixes
+	i.SinglePackages = p.Hidden_api.Single_packages
+	i.SplitPackages = p.Hidden_api.Split_packages
+}
+
+func (i *HiddenAPIPropertyInfo) gatherPropertyInfo(ctx android.ModuleContext, contents []android.Module) {
+	for _, module := range contents {
+		if ctx.OtherModuleHasProvider(module, hiddenAPIPropertyInfoProvider) {
+			info := ctx.OtherModuleProvider(module, hiddenAPIPropertyInfoProvider).(HiddenAPIPropertyInfo)
+			i.FlagFilesByCategory.append(info.FlagFilesByCategory)
+			i.PackagePrefixes = append(i.PackagePrefixes, info.PackagePrefixes...)
+			i.SinglePackages = append(i.SinglePackages, info.SinglePackages...)
+			i.SplitPackages = append(i.SplitPackages, info.SplitPackages...)
+		}
+	}
+
+	// Dedup and sort the information to ensure consistent builds.
+	i.FlagFilesByCategory.sort()
+	i.PackagePrefixes = android.SortedUniqueStrings(i.PackagePrefixes)
+	i.SinglePackages = android.SortedUniqueStrings(i.SinglePackages)
+	i.SplitPackages = android.SortedUniqueStrings(i.SplitPackages)
+}
+
+// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
+// needed for hidden API flag generation.
+type HiddenAPIFlagInput struct {
+	HiddenAPIPropertyInfo
+
 	// StubDexJarsByScope contains the stub dex jars for different *HiddenAPIScope and which determine
 	// the initial flags for each dex member.
 	StubDexJarsByScope StubDexJarsByModule
@@ -714,10 +794,10 @@
 	RemovedTxtFiles android.Paths
 }
 
-// newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct.
+// newHiddenAPIFlagInput creates a new initialized HiddenAPIFlagInput struct.
 func newHiddenAPIFlagInput() HiddenAPIFlagInput {
 	input := HiddenAPIFlagInput{
-		FlagFilesByCategory:          FlagFilesByCategory{},
+		HiddenAPIPropertyInfo:        newHiddenAPIPropertyInfo(),
 		StubDexJarsByScope:           StubDexJarsByModule{},
 		DependencyStubDexJarsByScope: StubDexJarsByModule{},
 		AdditionalStubDexJarsByScope: StubDexJarsByModule{},
@@ -773,15 +853,6 @@
 	i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles)
 }
 
-// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
-// supplied properties and stores them in this struct.
-func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
-	for _, category := range HiddenAPIFlagFileCategories {
-		paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
-		i.FlagFilesByCategory[category] = paths
-	}
-}
-
 func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule {
 	transitive := i.DependencyStubDexJarsByScope
 	transitive.addStubDexJarsByModule(i.StubDexJarsByScope)
diff --git a/java/jacoco.go b/java/jacoco.go
index e11c2ce..f8012b8 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -47,6 +47,34 @@
 		"strippedJar", "stripSpec", "tmpDir", "tmpJar")
 )
 
+func jacocoDepsMutator(ctx android.BottomUpMutatorContext) {
+	type instrumentable interface {
+		shouldInstrument(ctx android.BaseModuleContext) bool
+		shouldInstrumentInApex(ctx android.BaseModuleContext) bool
+		setInstrument(value bool)
+	}
+
+	j, ok := ctx.Module().(instrumentable)
+	if !ctx.Module().Enabled() || !ok {
+		return
+	}
+
+	if j.shouldInstrumentInApex(ctx) {
+		j.setInstrument(true)
+	}
+
+	if j.shouldInstrument(ctx) && ctx.ModuleName() != "jacocoagent" {
+		// We can use AddFarVariationDependencies here because, since this dep
+		// is added as libs only (i.e. a compiletime CLASSPATH entry only),
+		// the first variant of jacocoagent is sufficient to prevent
+		// compile time errors.
+		// At this stage in the build, AddVariationDependencies is not always
+		// able to procure a variant of jacocoagent that matches the calling
+		// module.
+		ctx.AddFarVariationDependencies(ctx.Module().Target().Variations(), libTag, "jacocoagent")
+	}
+}
+
 // Instruments a jar using the Jacoco command line interface.  Uses stripSpec to extract a subset
 // of the classes in inputJar into strippedJar, instruments strippedJar into tmpJar, and then
 // combines the classes in tmpJar with inputJar (preferring the instrumented classes in tmpJar)
diff --git a/java/java.go b/java/java.go
index 481c625..d04e52a 100644
--- a/java/java.go
+++ b/java/java.go
@@ -66,6 +66,8 @@
 	// to support the checks in dexpreoptDisabled().
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel()
+		// needs access to ApexInfoProvider which is available after variant creation
+		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
 	ctx.RegisterSingletonType("logtags", LogtagsSingleton)
@@ -629,6 +631,9 @@
 }
 
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+
+	j.provideHiddenAPIPropertyInfo(ctx)
+
 	j.sdkVersion = j.SdkVersion(ctx)
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 	j.maxSdkVersion = j.MaxSdkVersion(ctx)
@@ -852,11 +857,10 @@
 
 // Test option struct.
 type TestOptions struct {
+	android.CommonTestOptions
+
 	// a list of extra test configuration files that should be installed with the module.
 	Extra_test_configs []string `android:"path,arch_variant"`
-
-	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
-	Unit_test *bool
 }
 
 type testProperties struct {
@@ -1262,7 +1266,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.test = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
 	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
@@ -1278,7 +1282,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.test = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
@@ -2257,16 +2261,27 @@
 	StaticDeps bazel.LabelListAttribute
 }
 
-// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules
-// and also separates dependencies into dynamic dependencies and static dependencies.
-// Each corresponding Bazel target type, can have a different method for handling
-// dynamic vs. static dependencies, and so these are returned to the calling function.
 type eventLogTagsAttributes struct {
 	Srcs bazel.LabelListAttribute
 }
 
+type aidlLibraryAttributes struct {
+	Srcs bazel.LabelListAttribute
+}
+
+type javaAidlLibraryAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
+// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules
+// and also separates dependencies into dynamic dependencies and static dependencies.
+// Each corresponding Bazel target type, can have a different method for handling
+// dynamic vs. static dependencies, and so these are returned to the calling function.
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) {
 	var srcs bazel.LabelListAttribute
+	var deps bazel.LabelList
+	var staticDeps bazel.LabelList
+
 	archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
@@ -2280,18 +2295,18 @@
 	javaSrcPartition := "java"
 	protoSrcPartition := "proto"
 	logtagSrcPartition := "logtag"
+	aidlSrcPartition := "aidl"
 	srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
 		javaSrcPartition:   bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
 		logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}},
 		protoSrcPartition:  android.ProtoSrcLabelPartition,
+		aidlSrcPartition:   android.AidlSrcLabelPartition,
 	})
 
 	javaSrcs := srcPartitions[javaSrcPartition]
 
-	var logtagsSrcs bazel.LabelList
 	if !srcPartitions[logtagSrcPartition].IsEmpty() {
 		logtagsLibName := m.Name() + "_logtags"
-		logtagsSrcs = bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}})
 		ctx.CreateBazelTargetModule(
 			bazel.BazelTargetModuleProperties{
 				Rule_class:        "event_log_tags",
@@ -2302,8 +2317,45 @@
 				Srcs: srcPartitions[logtagSrcPartition],
 			},
 		)
+
+		logtagsSrcs := bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}})
+		javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs))
 	}
-	javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs))
+
+	if !srcPartitions[aidlSrcPartition].IsEmpty() {
+		aidlLibs, aidlSrcs := srcPartitions[aidlSrcPartition].Partition(func(src bazel.Label) bool {
+			return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName)
+		})
+
+		if !aidlSrcs.IsEmpty() {
+			aidlLibName := m.Name() + "_aidl_library"
+			ctx.CreateBazelTargetModule(
+				bazel.BazelTargetModuleProperties{
+					Rule_class:        "aidl_library",
+					Bzl_load_location: "//build/bazel/rules/aidl:library.bzl",
+				},
+				android.CommonAttributes{Name: aidlLibName},
+				&aidlLibraryAttributes{
+					Srcs: aidlSrcs,
+				},
+			)
+			aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
+		}
+
+		javaAidlLibName := m.Name() + "_java_aidl_library"
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "java_aidl_library",
+				Bzl_load_location: "//build/bazel/rules/java:aidl_library.bzl",
+			},
+			android.CommonAttributes{Name: javaAidlLibName},
+			&javaAidlLibraryAttributes{
+				Deps: aidlLibs,
+			},
+		)
+
+		staticDeps.Add(&bazel.Label{Label: ":" + javaAidlLibName})
+	}
 
 	var javacopts []string
 	if m.properties.Javacflags != nil {
@@ -2329,14 +2381,10 @@
 		Javacopts: bazel.MakeStringListAttribute(javacopts),
 	}
 
-	depLabels := &javaDependencyLabels{}
-
-	var deps bazel.LabelList
 	if m.properties.Libs != nil {
 		deps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Libs))))
 	}
 
-	var staticDeps bazel.LabelList
 	if m.properties.Static_libs != nil {
 		staticDeps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Static_libs))))
 	}
@@ -2350,6 +2398,7 @@
 	// and so this should be a static dependency.
 	staticDeps.Add(protoDepLabel)
 
+	depLabels := &javaDependencyLabels{}
 	depLabels.Deps = bazel.MakeLabelListAttribute(deps)
 	depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
 
@@ -2374,7 +2423,7 @@
 			// TODO(b/220869005) remove forced dependency on current public android.jar
 			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import"))
 		}
-	} else if !depLabels.Deps.IsEmpty() {
+	} else if !deps.IsEmpty() {
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
 	}
 
diff --git a/java/java_test.go b/java/java_test.go
index 9e5cf0c..7f0cea7 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1287,6 +1287,41 @@
 	}
 }
 
+func TestAidlIncludeDirFromConvertedFileGroupWithPathPropInMixedBuilds(t *testing.T) {
+	bp := `
+	filegroup {
+		name: "foo_aidl",
+		srcs: ["aidl/foo/IFoo.aidl"],
+		path: "aidl/foo",
+		bazel_module: { label: "//:foo_aidl" },
+	}
+	java_library {
+		name: "foo",
+		srcs: [":foo_aidl"],
+	}
+`
+
+	outBaseDir := "out/bazel/output"
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.PrepareForTestWithFilegroup,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outBaseDir,
+				LabelToOutputFiles: map[string][]string{
+					"//:foo_aidl": []string{"aidl/foo/IFoo.aidl"},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+
+	aidlCommand := result.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	expectedAidlFlag := "-I" + outBaseDir + "/execroot/__main__/aidl/foo"
+	if !strings.Contains(aidlCommand, expectedAidlFlag) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+	}
+}
+
 func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
@@ -1712,3 +1747,37 @@
 	android.AssertDeepEquals(t, "Implementation/Resources JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationAndResourcesJars))
 	android.AssertDeepEquals(t, "Implementation JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationJars))
 }
+
+func TestGenAidlIncludeFlagsForMixedBuilds(t *testing.T) {
+	bazelOutputBaseDir := filepath.Join("out", "bazel")
+	result := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: bazelOutputBaseDir,
+			}
+		}),
+	).RunTest(t)
+
+	ctx := &android.TestPathContext{TestResult: result}
+
+	srcDirectory := filepath.Join("frameworks", "base")
+	srcDirectoryAlreadyIncluded := filepath.Join("frameworks", "base", "core", "java")
+	bazelSrcDirectory := android.PathForBazelOut(ctx, srcDirectory)
+	bazelSrcDirectoryAlreadyIncluded := android.PathForBazelOut(ctx, srcDirectoryAlreadyIncluded)
+	srcs := android.Paths{
+		android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl.aidl"),
+		android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl2.aidl"),
+		android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidlExclude.aidl"),
+		android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidl2Exclude.aidl"),
+	}
+	dirsAlreadyIncluded := android.Paths{
+		android.PathForTesting(srcDirectoryAlreadyIncluded),
+	}
+
+	expectedFlags := " -Iout/bazel/execroot/__main__/frameworks/base"
+	flags := genAidlIncludeFlags(ctx, srcs, dirsAlreadyIncluded)
+	if flags != expectedFlags {
+		t.Errorf("expected flags to be %q; was %q", expectedFlags, flags)
+	}
+}
diff --git a/java/lint.go b/java/lint.go
index c27ca98..931820d 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -61,6 +61,11 @@
 
 		// If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false.
 		Strict_updatability_linting *bool
+
+		// Treat the code in this module as test code for @VisibleForTesting enforcement.
+		// This will be true by default for test module types, false otherwise.
+		// If soong gets support for testonly, this flag should be replaced with that.
+		Test *bool
 	}
 }
 
@@ -74,7 +79,6 @@
 	classpath               android.Paths
 	classes                 android.Path
 	extraLintCheckJars      android.Paths
-	test                    bool
 	library                 bool
 	minSdkVersion           int
 	targetSdkVersion        int
@@ -210,7 +214,7 @@
 	return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
 }
 
-func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths {
 	projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
 	// Lint looks for a lint.xml file next to the project.xml file, give it one.
 	configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
@@ -229,7 +233,7 @@
 	if l.library {
 		cmd.Flag("--library")
 	}
-	if l.test {
+	if proptools.BoolDefault(l.properties.Lint.Test, false) {
 		cmd.Flag("--test")
 	}
 	if l.manifest != nil {
@@ -241,8 +245,7 @@
 
 	// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
 	// lint separately.
-	srcsList := android.PathForModuleOut(ctx, "lint-srcs.list")
-	cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
+	cmd.FlagWithInput("--srcs ", srcsList)
 
 	cmd.FlagWithInput("--generated_srcs ", srcJarList)
 
@@ -328,12 +331,18 @@
 
 	if l.minSdkVersion != l.compileSdkVersion {
 		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
-		_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
-		if len(filtered) != 0 {
-			ctx.PropertyErrorf("lint.warning_checks",
-				"Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
+		// Skip lint warning checks for NewApi warnings for libcore where they come from source
+		// files that reference the API they are adding (b/208656169).
+		if ctx.ModuleDir() != "libcore" {
+			_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
+
+			if len(filtered) != 0 {
+				ctx.PropertyErrorf("lint.warning_checks",
+					"Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
+			}
 		}
-		_, filtered = android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
+
+		_, filtered := android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
 		if len(filtered) != 0 {
 			ctx.PropertyErrorf("lint.disabled_checks",
 				"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
@@ -381,7 +390,11 @@
 		rule.Temporary(manifest)
 	}
 
-	lintPaths := l.writeLintProjectXML(ctx, rule)
+	srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list")
+	srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
+	rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList)
+
+	lintPaths := l.writeLintProjectXML(ctx, rule, srcsList)
 
 	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
 	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
@@ -441,6 +454,7 @@
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
 		Flag("--exitcode").
+		Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox
 		Flags(l.properties.Lint.Flags).
 		Implicit(annotationsZipPath).
 		Implicit(apiVersionsXMLPath)
@@ -466,6 +480,13 @@
 	// 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/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 1e27238..24f8253 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -62,7 +62,7 @@
 type platformBootclasspathProperties struct {
 	BootclasspathFragmentsDepsProperties
 
-	Hidden_api HiddenAPIFlagFileProperties
+	HiddenAPIFlagFileProperties
 }
 
 func platformBootclasspathFactory() android.SingletonModule {
@@ -372,7 +372,7 @@
 	temporaryInput := newHiddenAPIFlagInput()
 
 	// Create paths to the flag files specified in the properties.
-	temporaryInput.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api)
+	temporaryInput.extractFlagFilesFromProperties(ctx, &b.properties.HiddenAPIFlagFileProperties)
 
 	// Create the monolithic info, by starting with the flag files specified on this and then merging
 	// in information from all the fragment dependencies of this.
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 1c42495..655021f 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -280,7 +280,7 @@
 	return &platformCompatConfigSingleton{}
 }
 
-//============== merged_compat_config =================
+// ============== merged_compat_config =================
 type globalCompatConfigProperties struct {
 	// name of the file into which the metadata will be copied.
 	Filename *string
diff --git a/java/robolectric.go b/java/robolectric.go
index f719521..71ffdb1 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"
 )
 
 func init() {
@@ -344,7 +345,7 @@
 		&module.testProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.test = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
 	module.testProperties.Test_suites = []string{"robolectric-tests"}
 
diff --git a/java/rro.go b/java/rro.go
index 9c8c53b..c12e748 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -51,6 +51,9 @@
 	// Name of the signing certificate lineage file.
 	Lineage *string
 
+	// For overriding the --rotation-min-sdk-version property of apksig
+	RotationMinSdkVersion *string
+
 	// optional theme name. If specified, the overlay package will be applied
 	// only when the ro.boot.vendor.overlay.theme system property is set to the same value.
 	Theme *string
@@ -149,7 +152,10 @@
 	if lineage := String(r.properties.Lineage); lineage != "" {
 		lineageFile = android.PathForModuleSrc(ctx, lineage)
 	}
-	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile)
+
+	rotationMinSdkVersion := String(r.properties.RotationMinSdkVersion)
+
+	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile, rotationMinSdkVersion)
 	r.certificate = certificates[0]
 
 	r.outputFile = signed
diff --git a/java/rro_test.go b/java/rro_test.go
index be0d7ba..00ba5ba 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -33,6 +33,7 @@
 			name: "foo",
 			certificate: "platform",
 			lineage: "lineage.bin",
+			rotationMinSdkVersion: "32",
 			product_specific: true,
 			static_libs: ["bar"],
 			resource_libs: ["baz"],
@@ -89,13 +90,14 @@
 		t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
 	}
 
-	// Check cert signing flag.
+	// Check cert signing flags.
 	signedApk := m.Output("signed/foo.apk")
-	lineageFlag := signedApk.Args["flags"]
-	expectedLineageFlag := "--lineage lineage.bin"
-	if expectedLineageFlag != lineageFlag {
-		t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
+	actualCertSigningFlags := signedApk.Args["flags"]
+	expectedCertSigningFlags := "--lineage lineage.bin --rotation-min-sdk-version 32"
+	if expectedCertSigningFlags != actualCertSigningFlags {
+		t.Errorf("Incorrect cert signing flags, expected: %q, got: %q", expectedCertSigningFlags, actualCertSigningFlags)
 	}
+
 	signingFlag := signedApk.Args["certificates"]
 	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
 	if expected != signingFlag {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index f7e5d9d..8f499b1 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2129,11 +2129,12 @@
 
 // The type of a structure that contains a field of type sdkLibraryScopeProperties
 // for each apiscope in allApiScopes, e.g. something like:
-// struct {
-//   Public sdkLibraryScopeProperties
-//   System sdkLibraryScopeProperties
-//   ...
-// }
+//
+//	struct {
+//	  Public sdkLibraryScopeProperties
+//	  System sdkLibraryScopeProperties
+//	  ...
+//	}
 var allScopeStructType = createAllScopePropertiesStructType()
 
 // Dynamically create a structure type for each apiscope in allApiScopes.
@@ -2556,9 +2557,7 @@
 	return requiredFilesFromPrebuiltApexForImport(name)
 }
 
-//
 // java_sdk_library_xml
-//
 type sdkLibraryXml struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -2700,7 +2699,10 @@
 			`"current" is not an allowed value for this attribute`)
 		return ""
 	}
-	return formattedOptionalAttribute(attrName, value)
+	// "safeValue" is safe because it translates finalized codenames to a string
+	// with their SDK int.
+	safeValue := apiLevel.String()
+	return formattedOptionalAttribute(attrName, &safeValue)
 }
 
 // formats an attribute for the xml permissions file if the value is not null
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
index 0acaa13..4f83981 100644
--- a/java/sdk_library_external.go
+++ b/java/sdk_library_external.go
@@ -49,9 +49,10 @@
 
 // Get partition group of java module that can be used at inter-partition dependency check.
 // We currently have three groups
-//   (system, system_ext) => system partition group
-//   (vendor, odm) => vendor partition group
-//   (product) => product partition group
+//
+//	(system, system_ext) => system partition group
+//	(vendor, odm) => vendor partition group
+//	(product) => product partition group
 func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup {
 	// system and system_ext partition can be treated as the same in terms of inter-partition dependency.
 	if j.Platform() || j.SystemExtSpecific() {
diff --git a/java/testing.go b/java/testing.go
index 4000334..1f41191 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -54,14 +54,14 @@
 		"build/soong/java/lint_defaults.txt": nil,
 		// Needed for apps that do not provide their own.
 		"build/make/target/product/security": nil,
+		// Required to generate Java used-by API coverage
+		"build/soong/scripts/gen_java_usedby_apex.sh": nil,
 	}.AddToFixture(),
 )
 
-// Test fixture preparer that will define all default java modules except the
-// fake_tool_binary for dex2oatd.
-var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers(
-	// Make sure that all the module types used in the defaults are registered.
-	PrepareForTestWithJavaBuildComponents,
+var prepareForTestWithFrameworkDeps = android.GroupFixturePreparers(
+	// The java default module definitions.
+	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
 	// Additional files needed when test disallows non-existent source.
 	android.MockFS{
 		// Needed for framework-res
@@ -75,8 +75,14 @@
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
 	}.AddToFixture(),
-	// The java default module definitions.
-	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
+)
+
+// Test fixture preparer that will define all default java modules except the
+// fake_tool_binary for dex2oatd.
+var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers(
+	// Make sure that all the module types used in the defaults are registered.
+	PrepareForTestWithJavaBuildComponents,
+	prepareForTestWithFrameworkDeps,
 	// Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module.
 	dexpreopt.PrepareForTestWithDexpreoptCompatLibs,
 )
@@ -139,6 +145,30 @@
 	"30": {},
 })
 
+var prepareForTestWithFrameworkJacocoInstrumentation = android.GroupFixturePreparers(
+	android.FixtureMergeEnv(map[string]string{
+		"EMMA_INSTRUMENT_FRAMEWORK": "true",
+	}),
+	PrepareForTestWithJacocoInstrumentation,
+)
+
+// PrepareForTestWithJacocoInstrumentation creates a mock jacocoagent library that can be
+// depended on as part of the build process for instrumented Java modules.
+var PrepareForTestWithJacocoInstrumentation = android.GroupFixturePreparers(
+	android.FixtureMergeEnv(map[string]string{
+		"EMMA_INSTRUMENT": "true",
+	}),
+	android.FixtureAddFile("jacocoagent/Test.java", nil),
+	android.FixtureAddFile("jacocoagent/Android.bp", []byte(`
+		java_library {
+			name: "jacocoagent",
+			host_supported: true,
+			srcs: ["Test.java"],
+			sdk_version: "current",
+		}
+	`)),
+)
+
 // FixtureWithPrebuiltApis creates a preparer that will define prebuilt api modules for the
 // specified releases and modules.
 //
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 2707f0c..aa48e63 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -14,13 +14,13 @@
 
 // Convert makefile containing device configuration to Starlark file
 // The conversion can handle the following constructs in a makefile:
-//   * comments
-//   * simple variable assignments
-//   * $(call init-product,<file>)
-//   * $(call inherit-product-if-exists
-//   * if directives
-// All other constructs are carried over to the output starlark file as comments.
+//   - comments
+//   - simple variable assignments
+//   - $(call init-product,<file>)
+//   - $(call inherit-product-if-exists
+//   - if directives
 //
+// All other constructs are carried over to the output starlark file as comments.
 package mk2rbc
 
 import (
diff --git a/mk2rbc/soong_variables.go b/mk2rbc/soong_variables.go
index de46925..a52ec4f 100644
--- a/mk2rbc/soong_variables.go
+++ b/mk2rbc/soong_variables.go
@@ -32,8 +32,8 @@
 
 // Scans the makefile Soong uses to generate soong.variables file,
 // collecting variable names and types from the lines that look like this:
-//    $(call add_json_XXX,  <...>,             $(VAR))
 //
+//	$(call add_json_XXX,  <...>,             $(VAR))
 func FindSoongVariables(mkFile string, includeFileScope mkparser.Scope, registrar variableRegistrar) error {
 	ctx := context{includeFileScope, registrar}
 	return ctx.doFind(mkFile)
diff --git a/multitree/api_imports.go b/multitree/api_imports.go
index 2c4cf80..6674d3e 100644
--- a/multitree/api_imports.go
+++ b/multitree/api_imports.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"strings"
 
 	"github.com/google/blueprint"
 )
@@ -26,6 +27,7 @@
 
 func init() {
 	RegisterApiImportsModule(android.InitRegistrationContext)
+	android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
 }
 
 func RegisterApiImportsModule(ctx android.RegistrationContext) {
@@ -86,3 +88,12 @@
 func GetApiImportSuffix() string {
 	return apiImportNameSuffix
 }
+
+func makeVarsProvider(ctx android.MakeVarsContext) {
+	ctx.VisitAllModules(func(m android.Module) {
+		if i, ok := m.(*ApiImports); ok {
+			ctx.Strict("API_IMPORTED_SHARED_LIBRARIES", strings.Join(i.properties.Shared_libs, " "))
+			ctx.Strict("API_IMPORTED_HEADER_LIBRARIES", strings.Join(i.properties.Header_libs, " "))
+		}
+	})
+}
diff --git a/python/androidmk.go b/python/androidmk.go
index 233d867..7dc4713 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -69,7 +69,7 @@
 
 			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
 
-			entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
+			p.testProperties.Test_options.SetAndroidMkEntries(entries)
 		})
 	base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
 }
diff --git a/python/python.go b/python/python.go
index eb0d3ca..8364169 100644
--- a/python/python.go
+++ b/python/python.go
@@ -438,9 +438,9 @@
 }
 
 // DepsMutator mutates dependencies for this module:
-//  * handles proto dependencies,
-//  * if required, specifies launcher and adds launcher dependencies,
-//  * applies python version mutations to Python dependencies
+//   - handles proto dependencies,
+//   - if required, specifies launcher and adds launcher dependencies,
+//   - applies python version mutations to Python dependencies
 func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
 	android.ProtoDeps(ctx, &p.protoProperties)
 
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index 138404b..23897b3 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -81,7 +81,9 @@
     os.environ.update(new_env)
 
     sys.stdout.flush()
-    retCode = subprocess.call(args)
+    # 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
diff --git a/python/test.go b/python/test.go
index 7413782..b9b3465 100644
--- a/python/test.go
+++ b/python/test.go
@@ -32,12 +32,6 @@
 	ctx.RegisterModuleType("python_test", PythonTestFactory)
 }
 
-// Test option struct.
-type TestOptions struct {
-	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
-	Unit_test *bool
-}
-
 type TestProperties struct {
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
 	// installed with the module.
@@ -55,7 +49,7 @@
 	Java_data []string
 
 	// Test options.
-	Test_options TestOptions
+	Test_options android.CommonTestOptions
 }
 
 type testDecorator struct {
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 2361e03..32c746e 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -105,10 +105,11 @@
 				entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
 			}
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
-			entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
 			if test.Properties.Data_bins != nil {
 				entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
 			}
+
+			test.Properties.Test_options.SetAndroidMkEntries(entries)
 		})
 
 	cc.AndroidMkWriteTestData(test.data, ret)
diff --git a/rust/config/global.go b/rust/config/global.go
index 9acbfb3..e676837 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.62.0.p1"
+	RustDefaultVersion = "1.63.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/test.go b/rust/test.go
index 6e53935..0cc3bca 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -24,12 +24,6 @@
 	"android/soong/tradefed"
 )
 
-// Test option struct.
-type TestOptions struct {
-	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
-	Unit_test *bool
-}
-
 type TestProperties struct {
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
@@ -67,7 +61,7 @@
 	Test_harness *bool
 
 	// Test options.
-	Test_options TestOptions
+	Test_options android.CommonTestOptions
 
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 814bd57..b5b588b 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -168,7 +168,7 @@
 }
 
 python_binary_host {
-    name: "gen-kotlin-build-file.py",
+    name: "gen-kotlin-build-file",
     main: "gen-kotlin-build-file.py",
     srcs: [
         "gen-kotlin-build-file.py",
diff --git a/scripts/build_broken_logs.go b/scripts/build_broken_logs.go
index 82ba749..bb4b9fd 100644
--- a/scripts/build_broken_logs.go
+++ b/scripts/build_broken_logs.go
@@ -19,12 +19,12 @@
 // To use, download the logs.zip from one or more branches, and extract them
 // into subdirectories of the current directory. So for example, I have:
 //
-//   ./aosp-master/aosp_arm/std_full.log
-//   ./aosp-master/aosp_arm64/std_full.log
-//   ./aosp-master/...
-//   ./internal-master/aosp_arm/std_full.log
-//   ./internal-master/aosp_arm64/std_full.log
-//   ./internal-master/...
+//	./aosp-master/aosp_arm/std_full.log
+//	./aosp-master/aosp_arm64/std_full.log
+//	./aosp-master/...
+//	./internal-master/aosp_arm/std_full.log
+//	./internal-master/aosp_arm64/std_full.log
+//	./internal-master/...
 //
 // Then I use `go run path/to/build_broken_logs.go *`
 package main
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 13ddbe7..c93055a 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -675,8 +675,8 @@
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
-		java.FixtureWithLastReleaseApis("mysdklibrary"),
-		java.FixtureConfigureApexBootJars("myapex:mybootlib"),
+		java.FixtureWithLastReleaseApis("mysdklibrary", "mynewlibrary"),
+		java.FixtureConfigureApexBootJars("myapex:mybootlib", "myapex:mynewlibrary"),
 		prepareForSdkTestWithApex,
 
 		// Add a platform_bootclasspath that depends on the fragment.
@@ -691,6 +691,7 @@
 			"my-removed.txt":                   nil,
 			"my-unsupported-packages.txt":      nil,
 			"my-unsupported.txt":               nil,
+			"my-new-max-target-q.txt":          nil,
 		}.AddToFixture(),
 		android.FixtureWithRootAndroidBp(`
 			sdk {
@@ -708,7 +709,7 @@
 			bootclasspath_fragment {
 				name: "mybootclasspathfragment",
 				apex_available: ["myapex"],
-				contents: ["mybootlib"],
+				contents: ["mybootlib", "mynewlibrary"],
 				api: {
 					stub_libs: ["mysdklibrary"],
 				},
@@ -737,7 +738,9 @@
 					unsupported_packages: [
 							"my-unsupported-packages.txt",
 					],
-					split_packages: ["*"],
+					split_packages: ["sdklibrary"],
+					package_prefixes: ["sdklibrary.all.mine"],
+					single_packages: ["sdklibrary.mine"],
 				},
 			}
 
@@ -759,6 +762,24 @@
 				public: {enabled: true},
 				permitted_packages: ["mysdklibrary"],
 			}
+
+			java_sdk_library {
+				name: "mynewlibrary",
+				apex_available: ["myapex"],
+				srcs: ["Test.java"],
+				min_sdk_version: "10",
+				compile_dex: true,
+				public: {enabled: true},
+				permitted_packages: ["mysdklibrary"],
+				hidden_api: {
+					max_target_q: [
+							"my-new-max-target-q.txt",
+					],
+					split_packages: ["sdklibrary", "newlibrary"],
+					package_prefixes: ["newlibrary.all.mine"],
+					single_packages: ["newlibrary.mine"],
+				},
+			}
 		`),
 	).RunTest(t)
 
@@ -774,7 +795,10 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    contents: ["mybootlib"],
+    contents: [
+        "mybootlib",
+        "mynewlibrary",
+    ],
     api: {
         stub_libs: ["mysdklibrary"],
     },
@@ -782,7 +806,10 @@
         unsupported: ["hiddenapi/my-unsupported.txt"],
         removed: ["hiddenapi/my-removed.txt"],
         max_target_r_low_priority: ["hiddenapi/my-max-target-r-low-priority.txt"],
-        max_target_q: ["hiddenapi/my-max-target-q.txt"],
+        max_target_q: [
+            "hiddenapi/my-max-target-q.txt",
+            "hiddenapi/my-new-max-target-q.txt",
+        ],
         max_target_p: ["hiddenapi/my-max-target-p.txt"],
         max_target_o_low_priority: ["hiddenapi/my-max-target-o-low-priority.txt"],
         blocked: ["hiddenapi/my-blocked.txt"],
@@ -806,6 +833,23 @@
 }
 
 java_sdk_library_import {
+    name: "mynewlibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    shared_library: true,
+    compile_dex: true,
+    permitted_packages: ["mysdklibrary"],
+    public: {
+        jars: ["sdk_library/public/mynewlibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mynewlibrary_stub_sources"],
+        current_api: "sdk_library/public/mynewlibrary.txt",
+        removed_api: "sdk_library/public/mynewlibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_sdk_library_import {
     name: "mysdklibrary",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -827,6 +871,7 @@
 my-removed.txt -> hiddenapi/my-removed.txt
 my-max-target-r-low-priority.txt -> hiddenapi/my-max-target-r-low-priority.txt
 my-max-target-q.txt -> hiddenapi/my-max-target-q.txt
+my-new-max-target-q.txt -> hiddenapi/my-new-max-target-q.txt
 my-max-target-p.txt -> hiddenapi/my-max-target-p.txt
 my-max-target-o-low-priority.txt -> hiddenapi/my-max-target-o-low-priority.txt
 my-blocked.txt -> hiddenapi/my-blocked.txt
@@ -838,6 +883,9 @@
 .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/mynewlibrary.stubs/android_common/javac/mynewlibrary.stubs.jar -> sdk_library/public/mynewlibrary-stubs.jar
+.intermediates/mynewlibrary.stubs.source/android_common/metalava/mynewlibrary.stubs.source_api.txt -> sdk_library/public/mynewlibrary.txt
+.intermediates/mynewlibrary.stubs.source/android_common/metalava/mynewlibrary.stubs.source_removed.txt -> sdk_library/public/mynewlibrary-removed.txt
 .intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
 .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
 .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 0494a28..ac57a32 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"math"
 	"reflect"
 	"strings"
 )
@@ -29,7 +30,10 @@
 	// The name of the release, e.g. S, Tiramisu, etc.
 	name string
 
-	// The index of this structure within the buildReleases list.
+	// The index of this structure within the dessertBuildReleases list.
+	//
+	// The buildReleaseCurrent does not appear in the dessertBuildReleases list as it has an ordinal value
+	// that is larger than the size of the dessertBuildReleases.
 	ordinal int
 }
 
@@ -56,7 +60,7 @@
 // addRange adds all the build releases from start (inclusive) to end (inclusive).
 func (s *buildReleaseSet) addRange(start *buildRelease, end *buildRelease) {
 	for i := start.ordinal; i <= end.ordinal; i += 1 {
-		s.addItem(buildReleases[i])
+		s.addItem(dessertBuildReleases[i])
 	}
 }
 
@@ -69,11 +73,17 @@
 // String returns a string representation of the set, sorted from earliest to latest release.
 func (s *buildReleaseSet) String() string {
 	list := []string{}
-	for _, release := range buildReleases {
+	addRelease := func(release *buildRelease) {
 		if _, ok := s.contents[release]; ok {
 			list = append(list, release.name)
 		}
 	}
+	// Add the names of the build releases in this set in the order in which they were created.
+	for _, release := range dessertBuildReleases {
+		addRelease(release)
+	}
+	// Always add "current" to the list of names last if it is present in the set.
+	addRelease(buildReleaseCurrent)
 	return fmt.Sprintf("[%s]", strings.Join(list, ","))
 }
 
@@ -81,30 +91,46 @@
 	// nameToBuildRelease contains a map from name to build release.
 	nameToBuildRelease = map[string]*buildRelease{}
 
-	// buildReleases lists all the available build releases.
-	buildReleases = []*buildRelease{}
+	// dessertBuildReleases lists all the available dessert build releases, i.e. excluding current.
+	dessertBuildReleases = []*buildRelease{}
 
 	// allBuildReleaseSet is the set of all build releases.
 	allBuildReleaseSet = &buildReleaseSet{contents: map[*buildRelease]struct{}{}}
 
-	// Add the build releases from oldest to newest.
+	// Add the dessert build releases from oldest to newest.
 	buildReleaseS = initBuildRelease("S")
 	buildReleaseT = initBuildRelease("Tiramisu")
+
+	// Add the current build release which is always treated as being more recent than any other
+	// build release, including those added in tests.
+	buildReleaseCurrent = initBuildRelease("current")
 )
 
 // initBuildRelease creates a new build release with the specified name.
 func initBuildRelease(name string) *buildRelease {
-	ordinal := len(nameToBuildRelease)
+	ordinal := len(dessertBuildReleases)
+	if name == "current" {
+		// The current build release is more recent than all other build releases, including those
+		// created in tests so use the max int value. It cannot just rely on being created after all
+		// the other build releases as some are created in tests which run after the current build
+		// release has been created.
+		ordinal = math.MaxInt
+	}
 	release := &buildRelease{name: name, ordinal: ordinal}
 	nameToBuildRelease[name] = release
-	buildReleases = append(buildReleases, release)
 	allBuildReleaseSet.addItem(release)
+	if name != "current" {
+		// As the current build release has an ordinal value that does not correspond to its position
+		// in the dessertBuildReleases list do not add it to the list.
+		dessertBuildReleases = append(dessertBuildReleases, release)
+	}
 	return release
 }
 
-// latestBuildRelease returns the latest build release, i.e. the last one added.
-func latestBuildRelease() *buildRelease {
-	return buildReleases[len(buildReleases)-1]
+// latestDessertBuildRelease returns the latest dessert release build name, i.e. the last dessert
+// release added to the list, which does not include current.
+func latestDessertBuildRelease() *buildRelease {
+	return dessertBuildReleases[len(dessertBuildReleases)-1]
 }
 
 // nameToRelease maps from build release name to the corresponding build release (if it exists) or
@@ -134,8 +160,10 @@
 		if err != nil {
 			return nil, err
 		}
-		end := latestBuildRelease()
+		end := latestDessertBuildRelease()
 		set.addRange(start, end)
+		// An open-ended range always includes the current release.
+		set.addItem(buildReleaseCurrent)
 	} else if strings.Contains(specification, "-") {
 		limits := strings.SplitN(specification, "-", 2)
 		start, err := nameToRelease(limits[0])
@@ -369,7 +397,9 @@
 // structure which are not supported by the specified target build release.
 //
 // A property is pruned if its field has a tag of the form:
-//     `supported_build_releases:"<build-release-set>"`
+//
+//	`supported_build_releases:"<build-release-set>"`
+//
 // and the resulting build release set does not contain the target build release. Properties that
 // have no such tag are assumed to be supported by all releases.
 func newPropertyPrunerByBuildRelease(propertiesStruct interface{}, targetBuildRelease *buildRelease) *propertyPruner {
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index 6f1ef9e..13730cb 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,T.*,F1,F2\]`, []error{err})
+		android.FailIfNoMatchingErrors(t, `unknown release "A", expected one of \[S,Tiramisu,F1,F2,current\]`, []error{err})
 	})
 }
 
@@ -55,7 +55,7 @@
 	t.Run("open range", func(t *testing.T) {
 		set, err := parseBuildReleaseSet("F1+")
 		android.AssertDeepEquals(t, "errors", nil, err)
-		android.AssertStringEquals(t, "set", "[F1,F2]", set.String())
+		android.AssertStringEquals(t, "set", "[F1,F2,current]", set.String())
 	})
 	t.Run("closed range", func(t *testing.T) {
 		set, err := parseBuildReleaseSet("S-F1")
diff --git a/sdk/member_trait.go b/sdk/member_trait.go
index 4229ca8..0843306 100644
--- a/sdk/member_trait.go
+++ b/sdk/member_trait.go
@@ -68,7 +68,6 @@
 // A list of sdkMemberTraitListProperty instances is created, one per member trait that provides:
 // * a reference to the member trait.
 // * a getter for the corresponding field in the properties struct.
-//
 func createDynamicSdkMemberTraits(sdkMemberTraits []android.SdkMemberTrait) *dynamicSdkMemberTraits {
 
 	var listProperties []*sdkMemberTraitListProperty
diff --git a/sdk/member_type.go b/sdk/member_type.go
index 10669fe..98f5982 100644
--- a/sdk/member_type.go
+++ b/sdk/member_type.go
@@ -80,7 +80,6 @@
 // * a reference to the member type.
 // * a getter for the corresponding field in the properties struct.
 // * a dependency tag that identifies the member type of a resolved dependency.
-//
 func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
 
 	var listProperties []*sdkMemberTypeListProperty
diff --git a/sdk/testing.go b/sdk/testing.go
index bed11b2..f4e2b03 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -262,8 +262,7 @@
 
 	// If the generated snapshot builders not for the current release then it cannot be loaded by
 	// the current release.
-	currentBuildRelease := latestBuildRelease()
-	if snapshotBuildInfo.targetBuildRelease != currentBuildRelease {
+	if snapshotBuildInfo.targetBuildRelease != buildReleaseCurrent {
 		return
 	}
 
diff --git a/sdk/update.go b/sdk/update.go
index c555ddc..5c9376b 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -387,12 +387,11 @@
 	// Always add -current to the end
 	snapshotFileSuffix := "-current"
 
-	currentBuildRelease := latestBuildRelease()
-	targetBuildReleaseEnv := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", currentBuildRelease.name)
+	targetBuildReleaseEnv := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", buildReleaseCurrent.name)
 	targetBuildRelease, err := nameToRelease(targetBuildReleaseEnv)
 	if err != nil {
 		ctx.ModuleErrorf("invalid SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE: %s", err)
-		targetBuildRelease = currentBuildRelease
+		targetBuildRelease = buildReleaseCurrent
 	}
 
 	builder := &snapshotBuilder{
@@ -472,7 +471,7 @@
 	contents := bp.content.String()
 	// If the snapshot is being generated for the current build release then check the syntax to make
 	// sure that it is compatible.
-	if targetBuildRelease == currentBuildRelease {
+	if targetBuildRelease == buildReleaseCurrent {
 		syntaxCheckSnapshotBpFile(ctx, contents)
 	}
 
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 4de0144..9627329 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -103,12 +103,6 @@
 	Recovery_available *bool
 }
 
-// Test option struct.
-type TestOptions struct {
-	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
-	Unit_test *bool
-}
-
 type TestProperties struct {
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
@@ -153,7 +147,7 @@
 	Per_testcase_directory *bool
 
 	// Test options.
-	Test_options TestOptions
+	Test_options android.CommonTestOptions
 }
 
 type ShBinary struct {
@@ -464,10 +458,9 @@
 				if s.testProperties.Data_bins != nil {
 					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
 				}
-				if Bool(s.testProperties.Test_options.Unit_test) {
-					entries.SetBool("LOCAL_IS_UNIT_TEST", true)
-				}
 				entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
+
+				s.testProperties.Test_options.SetAndroidMkEntries(entries)
 			},
 		},
 	}}
diff --git a/shared/env.go b/shared/env.go
index 152729b..b7d3baf 100644
--- a/shared/env.go
+++ b/shared/env.go
@@ -32,10 +32,11 @@
 //
 // e.g. OUT_DIR = "out"
 // is converted to:
-// {
-//     "Key": "OUT_DIR",
-//     "Value": "out",
-// },
+//
+//	{
+//	    "Key": "OUT_DIR",
+//	    "Value": "out",
+//	},
 func EnvFileContents(envDeps map[string]string) ([]byte, error) {
 	contents := make(envFileData, 0, len(envDeps))
 	for key, value := range envDeps {
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
index f1e31ca..ac002be 100644
--- a/snapshot/recovery_snapshot.go
+++ b/snapshot/recovery_snapshot.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
index 294f8b6..206ecc9 100644
--- a/snapshot/snapshot.go
+++ b/snapshot/snapshot.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 4a14f2e..8e5dfe4 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/snapshot/util.go b/snapshot/util.go
index f447052..806ac90 100644
--- a/snapshot/util.go
+++ b/snapshot/util.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
index 9bd26c2..8f7b8c2 100644
--- a/snapshot/vendor_snapshot.go
+++ b/snapshot/vendor_snapshot.go
@@ -4,7 +4,7 @@
 // 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
+//	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,
diff --git a/symbol_inject/cmd/symbol_inject.go b/symbol_inject/cmd/symbol_inject.go
index 1397b37..89b3619 100644
--- a/symbol_inject/cmd/symbol_inject.go
+++ b/symbol_inject/cmd/symbol_inject.go
@@ -94,4 +94,13 @@
 		os.Remove(*output)
 		os.Exit(5)
 	}
+
+	if file.IsMachoFile {
+		err = symbol_inject.CodeSignMachoFile(*output)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err.Error())
+			os.Remove(*output)
+			os.Exit(6)
+		}
+	}
 }
diff --git a/symbol_inject/macho.go b/symbol_inject/macho.go
index 6ee3f4f..9946d34 100644
--- a/symbol_inject/macho.go
+++ b/symbol_inject/macho.go
@@ -18,6 +18,7 @@
 	"debug/macho"
 	"fmt"
 	"io"
+	"os/exec"
 	"sort"
 	"strings"
 )
@@ -40,7 +41,7 @@
 		return symbols[i].Value < symbols[j].Value
 	})
 
-	file := &File{}
+	file := &File{IsMachoFile: true}
 
 	for _, section := range machoFile.Sections {
 		file.Sections = append(file.Sections, &Section{
@@ -95,3 +96,8 @@
 
 	return nil
 }
+
+func CodeSignMachoFile(path string) error {
+	cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", path)
+	return cmd.Run()
+}
diff --git a/symbol_inject/symbol_inject.go b/symbol_inject/symbol_inject.go
index 2a3d67e..77aff6f 100644
--- a/symbol_inject/symbol_inject.go
+++ b/symbol_inject/symbol_inject.go
@@ -161,9 +161,10 @@
 }
 
 type File struct {
-	r        io.ReaderAt
-	Symbols  []*Symbol
-	Sections []*Section
+	r           io.ReaderAt
+	Symbols     []*Symbol
+	Sections    []*Section
+	IsMachoFile bool
 }
 
 type Symbol struct {
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index 1d5eb31..e5263fe 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-bp2build",
         "soong-cc",
         "soong-java",
     ],
@@ -18,6 +19,7 @@
     ],
     testSrcs: [
         "sysprop_test.go",
+        "sysprop_library_conversion_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index a29d4c3..0785f89 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -23,6 +23,7 @@
 	"path"
 	"sync"
 
+	"android/soong/bazel"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -125,6 +126,7 @@
 type syspropLibrary struct {
 	android.ModuleBase
 	android.ApexModuleBase
+	android.BazelModuleBase
 
 	properties syspropLibraryProperties
 
@@ -372,6 +374,7 @@
 	)
 	android.InitAndroidModule(m)
 	android.InitApexModule(m)
+	android.InitBazelModule(m)
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) })
 	return m
 }
@@ -403,6 +406,9 @@
 	Host_supported     *bool
 	Apex_available     []string
 	Min_sdk_version    *string
+	Bazel_module       struct {
+		Bp2build_available *bool
+	}
 }
 
 type javaLibraryProperties struct {
@@ -483,6 +489,11 @@
 	ccProps.Host_supported = m.properties.Host_supported
 	ccProps.Apex_available = m.ApexProperties.Apex_available
 	ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
+	// A Bazel macro handles this, so this module does not need to be handled
+	// in bp2build
+	// TODO(b/237810289) perhaps do something different here so that we aren't
+	//                   also disabling these modules in mixed builds
+	ccProps.Bazel_module.Bp2build_available = proptools.BoolPtr(false)
 	ctx.CreateModule(cc.LibraryFactory, &ccProps)
 
 	scope := "internal"
@@ -557,3 +568,45 @@
 		*libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName())
 	}
 }
+
+// TODO(b/240463568): Additional properties will be added for API validation
+type bazelSyspropLibraryAttributes struct {
+	Srcs bazel.LabelListAttribute
+}
+
+type bazelCcSyspropLibraryAttributes struct {
+	Dep             bazel.LabelAttribute
+	Min_sdk_version *string
+}
+
+func (m *syspropLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "sysprop_library",
+			Bzl_load_location: "//build/bazel/rules/sysprop:sysprop_library.bzl",
+		},
+		android.CommonAttributes{Name: m.Name()},
+		&bazelSyspropLibraryAttributes{
+			Srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)),
+		})
+
+	attrs := &bazelCcSyspropLibraryAttributes{
+		Dep:             *bazel.MakeLabelAttribute(":" + m.Name()),
+		Min_sdk_version: m.properties.Cpp.Min_sdk_version,
+	}
+
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_sysprop_library_shared",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_sysprop_library.bzl",
+		},
+		android.CommonAttributes{Name: m.CcImplementationModuleName()},
+		attrs)
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_sysprop_library_static",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_sysprop_library.bzl",
+		},
+		android.CommonAttributes{Name: m.CcImplementationModuleName() + "_bp2build_cc_library_static"},
+		attrs)
+}
diff --git a/sysprop/sysprop_library_conversion_test.go b/sysprop/sysprop_library_conversion_test.go
new file mode 100644
index 0000000..c72faf3
--- /dev/null
+++ b/sysprop/sysprop_library_conversion_test.go
@@ -0,0 +1,110 @@
+// 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 sysprop
+
+import (
+	"testing"
+
+	"android/soong/bp2build"
+)
+
+func TestSyspropLibrarySimple(t *testing.T) {
+	bp2build.RunBp2BuildTestCaseSimple(t, bp2build.Bp2buildTestCase{
+		Description:                "sysprop_library simple",
+		ModuleTypeUnderTest:        "sysprop_library",
+		ModuleTypeUnderTestFactory: syspropLibraryFactory,
+		Filesystem: map[string]string{
+			"foo.sysprop": "",
+			"bar.sysprop": "",
+		},
+		Blueprint: `
+sysprop_library {
+	name: "sysprop_foo",
+	srcs: [
+		"foo.sysprop",
+		"bar.sysprop",
+	],
+	property_owner: "Platform",
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTargetNoRestrictions("sysprop_library",
+				"sysprop_foo_sysprop_library",
+				bp2build.AttrNameToString{
+					"srcs": `[
+        "foo.sysprop",
+        "bar.sysprop",
+    ]`,
+				}),
+			bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_shared",
+				"libsysprop_foo",
+				bp2build.AttrNameToString{
+					"dep": `":sysprop_foo_sysprop_library"`,
+				}),
+			bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_static",
+				"libsysprop_foo_bp2build_cc_library_static",
+				bp2build.AttrNameToString{
+					"dep": `":sysprop_foo_sysprop_library"`,
+				}),
+		},
+	})
+}
+
+func TestSyspropLibraryCppMinSdkVersion(t *testing.T) {
+	bp2build.RunBp2BuildTestCaseSimple(t, bp2build.Bp2buildTestCase{
+		Description:                "sysprop_library with min_sdk_version",
+		ModuleTypeUnderTest:        "sysprop_library",
+		ModuleTypeUnderTestFactory: syspropLibraryFactory,
+		Filesystem: map[string]string{
+			"foo.sysprop": "",
+			"bar.sysprop": "",
+		},
+		Blueprint: `
+sysprop_library {
+	name: "sysprop_foo",
+	srcs: [
+		"foo.sysprop",
+		"bar.sysprop",
+	],
+	cpp: {
+		min_sdk_version: "5",
+	},
+	property_owner: "Platform",
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTargetNoRestrictions("sysprop_library",
+				"sysprop_foo_sysprop_library",
+				bp2build.AttrNameToString{
+					"srcs": `[
+        "foo.sysprop",
+        "bar.sysprop",
+    ]`,
+				}),
+			bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_shared",
+				"libsysprop_foo",
+				bp2build.AttrNameToString{
+					"dep":             `":sysprop_foo_sysprop_library"`,
+					"min_sdk_version": `"5"`,
+				}),
+			bp2build.MakeBazelTargetNoRestrictions("cc_sysprop_library_static",
+				"libsysprop_foo_bp2build_cc_library_static",
+				bp2build.AttrNameToString{
+					"dep":             `":sysprop_foo_sysprop_library"`,
+					"min_sdk_version": `"5"`,
+				}),
+		},
+	})
+}
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index 4b2f795..6bc0165 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -33,6 +33,10 @@
 SOONG_OUTPUT_DIR="$OUTPUT_DIR/soong"
 BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
 
+function call_bazel() {
+  tools/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
+}
+
 function cleanup {
   # call bazel clean because some bazel outputs don't have w bits.
   call_bazel clean
@@ -54,12 +58,10 @@
 ######################
 build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
 
-function call_bazel() {
-  tools/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
-}
 BAZEL_OUT="$(call_bazel info output_path)"
 
-call_bazel build --config=bp2build --config=ci --config=android_arm \
+export TARGET_PRODUCT="module_arm"
+call_bazel build --config=bp2build --config=ci --config=android \
   //packages/modules/adb/apex:com.android.adbd \
   //system/timezone/apex:com.android.tzdata \
   //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex
@@ -85,7 +87,7 @@
 
   # Compare the outputs of `deapexer list`, which lists the contents of the apex filesystem image.
   local SOONG_APEX="$SOONG_OUTPUT_DIR/$APEX"
-  local BAZEL_APEX="$BAZEL_OUT/android_arm-fastbuild/bin/$APEX_DIR/$APEX"
+  local BAZEL_APEX="$BAZEL_OUT/android_target-fastbuild/bin/$APEX_DIR/$APEX"
 
   local SOONG_LIST="$OUTPUT_DIR/soong.list"
   local BAZEL_LIST="$OUTPUT_DIR/bazel.list"
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 3cdf6aa..78af54d 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -103,7 +103,7 @@
   fi
 
   # NOTE: We don't actually use the extra BUILD file for anything here
-  run_bazel build --package_path=out/soong/workspace //foo/...
+  run_bazel build --config=android --package_path=out/soong/workspace //foo/...
 
   local the_answer_file="bazel-out/android_target-fastbuild/bin/foo/convertible_soong_module/the_answer.txt"
   if [[ ! -f "${the_answer_file}" ]]; then
@@ -146,10 +146,10 @@
 
   run_soong bp2build
 
-  run_bazel build --package_path=out/soong/workspace //a:qq
+  run_bazel build --config=android --package_path=out/soong/workspace //a:qq
   local -r output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
-  run_bazel build --package_path=out/soong/workspace //a:qq
+  run_bazel build --config=android --package_path=out/soong/workspace //a:qq
   local -r output_mtime2=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
@@ -160,7 +160,7 @@
 #define QQ 2
 EOF
 
-  run_bazel build --package_path=out/soong/workspace //a:qq
+  run_bazel build --config=android --package_path=out/soong/workspace //a:qq
   local -r output_mtime3=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   if [[ "$output_mtime1" == "$output_mtime3" ]]; then
diff --git a/tests/lib.sh b/tests/lib.sh
index 69ad201..7248ade 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -111,7 +111,7 @@
 
 # shellcheck disable=SC2120
 function run_soong {
-  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
+  USE_RBE=false build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
 }
 
 function create_mock_bazel {
@@ -123,6 +123,7 @@
   symlink_directory prebuilts/jdk
   symlink_directory external/bazel-skylib
   symlink_directory external/bazelbuild-rules_android
+  symlink_directory external/bazelbuild-kotlin-rules
 
   symlink_file WORKSPACE
   symlink_file BUILD
diff --git a/third_party/zip/reader_test.go b/third_party/zip/reader_test.go
index 11c6d6e..5f97f5c 100644
--- a/third_party/zip/reader_test.go
+++ b/third_party/zip/reader_test.go
@@ -615,7 +615,6 @@
 //
 // It's here in hex for the same reason as rZipBytes above: to avoid
 // problems with on-disk virus scanners or other zip processors.
-//
 func biggestZipBytes() []byte {
 	s := `
 0000000 50 4b 03 04 14 00 08 00 08 00 00 00 00 00 00 00
diff --git a/ui/build/build.go b/ui/build/build.go
index 5b80b4d..ff2d5f2 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -104,12 +104,24 @@
 	// Whether to run ninja on the combined ninja.
 	RunNinja = 1 << iota
 	// Whether to run bazel on the combined ninja.
-	RunBazel        = 1 << iota
-	RunBuildTests   = 1 << iota
-	RunAll          = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja
-	RunAllWithBazel = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunBazel
+	RunBazel      = 1 << iota
+	RunBuildTests = 1 << iota
+	RunAll        = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja
 )
 
+// 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")
+	}
+}
+
 // checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
 func checkProblematicFiles(ctx Context) {
 	files := []string{"Android.mk", "CleanSpec.mk"}
@@ -201,13 +213,28 @@
 	buildLock := BecomeSingletonOrFail(ctx, config)
 	defer buildLock.Unlock()
 
+	logArgsOtherThan := func(specialTargets ...string) {
+		var ignored []string
+		for _, a := range config.Arguments() {
+			if !inList(a, specialTargets) {
+				ignored = append(ignored, a)
+			}
+		}
+		if len(ignored) > 0 {
+			ctx.Printf("ignoring arguments %q", ignored)
+		}
+	}
+
 	if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
+		logArgsOtherThan("clean", "clobber")
 		clean(ctx, config)
 		return
 	}
 
 	defer waitForDist(ctx)
 
+	checkBazelMode(ctx, config)
+
 	// checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
 	checkProblematicFiles(ctx)
 
@@ -223,9 +250,6 @@
 	SetupPath(ctx, config)
 
 	what := RunAll
-	if config.UseBazel() {
-		what = RunAllWithBazel
-	}
 	if config.Checkbuild() {
 		what |= RunBuildTests
 	}
@@ -279,6 +303,7 @@
 
 	if inList("installclean", config.Arguments()) ||
 		inList("install-clean", config.Arguments()) {
+		logArgsOtherThan("installclean", "install-clean")
 		installClean(ctx, config)
 		ctx.Println("Deleted images and staging directories.")
 		return
@@ -286,6 +311,7 @@
 
 	if inList("dataclean", config.Arguments()) ||
 		inList("data-clean", config.Arguments()) {
+		logArgsOtherThan("dataclean", "data-clean")
 		dataClean(ctx, config)
 		ctx.Println("Deleted data files.")
 		return
diff --git a/ui/build/config.go b/ui/build/config.go
index cbf1986..14a99d0 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -99,11 +99,8 @@
 
 	pathReplaced bool
 
-	useBazel bool
-
-	// During Bazel execution, Bazel cannot write outside OUT_DIR.
-	// So if DIST_DIR is set to an external dir (outside of OUT_DIR), we need to rig it temporarily and then migrate files at the end of the build.
-	riggedDistDirForBazel string
+	bazelProdMode bool
+	bazelDevMode  bool
 
 	// Set by multiproduct_kati
 	emptyNinjaFile bool
@@ -130,18 +127,6 @@
 	BUILD_MODULES
 )
 
-type bazelBuildMode int
-
-// Bazel-related build modes.
-const (
-	// Don't use bazel at all.
-	noBazel bazelBuildMode = iota
-
-	// Generate synthetic build files and incorporate these files into a build which
-	// partially uses Bazel. Build metadata may come from Android.bp or BUILD files.
-	mixedBuild
-)
-
 // checkTopDir validates that the current directory is at the root directory of the source tree.
 func checkTopDir(ctx Context) {
 	if _, err := os.Stat(srcDirFileCheck); err != nil {
@@ -448,21 +433,6 @@
 		ctx.Fatalf("Unable to remove bazel profile directory %q: %v", bpd, err)
 	}
 
-	ret.useBazel = ret.environ.IsEnvTrue("USE_BAZEL")
-
-	if ret.UseBazel() {
-		if err := os.MkdirAll(bpd, 0777); err != nil {
-			ctx.Fatalf("Failed to create bazel profile directory %q: %v", bpd, err)
-		}
-	}
-
-	if ret.UseBazel() {
-		ret.riggedDistDirForBazel = filepath.Join(ret.OutDir(), "dist")
-	} else {
-		// Not rigged
-		ret.riggedDistDirForBazel = ret.distDir
-	}
-
 	c := Config{ret}
 	storeConfigMetrics(ctx, c)
 	return c
@@ -495,8 +465,7 @@
 		ForceUseGoma:    proto.Bool(config.ForceUseGoma()),
 		UseGoma:         proto.Bool(config.UseGoma()),
 		UseRbe:          proto.Bool(config.UseRBE()),
-		BazelAsNinja:    proto.Bool(config.UseBazel()),
-		BazelMixedBuild: proto.Bool(config.bazelBuildMode() == mixedBuild),
+		BazelMixedBuild: proto.Bool(config.BazelBuildEnabled()),
 	}
 	c.Targets = append(c.Targets, config.arguments...)
 
@@ -747,6 +716,10 @@
 			c.skipSoongTests = true
 		} else if arg == "--mk-metrics" {
 			c.reportMkMetrics = true
+		} else if arg == "--bazel-mode" {
+			c.bazelProdMode = true
+		} else if arg == "--bazel-mode-dev" {
+			c.bazelDevMode = true
 		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
@@ -882,11 +855,7 @@
 }
 
 func (c *configImpl) DistDir() string {
-	if c.UseBazel() {
-		return c.riggedDistDirForBazel
-	} else {
-		return c.distDir
-	}
+	return c.distDir
 }
 
 func (c *configImpl) RealDistDir() string {
@@ -1134,16 +1103,8 @@
 	return false
 }
 
-func (c *configImpl) UseBazel() bool {
-	return c.useBazel
-}
-
-func (c *configImpl) bazelBuildMode() bazelBuildMode {
-	if c.Environment().IsEnvTrue("USE_BAZEL_ANALYSIS") {
-		return mixedBuild
-	} else {
-		return noBazel
-	}
+func (c *configImpl) BazelBuildEnabled() bool {
+	return c.bazelProdMode || c.bazelDevMode
 }
 
 func (c *configImpl) StartRBE() bool {
@@ -1326,6 +1287,10 @@
 	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja")
 }
 
+func (c *configImpl) SoongVarsFile() string {
+	return filepath.Join(c.SoongOutDir(), "soong.variables")
+}
+
 func (c *configImpl) SoongNinjaFile() string {
 	return filepath.Join(c.SoongOutDir(), "build.ninja")
 }
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index e293275..150ec35 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -28,6 +28,7 @@
 	"android/soong/ui/logger"
 	smpb "android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/status"
+	"google.golang.org/protobuf/encoding/prototext"
 
 	"google.golang.org/protobuf/proto"
 )
@@ -1005,6 +1006,8 @@
 		environ             Environment
 		arguments           []string
 		useBazel            bool
+		bazelDevMode        bool
+		bazelProdMode       bool
 		expectedBuildConfig *smpb.BuildConfig
 	}{
 		{
@@ -1014,7 +1017,6 @@
 				ForceUseGoma:    proto.Bool(false),
 				UseGoma:         proto.Bool(false),
 				UseRbe:          proto.Bool(false),
-				BazelAsNinja:    proto.Bool(false),
 				BazelMixedBuild: proto.Bool(false),
 			},
 		},
@@ -1025,7 +1027,6 @@
 				ForceUseGoma:    proto.Bool(true),
 				UseGoma:         proto.Bool(false),
 				UseRbe:          proto.Bool(false),
-				BazelAsNinja:    proto.Bool(false),
 				BazelMixedBuild: proto.Bool(false),
 			},
 		},
@@ -1036,7 +1037,6 @@
 				ForceUseGoma:    proto.Bool(false),
 				UseGoma:         proto.Bool(true),
 				UseRbe:          proto.Bool(false),
-				BazelAsNinja:    proto.Bool(false),
 				BazelMixedBuild: proto.Bool(false),
 			},
 		},
@@ -1047,7 +1047,6 @@
 				ForceUseGoma:    proto.Bool(false),
 				UseGoma:         proto.Bool(false),
 				UseRbe:          proto.Bool(true),
-				BazelAsNinja:    proto.Bool(false),
 				BazelMixedBuild: proto.Bool(false),
 			},
 		},
@@ -1059,18 +1058,28 @@
 				ForceUseGoma:    proto.Bool(false),
 				UseGoma:         proto.Bool(false),
 				UseRbe:          proto.Bool(false),
-				BazelAsNinja:    proto.Bool(true),
 				BazelMixedBuild: proto.Bool(false),
 			},
 		},
 		{
-			name:    "bazel mixed build",
-			environ: Environment{"USE_BAZEL_ANALYSIS=1"},
+			name:         "bazel mixed build from dev mode",
+			environ:      Environment{},
+			bazelDevMode: true,
 			expectedBuildConfig: &smpb.BuildConfig{
 				ForceUseGoma:    proto.Bool(false),
 				UseGoma:         proto.Bool(false),
 				UseRbe:          proto.Bool(false),
-				BazelAsNinja:    proto.Bool(false),
+				BazelMixedBuild: proto.Bool(true),
+			},
+		},
+		{
+			name:          "bazel mixed build from prod mode",
+			environ:       Environment{},
+			bazelProdMode: true,
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(false),
+				UseGoma:         proto.Bool(false),
+				UseRbe:          proto.Bool(false),
 				BazelMixedBuild: proto.Bool(true),
 			},
 		},
@@ -1083,7 +1092,6 @@
 				ForceUseGoma:    proto.Bool(false),
 				UseGoma:         proto.Bool(false),
 				UseRbe:          proto.Bool(false),
-				BazelAsNinja:    proto.Bool(true),
 				BazelMixedBuild: proto.Bool(false),
 				Targets:         []string{"droid", "dist"},
 			},
@@ -1094,30 +1102,34 @@
 				"FORCE_USE_GOMA=1",
 				"USE_GOMA=1",
 				"USE_RBE=1",
-				"USE_BAZEL_ANALYSIS=1",
 			},
-			useBazel: true,
+			useBazel:     true,
+			bazelDevMode: true,
 			expectedBuildConfig: &smpb.BuildConfig{
 				ForceUseGoma:    proto.Bool(true),
 				UseGoma:         proto.Bool(true),
 				UseRbe:          proto.Bool(true),
-				BazelAsNinja:    proto.Bool(true),
 				BazelMixedBuild: proto.Bool(true),
 			},
 		},
 	}
 
+	ctx := testContext()
 	for _, tc := range tests {
 		t.Run(tc.name, func(t *testing.T) {
 			c := &configImpl{
-				environ:   &tc.environ,
-				useBazel:  tc.useBazel,
-				arguments: tc.arguments,
+				environ:       &tc.environ,
+				bazelDevMode:  tc.bazelDevMode,
+				bazelProdMode: tc.bazelProdMode,
+				arguments:     tc.arguments,
 			}
 			config := Config{c}
+			checkBazelMode(ctx, config)
 			actualBuildConfig := buildConfig(config)
 			if expected := tc.expectedBuildConfig; !proto.Equal(expected, actualBuildConfig) {
-				t.Errorf("Expected build config != actual build config: %#v != %#v", *expected, *actualBuildConfig)
+				t.Errorf("Build config mismatch.\n"+
+					"Expected build config: %#v\n"+
+					"Actual build config: %#v", prototext.Format(expected), prototext.Format(actualBuildConfig))
 			}
 		})
 	}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index f56964c..adc56ac 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -141,7 +141,6 @@
 	"PLATFORM_VERSION",
 	"TARGET_PRODUCT",
 	"TARGET_BUILD_VARIANT",
-	"TARGET_BUILD_TYPE",
 	"TARGET_BUILD_APPS",
 	"TARGET_BUILD_UNBUNDLED",
 	"TARGET_ARCH",
@@ -150,18 +149,11 @@
 	"TARGET_2ND_ARCH",
 	"TARGET_2ND_ARCH_VARIANT",
 	"TARGET_2ND_CPU_VARIANT",
-	"HOST_ARCH",
-	"HOST_2ND_ARCH",
 	"HOST_OS",
 	"HOST_OS_EXTRA",
 	"HOST_CROSS_OS",
-	"HOST_CROSS_ARCH",
-	"HOST_CROSS_2ND_ARCH",
-	"HOST_BUILD_TYPE",
 	"BUILD_ID",
 	"OUT_DIR",
-	"AUX_OS_VARIANT_LIST",
-	"PRODUCT_SOONG_NAMESPACES",
 	"SOONG_SDK_SNAPSHOT_PREFER",
 	"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE",
 	"SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR",
@@ -236,6 +228,13 @@
 
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
+		"TARGET_BUILD_TYPE",
+		"HOST_ARCH",
+		"HOST_2ND_ARCH",
+		"HOST_CROSS_ARCH",
+		"HOST_CROSS_2ND_ARCH",
+		"HOST_BUILD_TYPE",
+		"PRODUCT_SOONG_NAMESPACES",
 
 		"DEFAULT_WARNING_BUILD_MODULE_TYPES",
 		"DEFAULT_ERROR_BUILD_MODULE_TYPES",
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 29c3b65..3ef77c7 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -22,6 +22,7 @@
 	"os"
 	"path/filepath"
 	"strconv"
+	"strings"
 
 	"android/soong/ui/metrics"
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -31,7 +32,6 @@
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
-	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/microfactory"
 
 	"google.golang.org/protobuf/proto"
@@ -169,9 +169,8 @@
 
 	commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list"))
 	invocationEnv := make(map[string]string)
-	debugMode := os.Getenv("SOONG_DELVE") != ""
-
-	if debugMode {
+	if os.Getenv("SOONG_DELVE") != "" {
+		//debug mode
 		commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE"))
 		commonArgs = append(commonArgs, "--delve_path", shared.ResolveDelveBinary())
 		// GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This
@@ -186,7 +185,7 @@
 		invocationEnv["GODEBUG"] = "asyncpreemptoff=1"
 	}
 
-	allArgs := make([]string, 0, 0)
+	var allArgs []string
 	allArgs = append(allArgs, specificArgs...)
 	allArgs = append(allArgs,
 		"--globListDir", name,
@@ -253,6 +252,12 @@
 	if config.EmptyNinjaFile() {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
 	}
+	if config.bazelProdMode {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode")
+	}
+	if config.bazelDevMode {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-dev")
+	}
 
 	mainSoongBuildInvocation := primaryBuilderInvocation(
 		config,
@@ -262,7 +267,7 @@
 		fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
 	)
 
-	if config.bazelBuildMode() == mixedBuild {
+	if config.BazelBuildEnabled() {
 		// Mixed builds call Bazel from soong_build and they therefore need the
 		// Bazel workspace to be available. Make that so by adding a dependency on
 		// the bp2build marker file to the action that invokes soong_build .
@@ -343,12 +348,9 @@
 			soongDocsInvocation},
 	}
 
-	bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
-	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja.d")
-	err := deptools.WriteDepFile(bootstrapDepFile, blueprintArgs.OutFile, bootstrapDeps)
-	if err != nil {
-		ctx.Fatalf("Error writing depfile '%s': %s", bootstrapDepFile, err)
-	}
+	// since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little
+	// reason to write a `bootstrap.ninja.d` file
+	_ = bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
 }
 
 func checkEnvironmentFile(currentEnv *Environment, envFile string) {
@@ -372,9 +374,6 @@
 	// unused variables were changed?
 	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
 
-	buildMode := config.bazelBuildMode()
-	integratedBp2Build := buildMode == mixedBuild
-
 	// This is done unconditionally, but does not take a measurable amount of time
 	bootstrapBlueprint(ctx, config)
 
@@ -404,7 +403,7 @@
 
 		checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongBuildTag))
 
-		if integratedBp2Build || config.Bp2Build() {
+		if config.BazelBuildEnabled() || config.Bp2Build() {
 			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildTag))
 		}
 
@@ -445,6 +444,11 @@
 			"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
 		}
 
+		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
+			ctx.Printf(`CAUTION: arguments in $SOONG_UI_NINJA_ARGS=%q, e.g. "-n", can make soong_build FAIL or INCORRECT`, extra)
+			ninjaArgs = append(ninjaArgs, strings.Fields(extra)...)
+		}
+
 		ninjaArgs = append(ninjaArgs, targets...)
 		cmd := Command(ctx, config, "soong "+name,
 			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
@@ -496,6 +500,7 @@
 	}
 
 	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+	distFile(ctx, config, config.SoongVarsFile(), "soong")
 
 	if !config.SkipKati() {
 		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
diff --git a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
new file mode 100644
index 0000000..356b188
--- /dev/null
+++ b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
@@ -0,0 +1,27 @@
+# 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.
+
+load("//build/bazel/rules/python:py_proto.bzl", "py_proto_library")
+
+proto_library(
+    name = "bp2build_proto",
+    srcs = ["bp2build.proto"],
+    strip_import_prefix = "",
+)
+
+py_proto_library(
+    name = "bp2build_py_proto",
+    deps = [":bp2build_proto"],
+    visibility = ["//build/bazel/scripts/bp2build-progress:__pkg__"],
+)
diff --git a/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
new file mode 100644
index 0000000..4aee88b
--- /dev/null
+++ b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package bp2build_proto;
+
+
+// Conversion progress report for root_modules .
+message Bp2buildConversionProgress {
+
+  // Soong module identifying information.
+  message Module {
+    // Name of the Soong module.
+    string name = 1;
+
+    // Directory that the Soong module is in.
+    string directory = 2;
+
+    // Module type of this module.
+    string type = 3;
+
+    // All unconverted transitive dependencies.
+    repeated string unconverted_deps = 4;
+
+    // Total number of transitive dependencies.
+    int32 num_deps = 5;
+  }
+
+  // Modules that the transitive dependencies were identified for.
+  repeated string root_modules = 1;
+
+  // Names of all dependencies of the root_modules.
+  int32 num_deps = 2;
+
+  // Module with all its unconverted transitive dependencies.
+  repeated Module unconverted = 3;
+}
diff --git a/ui/signal/signal.go b/ui/signal/signal.go
index 4929a7b..552545d 100644
--- a/ui/signal/signal.go
+++ b/ui/signal/signal.go
@@ -31,13 +31,12 @@
 // same time we do. Most of the time this means we just need to ignore the signal and we'll
 // just see errors from all of our subprocesses. But in case that fails, when we get a signal:
 //
-//   1. Wait two seconds to exit normally.
-//   2. Call cancel() which is normally the cancellation of a Context. This will send a SIGKILL
-//      to any subprocesses attached to that context.
-//   3. Wait two seconds to exit normally.
-//   4. Call cleanup() to close the log/trace buffers, then panic.
-//   5. If another two seconds passes (if cleanup got stuck, etc), then panic.
-//
+//  1. Wait two seconds to exit normally.
+//  2. Call cancel() which is normally the cancellation of a Context. This will send a SIGKILL
+//     to any subprocesses attached to that context.
+//  3. Wait two seconds to exit normally.
+//  4. Call cleanup() to close the log/trace buffers, then panic.
+//  5. If another two seconds passes (if cleanup got stuck, etc), then panic.
 func SetupSignals(log logger.Logger, cancel, cleanup func()) {
 	signals := make(chan os.Signal, 5)
 	signal.Notify(signals, os.Interrupt, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM)
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 4d99621..fc0e21a 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -35,8 +35,7 @@
 func NewNinjaReader(ctx logger.Logger, status ToolStatus, fifo string) *NinjaReader {
 	os.Remove(fifo)
 
-	err := syscall.Mkfifo(fifo, 0666)
-	if err != nil {
+	if err := syscall.Mkfifo(fifo, 0666); err != nil {
 		ctx.Fatalf("Failed to mkfifo(%q): %v", fifo, err)
 	}
 
diff --git a/zip/zip.go b/zip/zip.go
index ae379f5..955fe68 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -201,6 +201,16 @@
 	return fmt.Sprintf("path %q is outside relative root %q", x.Path, x.RelativeRoot)
 }
 
+type ConflictingFileError struct {
+	Dest string
+	Prev string
+	Src  string
+}
+
+func (x ConflictingFileError) Error() string {
+	return fmt.Sprintf("destination %q has two files %q and %q", x.Dest, x.Prev, x.Src)
+}
+
 type ZipWriter struct {
 	time         time.Time
 	createdFiles map[string]string
@@ -605,13 +615,24 @@
 		if prev, exists := z.createdDirs[dest]; exists {
 			return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
 		}
+
+		return nil
+	}
+
+	checkDuplicateFiles := func(dest, src string) (bool, error) {
 		if prev, exists := z.createdFiles[dest]; exists {
-			return fmt.Errorf("destination %q has two files %q and %q", dest, prev, src)
+			if prev != src {
+				return true, ConflictingFileError{
+					Dest: dest,
+					Prev: prev,
+					Src:  src,
+				}
+			}
+			return true, nil
 		}
 
 		z.createdFiles[dest] = src
-
-		return nil
+		return false, nil
 	}
 
 	if s.IsDir() {
@@ -625,6 +646,14 @@
 			return err
 		}
 
+		duplicate, err := checkDuplicateFiles(dest, src)
+		if err != nil {
+			return err
+		}
+		if duplicate {
+			return nil
+		}
+
 		return z.writeSymlink(dest, src)
 	} else if s.Mode().IsRegular() {
 		r, err := z.fs.Open(src)
@@ -667,6 +696,14 @@
 			return err
 		}
 
+		duplicate, err := checkDuplicateFiles(dest, src)
+		if err != nil {
+			return err
+		}
+		if duplicate {
+			return nil
+		}
+
 		return z.writeFileContents(header, r)
 	} else {
 		return fmt.Errorf("%s is not a file, directory, or symlink", src)
@@ -678,7 +715,14 @@
 		return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
 	}
 	if prev, exists := z.createdFiles[dest]; exists {
-		return fmt.Errorf("destination %q has two files %q and %q", dest, prev, src)
+		if prev != src {
+			return ConflictingFileError{
+				Dest: dest,
+				Prev: prev,
+				Src:  src,
+			}
+		}
+		return nil
 	}
 
 	if err := z.writeDirectory(filepath.Dir(dest), src, true); err != nil {
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 79cc0b4..c4832dc 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -46,6 +46,7 @@
 	"dangling -> missing": nil,
 	"a/a/d -> b":          nil,
 	"c":                   fileC,
+	"d/a/a":               nil,
 	"l_nl":                []byte("a/a/a\na/a/b\nc\n\\[\n"),
 	"l_sp":                []byte("a/a/a a/a/b c \\["),
 	"l2":                  []byte("missing\n"),
@@ -400,6 +401,17 @@
 				fh("a/a/b", fileB, zip.Deflate),
 			},
 		},
+		{
+			name: "duplicate sources",
+			args: fileArgsBuilder().
+				File("a/a/a").
+				File("a/a/a"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("a/a/a", fileA, zip.Deflate),
+			},
+		},
 
 		// errors
 		{
@@ -427,6 +439,15 @@
 				File("a/a/a"),
 			err: IncorrectRelativeRootError{},
 		},
+		{
+			name: "error conflicting file",
+			args: fileArgsBuilder().
+				SourcePrefixToStrip("a").
+				File("a/a/a").
+				SourcePrefixToStrip("d").
+				File("d/a/a"),
+			err: ConflictingFileError{},
+		},
 	}
 
 	for _, test := range testCases {
@@ -454,13 +475,17 @@
 				t.Fatalf("want error %v, got %v", test.err, err)
 			} else if test.err != nil {
 				if os.IsNotExist(test.err) {
-					if !os.IsNotExist(test.err) {
+					if !os.IsNotExist(err) {
 						t.Fatalf("want error %v, got %v", test.err, err)
 					}
 				} else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr {
 					if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr {
 						t.Fatalf("want error %v, got %v", test.err, err)
 					}
+				} else if _, wantConflictingFileError := test.err.(ConflictingFileError); wantConflictingFileError {
+					if _, gotConflictingFileError := err.(ConflictingFileError); !gotConflictingFileError {
+						t.Fatalf("want error %v, got %v", test.err, err)
+					}
 				} else {
 					t.Fatalf("want error %v, got %v", test.err, err)
 				}