Merge "Dexpreopt: use "speed-profile" if a system server jar has a profile."
diff --git a/.gitignore b/.gitignore
index a09c56d..45884c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 /.idea
+*.iml
diff --git a/OWNERS b/OWNERS
index 0662016..730db7a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -26,6 +26,6 @@
 jingwen@google.com
 
 # EMEA
-hansson@google.com
+hansson@google.com #{LAST_RESORT_SUGGESTION}
 lberki@google.com
-paulduffin@google.com
+paulduffin@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/android/Android.bp b/android/Android.bp
index 8eb55d2..cbd3459 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -98,6 +98,7 @@
         "apex_test.go",
         "arch_test.go",
         "bazel_handler_test.go",
+        "bazel_paths_test.go",
         "bazel_test.go",
         "config_test.go",
         "config_bp2build_test.go",
@@ -107,6 +108,7 @@
         "deptag_test.go",
         "expand_test.go",
         "fixture_test.go",
+        "gen_notice_test.go",
         "license_kind_test.go",
         "license_test.go",
         "licenses_test.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 0cb6946..a5cea17 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -37,17 +37,22 @@
 
 var (
 	Bp2buildDefaultConfig = Bp2BuildConfig{
-		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
-		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
-		"art/libnativebridge":                   Bp2BuildDefaultTrueRecursively,
-		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
-		"art/tools":                             Bp2BuildDefaultTrue,
-		"bionic":                                Bp2BuildDefaultTrueRecursively,
-		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
-		"build/bazel/examples/apex/minimal":     Bp2BuildDefaultTrueRecursively,
+		"prebuilts/runtime/mainline/platform/sdk":            Bp2BuildDefaultTrueRecursively,
+		"art/libartpalette":                                  Bp2BuildDefaultTrueRecursively,
+		"art/libartbase":                                     Bp2BuildDefaultTrueRecursively,
+		"art/libdexfile":                                     Bp2BuildDefaultTrueRecursively,
+		"art/libnativebridge":                                Bp2BuildDefaultTrueRecursively,
+		"art/runtime":                                        Bp2BuildDefaultTrueRecursively,
+		"art/tools":                                          Bp2BuildDefaultTrue,
+		"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,
@@ -102,7 +107,10 @@
 		"external/conscrypt":                                 Bp2BuildDefaultTrue,
 		"external/e2fsprogs":                                 Bp2BuildDefaultTrueRecursively,
 		"external/eigen":                                     Bp2BuildDefaultTrueRecursively,
+		"external/erofs-utils":                               Bp2BuildDefaultTrueRecursively,
 		"external/error_prone":                               Bp2BuildDefaultTrueRecursively,
+		"external/expat":                                     Bp2BuildDefaultTrueRecursively,
+		"external/f2fs-tools":                                Bp2BuildDefaultTrue,
 		"external/flac":                                      Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
 		"external/google-benchmark":                          Bp2BuildDefaultTrueRecursively,
@@ -138,8 +146,9 @@
 		"external/selinux/libselinux":                        Bp2BuildDefaultTrueRecursively,
 		"external/selinux/libsepol":                          Bp2BuildDefaultTrueRecursively,
 		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
+		"external/zopfli":                                    Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                                      Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/codecs/g711/decoder":            Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
@@ -173,8 +182,10 @@
 		"prebuilts/clang/host/linux-x86":                     Bp2BuildDefaultTrueRecursively,
 		"prebuilts/tools/common/m2":                          Bp2BuildDefaultTrue,
 		"system/apex":                                        Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
+		"system/apex/apexer":                                 Bp2BuildDefaultTrue,
 		"system/apex/libs":                                   Bp2BuildDefaultTrueRecursively,
 		"system/apex/proto":                                  Bp2BuildDefaultTrueRecursively,
+		"system/apex/tools":                                  Bp2BuildDefaultTrueRecursively,
 		"system/core/debuggerd":                              Bp2BuildDefaultTrueRecursively,
 		"system/core/diagnose_usb":                           Bp2BuildDefaultTrueRecursively,
 		"system/core/libasyncio":                             Bp2BuildDefaultTrue,
@@ -196,11 +207,11 @@
 		"system/libziparchive":                               Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":                              Bp2BuildDefaultTrueRecursively,
 		"system/media/audio":                                 Bp2BuildDefaultTrueRecursively,
+		"system/memory/libion":                               Bp2BuildDefaultTrueRecursively,
 		"system/memory/libmemunreachable":                    Bp2BuildDefaultTrueRecursively,
 		"system/sepolicy/apex":                               Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":                               Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":                        Bp2BuildDefaultTrueRecursively,
-		"system/unwinding/libbacktrace":                      Bp2BuildDefaultTrueRecursively,
 		"system/unwinding/libunwindstack":                    Bp2BuildDefaultTrueRecursively,
 		"tools/apksig":                                       Bp2BuildDefaultTrue,
 		"tools/platform-compat/java/android/compat":          Bp2BuildDefaultTrueRecursively,
@@ -217,12 +228,14 @@
 		"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_common_rules":/* recursive = */ true,
@@ -255,21 +268,66 @@
 
 	Bp2buildModuleAlwaysConvertList = []string{
 		// cc mainline modules
-		"libnativeloader-headers",
-		"libgui_bufferqueue_sources",
 		"code_coverage.policy",
 		"code_coverage.policy.other",
 		"codec2_soft_exports",
+		"com.android.media.swcodec-androidManifest",
 		"com.android.media.swcodec-ld.config.txt",
-		"libcodec2_headers",
-		"libcodec2_internal",
 		"com.android.media.swcodec-mediaswcodec.rc",
+		"com.android.media.swcodec.certificate",
+		"com.android.media.swcodec.key",
+		"com.android.neuralnetworks-androidManifest",
+		"com.android.neuralnetworks.certificate",
+		"com.android.neuralnetworks.key",
 		"flatbuffer_headers",
 		"gemmlowp_headers",
 		"gl_headers",
-		"libbluetooth-types-header",
+		"libandroid_runtime_lazy",
+		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
 		"libaudioutils_fixedfft",
+		"libbinder_headers",
+		"libbinder_headers_platform_shared",
+		"libbluetooth-types-header",
+		"libbufferhub_headers",
+		"libcodec2",
+		"libcodec2_headers",
+		"libcodec2_internal",
+		"libdmabufheap",
+		"libdvr_headers",
+		"libgsm",
+		"libgui_bufferqueue_sources",
+		"libhardware",
+		"libhardware_headers",
+		"libincfs_headers",
+		"libnativeloader-headers",
+		"libnativewindow_headers",
+		"libneuralnetworks_headers",
+		"libopus",
+		"libpdx_headers",
+		"libprocpartition",
+		"libruy_static",
+		"libserviceutils",
+		"libstagefright_enc_common",
+		"libstagefright_foundation_headers",
+		"libstagefright_headers",
+		"libsurfaceflinger_headers",
+		"libsync",
+		"libtextclassifier_hash_headers",
+		"libtextclassifier_hash_static",
+		"libtflite_kernel_utils",
+		"libtinyxml2",
+		"libui-types",
+		"libui_headers",
+		"libvorbisidec",
+		"media_ndk_headers",
+		"media_plugin_headers",
+		"mediaswcodec.policy",
+		"mediaswcodec.xml",
+		"philox_random",
+		"philox_random_headers",
+		"server_configurable_flags",
+		"tensorflow_headers",
 
 		//external/avb
 		"avbtool",
@@ -284,6 +342,7 @@
 
 		//system/extras/ext4_utils
 		"libext4_utils",
+		"mke2fs_conf",
 
 		//system/extras/libfec
 		"libfec",
@@ -310,22 +369,25 @@
 
 	Bp2buildModuleDoNotConvertList = []string{
 		// cc bugs
-		"libsepol",                                  // TODO(b/207408632): Unsupported case of .l sources in cc library rules
 		"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
 		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
 		"linkerconfig", "mdnsd", // TODO(b/202876379): has arch-variant static_executable
-		"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
+		"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
+		"libjpeg", "libvpx", // TODO(b/233948256): Convert .asm files
+		"art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers
+		"apexer_test",            // Requires aapt2
+		"apexer_test_host_tools",
+		"host_apex_verifier",
 
 		// java bugs
 		"libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
 
 		// python protos
-		"libprotobuf-python",                           // TODO(b/196084681): contains .proto sources
-		"apex_build_info_proto", "apex_manifest_proto", // TODO(b/196084681): a python lib with proto sources
-		"linker_config_proto", // TODO(b/196084681): contains .proto sources
+		"libprotobuf-python", // Has a handcrafted alternative
 
 		// genrule incompatibilities
 		"brotli-fuzzer-corpus",                                       // TODO(b/202015218): outputs are in location incompatible with bazel genrule handling.
@@ -336,6 +398,9 @@
 		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
 		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
 
+		// proto support
+		"libstats_proto_host", // TODO(b/236055697): handle protos from other packages
+
 		// path property for filegroups
 		"conscrypt",                        // TODO(b/210751803), we don't handle path property for filegroups
 		"conscrypt-for-host",               // TODO(b/210751803), we don't handle path property for filegroups
@@ -363,15 +428,14 @@
 		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
 
 		// 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
-		"apex_manifest_proto_java",     // b/210751803, depends on libprotobuf-java-full
-		"art-script",                   // depends on unconverted modules: dalvikvm, dex2oat
-		"bin2c_fastdeployagent",        // depends on unconverted modules: deployagent
-		"chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
+		"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
+		"apex_manifest_proto_java",                                   // b/210751803, depends on libprotobuf-java-full
+		"art-script",                                                 // depends on unconverted modules: dalvikvm, dex2oat
+		"bin2c_fastdeployagent",                                      // depends on unconverted modules: deployagent
 		"com.android.runtime",                                        // depends on unconverted modules: bionic-linker-config, linkerconfig
 		"conv_linker_config",                                         // depends on unconverted modules: linker_config_proto
 		"currysrc",                                                   // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
@@ -405,9 +469,11 @@
 		"stats-log-api-gen",                         // depends on unconverted modules: libstats_proto_host
 		"statslog.cpp", "statslog.h", "statslog.rs", // depends on unconverted modules: stats-log-api-gen
 		"statslog_art.cpp", "statslog_art.h", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
-		"timezone-host",       // depends on unconverted modules: art.module.api.annotations
-		"truth-host-prebuilt", // depends on unconverted modules: truth-prebuilt
-		"truth-prebuilt",      // depends on unconverted modules: asm-7.0, guava
+		"timezone-host",         // depends on unconverted modules: art.module.api.annotations
+		"truth-host-prebuilt",   // depends on unconverted modules: truth-prebuilt
+		"truth-prebuilt",        // depends on unconverted modules: asm-7.0, guava
+		"libartbase-art-gtest",  // depends on unconverted modules: libgtest_isolated, libart, libart-compiler, libdexfile, libprofile
+		"libartbased-art-gtest", // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
 
 		// b/215723302; awaiting tz{data,_version} to then rename targets conflicting with srcs
 		"tzdata",
@@ -417,6 +483,8 @@
 	Bp2buildCcLibraryStaticOnlyList = []string{}
 
 	MixedBuildsDisabledList = []string{
+		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
+
 		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
 
 		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
diff --git a/android/androidmk.go b/android/androidmk.go
index 5c715b4..832c7df 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -288,6 +288,8 @@
 
 // The contributions to the dist.
 type distContributions struct {
+	// Path to license metadata file.
+	licenseMetadataFile Path
 	// List of goals and the dist copy instructions.
 	copiesForGoals []*copiesForGoals
 }
@@ -364,6 +366,8 @@
 	// Collate the contributions this module makes to the dist.
 	distContributions := &distContributions{}
 
+	distContributions.licenseMetadataFile = amod.licenseMetadataFile
+
 	// Iterate over this module's dist structs, merged from the dist and dists properties.
 	for _, dist := range amod.Dists() {
 		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
@@ -456,6 +460,10 @@
 		for _, c := range d.copies {
 			ret = append(
 				ret,
+				fmt.Sprintf("$(if $(strip $(ALL_TARGETS.%s.META_LIC)),,$(eval ALL_TARGETS.%s.META_LIC := %s))\n",
+					c.from.String(), c.from.String(), distContributions.licenseMetadataFile.String()))
+			ret = append(
+				ret,
 				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", d.goals, c.from.String(), c.dest))
 		}
 	}
@@ -937,7 +945,10 @@
 	return !module.Enabled() ||
 		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
-		module.Os() == LinuxBionic
+		module.Os() == LinuxBionic ||
+		// Make does not understand LinuxMusl, except when we are building with USE_HOST_MUSL=true
+		// and all host binaries are LinuxMusl
+		(module.Os() == LinuxMusl && module.Target().HostCross)
 }
 
 // A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index caf11f1..ae2187f 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -50,6 +50,8 @@
 
 func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 
+	m.base().licenseMetadataFile = PathForOutput(ctx, "meta_lic")
+
 	// If the dist_output_file: true then create an output file that is stored in
 	// the OutputFile property of the AndroidMkEntry.
 	if proptools.BoolDefault(m.properties.Dist_output_file, true) {
@@ -198,10 +200,13 @@
 		},
 	}
 
+	dc.licenseMetadataFile = PathForTesting("meta_lic")
 	makeOutput := generateDistContributionsForMake(dc)
 
 	assertStringEquals(t, `.PHONY: my_goal
+$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))
 $(call dist-for-goals,my_goal,one.out:one.out)
+$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))
 $(call dist-for-goals,my_goal,two.out:other.out)
 `, strings.Join(makeOutput, ""))
 }
@@ -243,18 +248,26 @@
 
 	expectedAndroidMkLines := []string{
 		".PHONY: my_second_goal\n",
+		"$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
+		"$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
 		".PHONY: my_third_goal\n",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
 		".PHONY: my_fourth_goal\n",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
 		".PHONY: my_fifth_goal\n",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
 		".PHONY: my_sixth_goal\n",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
 		".PHONY: my_goal my_other_goal\n",
+		"$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
+		"$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))\n",
 		"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
 	}
 
@@ -274,7 +287,7 @@
 		)
 	}
 	for idx, line := range androidMkLines {
-		expectedLine := expectedAndroidMkLines[idx]
+		expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic", module.base().licenseMetadataFile.String())
 		if line != expectedLine {
 			t.Errorf(
 				"Expected AndroidMk line to be '%s', got '%s'",
diff --git a/android/apex.go b/android/apex.go
index 019efdd..c53ceb3 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -836,40 +836,6 @@
 	ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath)
 }
 
-// TODO(b/158059172): remove minSdkVersion allowlist
-var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel {
-	list := make(map[string]ApiLevel, len(apiMap))
-	for name, finalApiInt := range apiMap {
-		list[name] = uncheckedFinalApiLevel(finalApiInt)
-	}
-	return list
-}(map[string]int{
-	"androidx-constraintlayout_constraintlayout-solver-nodeps": 29,
-	"apache-commons-compress":                                  29,
-	"bouncycastle_ike_digests":                                 30,
-	"brotli-java":                                              29,
-	"flatbuffer_headers":                                       30,
-	"gemmlowp_headers":                                         30,
-	"ike-internals":                                            30,
-	"libbrotli":                                                30,
-	"libcrypto_static":                                         30,
-	"libeigen":                                                 30,
-	"liblz4":                                                   30,
-	"libmdnssd":                                                30,
-	"libprocpartition":                                         30,
-	"libprotobuf-java-lite":                                    30,
-	"libprotoutil":                                             30,
-	"libtextclassifier_hash_headers":                           30,
-	"libtextclassifier_hash_static":                            30,
-	"libtflite_kernel_utils":                                   30,
-	"libzstd":                                                  30,
-	"net-utils-framework-common":                               29,
-	"philox_random_headers":                                    30,
-	"philox_random":                                            30,
-	"tensorflow_headers":                                       30,
-	"xz-java":                                                  29,
-})
-
 // Function called while walking an APEX's payload dependencies.
 //
 // Return true if the `to` module should be visited, false otherwise.
@@ -921,15 +887,13 @@
 		}
 		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
 			toName := ctx.OtherModuleName(to)
-			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
-				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
-					"\n\nDependency path: %s\n\n"+
-					"Consider adding 'min_sdk_version: %q' to %q",
-					minSdkVersion, ctx.ModuleName(), err.Error(),
-					ctx.GetPathString(false),
-					minSdkVersion, toName)
-				return false
-			}
+			ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
+				"\n\nDependency path: %s\n\n"+
+				"Consider adding 'min_sdk_version: %q' to %q",
+				minSdkVersion, ctx.ModuleName(), err.Error(),
+				ctx.GetPathString(false),
+				minSdkVersion, toName)
+			return false
 		}
 		return true
 	})
diff --git a/android/api_levels.go b/android/api_levels.go
index 8163894..da50b19 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -54,6 +54,14 @@
 	isPreview bool
 }
 
+func (this ApiLevel) FinalInt() int {
+	if this.IsPreview() {
+		panic("Requested a final int from a non-final ApiLevel")
+	} else {
+		return this.number
+	}
+}
+
 func (this ApiLevel) FinalOrFutureInt() int {
 	if this.IsPreview() {
 		return FutureApiLevelInt
@@ -184,17 +192,9 @@
 // a core-for-system-modules.jar for the module-lib API scope.
 var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31)
 
-// If the `raw` input is the codename of an API level has been finalized, this
-// function returns the API level number associated with that API level. If the
-// input is *not* a finalized codename, the input is returned unmodified.
-//
-// For example, at the time of writing, R has been finalized as API level 30,
-// but S is in development so it has no number assigned. For the following
-// inputs:
-//
-// * "30" -> "30"
-// * "R" -> "30"
-// * "S" -> "S"
+// ReplaceFinalizedCodenames returns the API level number associated with that API level
+// if the `raw` input is the codename of an API level has been finalized.
+// If the input is *not* a finalized codename, the input is returned unmodified.
 func ReplaceFinalizedCodenames(config Config, raw string) string {
 	num, ok := getFinalCodenamesMap(config)[raw]
 	if !ok {
diff --git a/android/arch.go b/android/arch.go
index f732a7d..e72614c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -307,7 +307,7 @@
 	// Linux is the OS for the Linux kernel plus the glibc runtime.
 	Linux = newOsType("linux_glibc", Host, false, X86, X86_64)
 	// LinuxMusl is the OS for the Linux kernel plus the musl runtime.
-	LinuxMusl = newOsType("linux_musl", Host, false, X86, X86_64)
+	LinuxMusl = newOsType("linux_musl", Host, false, X86, X86_64, Arm64, Arm)
 	// Darwin is the OS for MacOS/Darwin host machines.
 	Darwin = newOsType("darwin", Host, false, Arm64, X86_64)
 	// LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
@@ -1825,17 +1825,19 @@
 	for _, t := range targets {
 		if _, found := set[t.Os.String()]; !found {
 			set[t.Os.String()] = true
-			ret = append(ret, commonTargetMap[t.Os.String()])
+			common := commonTargetMap[t.Os.String()]
+			common.HostCross = t.HostCross
+			ret = append(ret, common)
 		}
 	}
 
 	return ret
 }
 
-// firstTarget takes a list of Targets and a list of multilib values and returns a list of Targets
+// 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.
-func firstTarget(targets []Target, filters ...string) []Target {
+func FirstTarget(targets []Target, filters ...string) []Target {
 	// find the first target from each OS
 	var ret []Target
 	hasHost := false
@@ -1865,9 +1867,9 @@
 	case "common_first":
 		buildTargets = getCommonTargets(targets)
 		if prefer32 {
-			buildTargets = append(buildTargets, firstTarget(targets, "lib32", "lib64")...)
+			buildTargets = append(buildTargets, FirstTarget(targets, "lib32", "lib64")...)
 		} else {
-			buildTargets = append(buildTargets, firstTarget(targets, "lib64", "lib32")...)
+			buildTargets = append(buildTargets, FirstTarget(targets, "lib64", "lib32")...)
 		}
 	case "both":
 		if prefer32 {
@@ -1883,12 +1885,12 @@
 		buildTargets = filterMultilibTargets(targets, "lib64")
 	case "first":
 		if prefer32 {
-			buildTargets = firstTarget(targets, "lib32", "lib64")
+			buildTargets = FirstTarget(targets, "lib32", "lib64")
 		} else {
-			buildTargets = firstTarget(targets, "lib64", "lib32")
+			buildTargets = FirstTarget(targets, "lib64", "lib32")
 		}
 	case "first_prefer32":
-		buildTargets = firstTarget(targets, "lib32", "lib64")
+		buildTargets = FirstTarget(targets, "lib32", "lib64")
 	case "prefer32":
 		buildTargets = filterMultilibTargets(targets, "lib32")
 		if len(buildTargets) == 0 {
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 4d9423a..3be9805 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -21,6 +21,7 @@
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -33,6 +34,21 @@
 	"android/soong/bazel"
 )
 
+var (
+	writeBazelFile = pctx.AndroidStaticRule("bazelWriteFileRule", blueprint.RuleParams{
+		Command:        `sed "s/\\\\n/\n/g" ${out}.rsp >${out}`,
+		Rspfile:        "${out}.rsp",
+		RspfileContent: "${content}",
+	}, "content")
+	_                 = pctx.HostBinToolVariable("bazelBuildRunfilesTool", "build-runfiles")
+	buildRunfilesRule = pctx.AndroidStaticRule("bazelBuildRunfiles", blueprint.RuleParams{
+		Command:     "${bazelBuildRunfilesTool} ${in} ${outDir}",
+		Depfile:     "",
+		Description: "",
+		CommandDeps: []string{"${bazelBuildRunfilesTool}"},
+	}, "outDir")
+)
+
 func init() {
 	RegisterMixedBuildsMutator(InitRegistrationContext)
 }
@@ -111,7 +127,7 @@
 
 	// Issues commands to Bazel to receive results for all cquery requests
 	// queued in the BazelContext.
-	InvokeBazel() error
+	InvokeBazel(config Config) error
 
 	// Returns true if bazel is enabled for the given configuration.
 	BazelEnabled() bool
@@ -172,26 +188,26 @@
 	LabelToPythonBinary map[string]string
 }
 
-func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
 	result, _ := m.LabelToOutputFiles[label]
 	return result, nil
 }
 
-func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
 	result, _ := m.LabelToCcInfo[label]
 	return result, nil
 }
 
-func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
 	result, _ := m.LabelToPythonBinary[label]
 	return result, nil
 }
 
-func (m MockBazelContext) InvokeBazel() error {
+func (m MockBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
 
@@ -245,23 +261,23 @@
 	return "", fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (n noopBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+func (n noopBazelContext) GetCcInfo(_ string, _ configKey) (cquery.CcInfo, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) InvokeBazel() error {
+func (n noopBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
 
@@ -303,7 +319,7 @@
 	p := bazelPaths{
 		soongOutDir: c.soongOutDir,
 	}
-	missingEnvVars := []string{}
+	var missingEnvVars []string
 	if len(c.Getenv("BAZEL_HOME")) > 1 {
 		p.homeDir = c.Getenv("BAZEL_HOME")
 	} else {
@@ -361,13 +377,13 @@
 type mockBazelRunner struct {
 	bazelCommandResults map[bazelCommand]string
 	commands            []bazelCommand
+	extraFlags          []string
 }
 
-func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
-	runName bazel.RunName,
-	command bazelCommand,
-	extraFlags ...string) (string, string, error) {
+func (r *mockBazelRunner) issueBazelCommand(_ *bazelPaths, _ bazel.RunName,
+	command bazelCommand, extraFlags ...string) (string, string, error) {
 	r.commands = append(r.commands, command)
+	r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
 	if ret, ok := r.bazelCommandResults[command]; ok {
 		return ret, "", nil
 	}
@@ -393,26 +409,30 @@
 		command.command,
 	}
 	cmdFlags = append(cmdFlags, command.expression)
-	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
+	cmdFlags = append(cmdFlags,
+		// TODO(asmundak): is it needed in every build?
+		"--profile="+shared.BazelMetricsFilename(paths, runName),
 
-	// Set default platforms to canonicalized values for mixed builds requests.
-	// If these are set in the bazelrc, they will have values that are
-	// non-canonicalized to @sourceroot labels, and thus be invalid when
-	// referenced from the buildroot.
-	//
-	// The actual platform values here may be overridden by configuration
-	// transitions from the buildroot.
-	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"))
-	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"))
-	// This should be parameterized on the host OS, but let's restrict to linux
-	// to keep things simple for now.
-	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"))
+		// Set default platforms to canonicalized values for mixed builds requests.
+		// If these are set in the bazelrc, they will have values that are
+		// non-canonicalized to @sourceroot labels, and thus be invalid when
+		// referenced from the buildroot.
+		//
+		// The actual platform values here may be overridden by configuration
+		// transitions from the buildroot.
+		fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"),
+		fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"),
 
-	// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
-	cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
+		// This should be parameterized on the host OS, but let's restrict to linux
+		// to keep things simple for now.
+		fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"),
+
+		// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
+		"--experimental_repository_disable_download",
+
+		// Suppress noise
+		"--ui_event_filters=-INFO",
+		"--noshow_progress")
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
@@ -526,7 +546,7 @@
 	configNodesSection := ""
 
 	labelsByConfig := map[string][]string{}
-	for val, _ := range context.requests {
+	for val := range context.requests {
 		labelString := fmt.Sprintf("\"@%s\"", val.label)
 		configString := getConfigString(val)
 		labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
@@ -564,7 +584,7 @@
 // request type.
 func (context *bazelContext) cqueryStarlarkFileContents() []byte {
 	requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
-	for val, _ := range context.requests {
+	for val := range context.requests {
 		cqueryId := getCqueryId(val)
 		mapEntryString := fmt.Sprintf("%q : True", cqueryId)
 		requestTypeToCqueryIdEntries[val.requestType] =
@@ -676,11 +696,9 @@
 
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
-func (context *bazelContext) InvokeBazel() error {
+func (context *bazelContext) InvokeBazel(config Config) error {
 	context.results = make(map[cqueryKey]string)
 
-	var cqueryOutput string
-	var cqueryErr string
 	var err error
 
 	soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
@@ -697,45 +715,27 @@
 			return err
 		}
 	}
-	err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
 		return err
 	}
-
-	err = ioutil.WriteFile(
-		filepath.Join(mixedBuildsPath, "main.bzl"),
-		context.mainBzlFileContents(), 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
 		return err
 	}
-
-	err = ioutil.WriteFile(
-		filepath.Join(mixedBuildsPath, "BUILD.bazel"),
-		context.mainBuildFileContents(), 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
 		return err
 	}
 	cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
-	err = ioutil.WriteFile(
-		absolutePath(cqueryFileRelpath),
-		context.cqueryStarlarkFileContents(), 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
 		return err
 	}
 
-	buildrootLabel := "@soong_injection//mixed_builds:buildroot"
-	cqueryOutput, cqueryErr, err = context.issueBazelCommand(
-		context.paths,
-		bazel.CqueryBuildRootRunName,
-		bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)},
-		"--output=starlark",
-		"--starlark:file="+absolutePath(cqueryFileRelpath))
-	err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
-		[]byte(cqueryOutput), 0666)
+	const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
+	cqueryCmd := bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
+	cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
+		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
 	if err != nil {
-		return err
+		err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
 	}
-
 	if err != nil {
 		return err
 	}
@@ -747,7 +747,6 @@
 			cqueryResults[splitLine[0]] = splitLine[1]
 		}
 	}
-
 	for val := range context.requests {
 		if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
 			context.results[val] = cqueryResult
@@ -759,21 +758,27 @@
 
 	// Issue an aquery command to retrieve action information about the bazel build tree.
 	//
-	// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
-	var aqueryOutput string
-	aqueryOutput, _, err = context.issueBazelCommand(
-		context.paths,
-		bazel.AqueryBuildRootRunName,
-		bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
-		// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
-		// proto sources, which would add a number of unnecessary dependencies.
-		"--output=jsonproto")
-
-	if err != nil {
-		return err
+	// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+	// proto sources, which would add a number of unnecessary dependencies.
+	extraFlags := []string{"--output=jsonproto", "--include_file_write_contents"}
+	if Bool(config.productVariables.ClangCoverage) {
+		extraFlags = append(extraFlags, "--collect_code_coverage")
+		paths := make([]string, 0, 2)
+		if p := config.productVariables.NativeCoveragePaths; len(p) > 0 {
+			paths = append(paths, JoinWithPrefixAndSeparator(p, "+", ","))
+		}
+		if p := config.productVariables.NativeCoverageExcludePaths; len(p) > 0 {
+			paths = append(paths, JoinWithPrefixAndSeparator(p, "-", ","))
+		}
+		if len(paths) > 0 {
+			extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
+		}
 	}
-
-	context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+	aqueryCmd := bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
+	if aqueryOutput, _, err := context.issueBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
+		extraFlags...); err == nil {
+		context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+	}
 	if err != nil {
 		return err
 	}
@@ -781,12 +786,8 @@
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, _, err = context.issueBazelCommand(
-		context.paths,
-		bazel.BazelBuildPhonyRootRunName,
-		bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"})
-
-	if err != nil {
+	buildCmd := bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+	if _, _, err = context.issueBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd); err != nil {
 		return err
 	}
 
@@ -852,61 +853,111 @@
 		})
 	}
 
-	// Register bazel-owned build statements (obtained from the aquery invocation).
+	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
+	bazelOutDir := path.Join(executionRoot, "bazel-out")
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
-		if len(buildStatement.Command) < 1 {
+		if len(buildStatement.Command) > 0 {
+			rule := NewRuleBuilder(pctx, ctx)
+			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
+			desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
+			rule.Build(fmt.Sprintf("bazel %d", index), desc)
+			continue
+		}
+		// Certain actions returned by aquery (for instance FileWrite) do not contain a command
+		// and thus require special treatment. If BuildStatement were an interface implementing
+		// buildRule(ctx) function, the code here would just call it.
+		// Unfortunately, the BuildStatement is defined in
+		// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
+		// because this would cause circular dependency. So, until we move aquery processing
+		// to the 'android' package, we need to handle special cases here.
+		if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
+			// Pass file contents as the value of the rule's "content" argument.
+			// Escape newlines and $ in the contents (the action "writeBazelFile" restores "\\n"
+			// back to the newline, and Ninja reads $$ as $.
+			escaped := strings.ReplaceAll(strings.ReplaceAll(buildStatement.FileContents, "\n", "\\n"),
+				"$", "$$")
+			ctx.Build(pctx, BuildParams{
+				Rule:        writeBazelFile,
+				Output:      PathForBazelOut(ctx, buildStatement.OutputPaths[0]),
+				Description: fmt.Sprintf("%s %s", buildStatement.Mnemonic, buildStatement.OutputPaths[0]),
+				Args: map[string]string{
+					"content": escaped,
+				},
+			})
+		} else if buildStatement.Mnemonic == "SymlinkTree" {
+			// build-runfiles arguments are the manifest file and the target directory
+			// where it creates the symlink tree according to this manifest (and then
+			// writes the MANIFEST file to it).
+			outManifest := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
+			outManifestPath := outManifest.String()
+			if !strings.HasSuffix(outManifestPath, "MANIFEST") {
+				panic("the base name of the symlink tree action should be MANIFEST, got " + outManifestPath)
+			}
+			outDir := filepath.Dir(outManifestPath)
+			ctx.Build(pctx, BuildParams{
+				Rule:        buildRunfilesRule,
+				Output:      outManifest,
+				Inputs:      []Path{PathForBazelOut(ctx, buildStatement.InputPaths[0])},
+				Description: "symlink tree for " + outDir,
+				Args: map[string]string{
+					"outDir": outDir,
+				},
+			})
+		} else {
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
-		rule := NewRuleBuilder(pctx, ctx)
-		cmd := rule.Command()
+	}
+}
 
-		// cd into Bazel's execution root, which is the action cwd.
-		cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ &&", ctx.Config().BazelContext.OutputBase()))
+// Register bazel-owned build statements (obtained from the aquery invocation).
+func createCommand(cmd *RuleBuilderCommand, buildStatement bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx PathContext) {
+	// executionRoot is the action cwd.
+	cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
 
-		// Remove old outputs, as some actions might not rerun if the outputs are detected.
-		if len(buildStatement.OutputPaths) > 0 {
-			cmd.Text("rm -f")
-			for _, outputPath := range buildStatement.OutputPaths {
-				cmd.Text(outputPath)
-			}
-			cmd.Text("&&")
-		}
-
-		for _, pair := range buildStatement.Env {
-			// Set per-action env variables, if any.
-			cmd.Flag(pair.Key + "=" + pair.Value)
-		}
-
-		// The actual Bazel action.
-		cmd.Text(" " + buildStatement.Command)
-
+	// Remove old outputs, as some actions might not rerun if the outputs are detected.
+	if len(buildStatement.OutputPaths) > 0 {
+		cmd.Text("rm -f")
 		for _, outputPath := range buildStatement.OutputPaths {
-			cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+			cmd.Text(fmt.Sprintf("'%s'", outputPath))
 		}
-		for _, inputPath := range buildStatement.InputPaths {
-			cmd.Implicit(PathForBazelOut(ctx, inputPath))
-		}
-		for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
-			otherDepsetName := bazelDepsetName(inputDepsetHash)
-			cmd.Implicit(PathForPhony(ctx, otherDepsetName))
-		}
+		cmd.Text("&&")
+	}
 
-		if depfile := buildStatement.Depfile; depfile != nil {
-			cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
-		}
+	for _, pair := range buildStatement.Env {
+		// Set per-action env variables, if any.
+		cmd.Flag(pair.Key + "=" + pair.Value)
+	}
 
-		for _, symlinkPath := range buildStatement.SymlinkPaths {
-			cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
-		}
+	// The actual Bazel action.
+	cmd.Text(buildStatement.Command)
 
-		// This is required to silence warnings pertaining to unexpected timestamps. Particularly,
-		// some Bazel builtins (such as files in the bazel_tools directory) have far-future
-		// timestamps. Without restat, Ninja would emit warnings that the input files of a
-		// build statement have later timestamps than the outputs.
-		rule.Restat()
+	for _, outputPath := range buildStatement.OutputPaths {
+		cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+	}
+	for _, inputPath := range buildStatement.InputPaths {
+		cmd.Implicit(PathForBazelOut(ctx, inputPath))
+	}
+	for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
+		otherDepsetName := bazelDepsetName(inputDepsetHash)
+		cmd.Implicit(PathForPhony(ctx, otherDepsetName))
+	}
 
-		desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
-		rule.Build(fmt.Sprintf("bazel %d", index), desc)
+	if depfile := buildStatement.Depfile; depfile != nil {
+		// The paths in depfile are relative to `executionRoot`.
+		// Hence, they need to be corrected by replacing "bazel-out"
+		// with the full `bazelOutDir`.
+		// Otherwise, implicit outputs and implicit inputs under "bazel-out/"
+		// would be deemed missing.
+		// (Note: The regexp uses a capture group because the version of sed
+		//  does not support a look-behind pattern.)
+		replacement := fmt.Sprintf(`&& sed -i'' -E 's@(^|\s|")bazel-out/@\1%s/@g' '%s'`,
+			bazelOutDir, *depfile)
+		cmd.Text(replacement)
+		cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
+	}
+
+	for _, symlinkPath := range buildStatement.SymlinkPaths {
+		cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
 	}
 }
 
@@ -917,15 +968,22 @@
 func getConfigString(key cqueryKey) string {
 	arch := key.configKey.arch
 	if len(arch) == 0 || arch == "common" {
-		// Use host platform, which is currently hardcoded to be x86_64.
-		arch = "x86_64"
+		if key.configKey.osType.Class == Device {
+			// For the generic Android, the expected result is "target|android", which
+			// corresponds to the product_variable_config named "android_target" in
+			// build/bazel/platforms/BUILD.bazel.
+			arch = "target"
+		} else {
+			// Use host platform, which is currently hardcoded to be x86_64.
+			arch = "x86_64"
+		}
 	}
-	os := key.configKey.osType.Name
-	if len(os) == 0 || os == "common_os" || os == "linux_glibc" {
+	osName := key.configKey.osType.Name
+	if len(osName) == 0 || osName == "common_os" || osName == "linux_glibc" {
 		// Use host OS, which is currently hardcoded to be linux.
-		os = "linux"
+		osName = "linux"
 	}
-	return arch + "|" + os
+	return arch + "|" + osName
 }
 
 func GetConfigKey(ctx BaseModuleContext) configKey {
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index cfdccd7..ec2541b 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -4,11 +4,14 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"strings"
 	"testing"
 
 	"android/soong/bazel/cquery"
 )
 
+var testConfig = TestConfig("out", nil, "", nil)
+
 func TestRequestResultsAfterInvokeBazel(t *testing.T) {
 	label := "//foo:bar"
 	cfg := configKey{"arm64_armv8-a", Android}
@@ -16,7 +19,7 @@
 		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
 	})
 	bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
-	err := bazelContext.InvokeBazel()
+	err := bazelContext.InvokeBazel(testConfig)
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
@@ -30,7 +33,7 @@
 
 func TestInvokeBazelWritesBazelFiles(t *testing.T) {
 	bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
-	err := bazelContext.InvokeBazel()
+	err := bazelContext.InvokeBazel(testConfig)
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
@@ -54,16 +57,17 @@
 }
 
 func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
-	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: `
+	type testCase struct {
+		input   string
+		command string
+	}
+
+	var testCases = []testCase{
+		{`
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -73,28 +77,103 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`,
-	})
-	err := bazelContext.InvokeBazel()
+			"cd 'test/exec_root' && rm -f 'one' && touch foo",
+		}, {`
+{
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 10 },
+    { "id": 2, "pathFragmentId": 20 }],
+  "actions": [{
+    "targetId": 100,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["bogus", "command"],
+    "outputIds": [1, 2],
+    "primaryOutputId": 1
+  }],
+  "pathFragments": [
+    { "id": 10, "label": "one", "parentId": 30 },
+    { "id": 20, "label": "one.d", "parentId": 30 },
+    { "id": 30, "label": "parent" }]
+}`,
+			`cd 'test/exec_root' && rm -f 'parent/one' && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1test/bazel_out/@g' 'parent/one.d'`,
+		},
+	}
+
+	for i, testCase := range testCases {
+		bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+			bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: testCase.input})
+
+		err := bazelContext.InvokeBazel(testConfig)
+		if err != nil {
+			t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
+		}
+
+		got := bazelContext.BuildStatementsToRegister()
+		if want := 1; len(got) != want {
+			t.Fatalf("expected %d registered build statements, but got %#v", want, got)
+		}
+
+		cmd := RuleBuilderCommand{}
+		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", PathContextForTesting(TestConfig("out", nil, "", nil)))
+		if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
+			t.Errorf("expected: [%s], actual: [%s]", expected, actual)
+		}
+	}
+}
+
+func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
+	testConfig.productVariables.ClangCoverage = boolPtr(true)
+
+	testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
+	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
+	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,+foo2,-bar1,-bar2`)
+
+	testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
+	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
+	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,-bar1`)
+
+	testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
+	testConfig.productVariables.NativeCoverageExcludePaths = nil
+	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1`)
+
+	testConfig.productVariables.NativeCoveragePaths = nil
+	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
+	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`)
+
+	testConfig.productVariables.ClangCoverage = boolPtr(false)
+	actual := verifyExtraFlags(t, testConfig, ``)
+	if strings.Contains(actual, "--collect_code_coverage") ||
+		strings.Contains(actual, "--instrumentation_filter=") {
+		t.Errorf("Expected code coverage disabled, but got %#v", actual)
+	}
+}
+
+func verifyExtraFlags(t *testing.T, config Config, expected string) string {
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+	err := bazelContext.InvokeBazel(config)
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
 
-	got := bazelContext.BuildStatementsToRegister()
-	if want := 1; len(got) != want {
-		t.Errorf("Expected %d registered build statements, got %#v", want, got)
+	flags := bazelContext.bazelRunner.(*mockBazelRunner).extraFlags
+	if expected := 3; len(flags) != expected {
+		t.Errorf("Expected %d extra flags got %#v", expected, flags)
 	}
+
+	actual := flags[1]
+	if !strings.Contains(actual, expected) {
+		t.Errorf("Expected %#v got %#v", expected, actual)
+	}
+
+	return actual
 }
 
 func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index fa10f62..1d0a6d5 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -449,25 +449,51 @@
 	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
 }
 
-// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
-// bazel-owned outputs.
-func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
-	execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
-	execRootPath := filepath.Join(execRootPathComponents...)
-	validatedExecRootPath, err := validatePath(execRootPath)
+// PathForBazelOutRelative returns a BazelOutPath representing the path under an output directory dedicated to
+// bazel-owned outputs. Calling .Rel() on the result will give the input path as relative to the given
+// relativeRoot.
+func PathForBazelOutRelative(ctx PathContext, relativeRoot string, path string) BazelOutPath {
+	validatedPath, err := validatePath(filepath.Join("execroot", "__main__", path))
 	if err != nil {
 		reportPathError(ctx, err)
 	}
+	relativeRootPath := filepath.Join("execroot", "__main__", relativeRoot)
+	if pathComponents := strings.Split(path, "/"); len(pathComponents) >= 3 &&
+		pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
+		// If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
+		// make it relative to that folder. bazel-out/volatile-status.txt is an example
+		// of something that starts with bazel-out but is not relative to the bin folder
+		relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
+	}
 
-	outputPath := OutputPath{basePath{"", ""},
+	var relPath string
+	if relPath, err = filepath.Rel(relativeRootPath, validatedPath); err != nil || strings.HasPrefix(relPath, "../") {
+		// We failed to make this path relative to execroot/__main__, fall back to a non-relative path
+		// One case where this happens is when path is ../bazel_tools/something
+		relativeRootPath = ""
+		relPath = validatedPath
+	}
+
+	outputPath := OutputPath{
+		basePath{"", ""},
 		ctx.Config().soongOutDir,
-		ctx.Config().BazelContext.OutputBase()}
+		ctx.Config().BazelContext.OutputBase(),
+	}
 
 	return BazelOutPath{
-		OutputPath: outputPath.withRel(validatedExecRootPath),
+		// .withRel() appends its argument onto the current path, and only the most
+		// recently appended part is returned by outputPath.rel().
+		// So outputPath.rel() will return relPath.
+		OutputPath: outputPath.withRel(relativeRootPath).withRel(relPath),
 	}
 }
 
+// PathForBazelOut returns a BazelOutPath representing the path under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, path string) BazelOutPath {
+	return PathForBazelOutRelative(ctx, "", path)
+}
+
 // PathsForBazelOut returns a list of paths representing the paths under an output directory
 // dedicated to Bazel-owned outputs.
 func PathsForBazelOut(ctx PathContext, paths []string) Paths {
diff --git a/android/bazel_paths_test.go b/android/bazel_paths_test.go
new file mode 100644
index 0000000..b047511
--- /dev/null
+++ b/android/bazel_paths_test.go
@@ -0,0 +1,108 @@
+// 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 (
+	"path/filepath"
+	"testing"
+)
+
+type TestBazelPathContext struct{}
+
+func (*TestBazelPathContext) Config() Config {
+	cfg := NullConfig("out", "out/soong")
+	cfg.BazelContext = MockBazelContext{
+		OutputBaseDir: "out/bazel",
+	}
+	return cfg
+}
+
+func (*TestBazelPathContext) AddNinjaFileDeps(deps ...string) {
+	panic("Unimplemented")
+}
+
+func TestPathForBazelOut(t *testing.T) {
+	ctx := &TestBazelPathContext{}
+	out := PathForBazelOut(ctx, "foo/bar/baz/boq.txt")
+	expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/foo/bar/baz/boq.txt")
+	if out.String() != expectedPath {
+		t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+	}
+
+	expectedRelPath := "foo/bar/baz/boq.txt"
+	if out.Rel() != expectedRelPath {
+		t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+	}
+}
+
+func TestPathForBazelOutRelative(t *testing.T) {
+	ctx := &TestBazelPathContext{}
+	out := PathForBazelOutRelative(ctx, "foo/bar", "foo/bar/baz/boq.txt")
+
+	expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/foo/bar/baz/boq.txt")
+	if out.String() != expectedPath {
+		t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+	}
+
+	expectedRelPath := "baz/boq.txt"
+	if out.Rel() != expectedRelPath {
+		t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+	}
+}
+
+func TestPathForBazelOutRelativeUnderBinFolder(t *testing.T) {
+	ctx := &TestBazelPathContext{}
+	out := PathForBazelOutRelative(ctx, "foo/bar", "bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/foo/bar/baz/boq.txt")
+
+	expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/__main__/bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/foo/bar/baz/boq.txt")
+	if out.String() != expectedPath {
+		t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+	}
+
+	expectedRelPath := "baz/boq.txt"
+	if out.Rel() != expectedRelPath {
+		t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+	}
+}
+
+func TestPathForBazelOutOutsideOfExecroot(t *testing.T) {
+	ctx := &TestBazelPathContext{}
+	out := PathForBazelOut(ctx, "../bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar")
+
+	expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar")
+	if out.String() != expectedPath {
+		t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+	}
+
+	expectedRelPath := "execroot/bazel_tools/linux_x86_64-fastbuild/bin/tools/android/java_base_extras.jar"
+	if out.Rel() != expectedRelPath {
+		t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+	}
+}
+
+func TestPathForBazelOutRelativeWithParentDirectoryRoot(t *testing.T) {
+	ctx := &TestBazelPathContext{}
+	out := PathForBazelOutRelative(ctx, "../bazel_tools", "../bazel_tools/foo/bar/baz.sh")
+
+	expectedPath := filepath.Join(ctx.Config().BazelContext.OutputBase(), "execroot/bazel_tools/foo/bar/baz.sh")
+	if out.String() != expectedPath {
+		t.Errorf("incorrect OutputPath: expected %q, got %q", expectedPath, out.String())
+	}
+
+	expectedRelPath := "foo/bar/baz.sh"
+	if out.Rel() != expectedRelPath {
+		t.Errorf("incorrect OutputPath.Rel(): expected %q, got %q", expectedRelPath, out.Rel())
+	}
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
index 6339a71..acebdbb 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -89,6 +89,7 @@
 	writeProp("ro.build.version.security_patch", config.PlatformSecurityPatch())
 	writeProp("ro.build.version.base_os", config.PlatformBaseOS())
 	writeProp("ro.build.version.min_supported_target_sdk", config.PlatformMinSupportedTargetSdkVersion())
+	writeProp("ro.build.version.known_codenames", config.PlatformVersionKnownCodenames())
 
 	if config.Eng() {
 		writeProp("ro.build.type", "eng")
@@ -109,7 +110,6 @@
 		writeProp("ro.build.display.id", $BUILD_DISPLAY_ID)
 		writeProp("ro.build.version.incremental", $BUILD_NUMBER)
 		writeProp("ro.build.version.preview_sdk_fingerprint", $PLATFORM_PREVIEW_SDK_FINGERPRINT)
-		writeProp("ro.build.version.known_codenames", $PLATFORM_VERSION_KNOWN_CODENAMES)
 		writeProp("ro.build.version.release_or_preview_display", $PLATFORM_DISPLAY_VERSION)
 		writeProp("ro.build.date", `$DATE`)
 		writeProp("ro.build.date.utc", `$DATE +%s`)
diff --git a/android/config.go b/android/config.go
index 403999d..47346fc 100644
--- a/android/config.go
+++ b/android/config.go
@@ -416,7 +416,7 @@
 	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.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")
@@ -554,7 +554,7 @@
 	// Compilation targets for Android.
 	if len(config.Targets[Android]) > 0 {
 		config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
-		config.AndroidFirstDeviceTarget = firstTarget(config.Targets[Android], "lib64", "lib32")[0]
+		config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	}
 
 	config.BazelContext, err = NewBazelContext(config)
@@ -698,6 +698,10 @@
 	return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
 }
 
+func (c *config) TargetsJava17() bool {
+	return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_17")
+}
+
 // EnvDeps returns the environment variables this build depends on. The first
 // call to this function blocks future reads from the environment.
 func (c *config) EnvDeps() map[string]string {
@@ -789,6 +793,10 @@
 	return String(c.productVariables.Platform_version_last_stable)
 }
 
+func (c *config) PlatformVersionKnownCodenames() string {
+	return String(c.productVariables.Platform_version_known_codenames)
+}
+
 func (c *config) MinSupportedSdkVersion() ApiLevel {
 	return uncheckedFinalApiLevel(19)
 }
@@ -1689,6 +1697,10 @@
 	return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
 }
 
+func (c *deviceConfig) BuildBrokenDepfile() bool {
+	return Bool(c.config.productVariables.BuildBrokenDepfile)
+}
+
 func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
 	return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
 }
diff --git a/android/deapexer.go b/android/deapexer.go
index 265f531..6a93f60 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"strings"
+
 	"github.com/google/blueprint"
 )
 
@@ -148,12 +150,19 @@
 func FindDeapexerProviderForModule(ctx ModuleContext) *DeapexerInfo {
 	var di *DeapexerInfo
 	ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
-		p := ctx.OtherModuleProvider(m, DeapexerProvider).(DeapexerInfo)
+		c := ctx.OtherModuleProvider(m, DeapexerProvider).(DeapexerInfo)
+		p := &c
 		if di != nil {
+			// If two DeapexerInfo providers have been found then check if they are
+			// equivalent. If they are then use the selected one, otherwise fail.
+			if selected := equivalentDeapexerInfoProviders(di, p); selected != nil {
+				di = selected
+				return
+			}
 			ctx.ModuleErrorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s",
 				di.ApexModuleName(), p.ApexModuleName())
 		}
-		di = &p
+		di = p
 	})
 	if di != nil {
 		return di
@@ -162,3 +171,33 @@
 	ctx.ModuleErrorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
 	return nil
 }
+
+// removeCompressedApexSuffix removes the _compressed suffix from the name if present.
+func removeCompressedApexSuffix(name string) string {
+	return strings.TrimSuffix(name, "_compressed")
+}
+
+// equivalentDeapexerInfoProviders checks to make sure that the two DeapexerInfo structures are
+// equivalent.
+//
+// At the moment <x> and <x>_compressed APEXes are treated as being equivalent.
+//
+// If they are not equivalent then this returns nil, otherwise, this returns the DeapexerInfo that
+// should be used by the build, which is always the uncompressed one. That ensures that the behavior
+// of the build is not dependent on which prebuilt APEX is visited first.
+func equivalentDeapexerInfoProviders(p1 *DeapexerInfo, p2 *DeapexerInfo) *DeapexerInfo {
+	n1 := removeCompressedApexSuffix(p1.ApexModuleName())
+	n2 := removeCompressedApexSuffix(p2.ApexModuleName())
+
+	// If the names don't match then they are not equivalent.
+	if n1 != n2 {
+		return nil
+	}
+
+	// Select the uncompressed APEX.
+	if n1 == removeCompressedApexSuffix(n1) {
+		return p1
+	} else {
+		return p2
+	}
+}
diff --git a/android/defaults.go b/android/defaults.go
index 8b121f6..54f44bc 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"bytes"
+	"fmt"
 	"reflect"
 
 	"github.com/google/blueprint"
@@ -67,9 +69,11 @@
 	// Set the property structures into which defaults will be added.
 	setProperties(props []interface{}, variableProperties interface{})
 
-	// Apply defaults from the supplied Defaults to the property structures supplied to
+	// Apply defaults from the supplied DefaultsModule to the property structures supplied to
 	// setProperties(...).
-	applyDefaults(TopDownMutatorContext, []Defaults)
+	applyDefaults(TopDownMutatorContext, []DefaultsModule)
+
+	applySingleDefaultsWithTracker(EarlyModuleContext, DefaultsModule, defaultsTrackerFunc)
 
 	// Set the hook to be called after any defaults have been applied.
 	//
@@ -115,9 +119,23 @@
 	Defaults_visibility []string
 }
 
+// AdditionalDefaultsProperties contains properties of defaults modules which
+// can have other defaults applied.
+type AdditionalDefaultsProperties struct {
+
+	// The list of properties set by the default whose values must not be changed by any module that
+	// applies these defaults. It is an error if a property is not supported by the defaults module or
+	// has not been set to a non-zero value. If this contains "*" then that must be the only entry in
+	// which case all properties that are set on this defaults will be protected (except the
+	// protected_properties and visibility.
+	Protected_properties []string
+}
+
 type DefaultsModuleBase struct {
 	DefaultableModuleBase
 
+	defaultsProperties AdditionalDefaultsProperties
+
 	// Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
 	// target. This is primarily useful for modules that were architecture specific and instead are
 	// handled in Bazel as a select().
@@ -151,6 +169,18 @@
 	// DefaultsModuleBase will type-assert to the Defaults interface.
 	isDefaults() bool
 
+	// additionalDefaultableProperties returns additional properties provided by the defaults which
+	// can themselves have defaults applied.
+	additionalDefaultableProperties() []interface{}
+
+	// protectedProperties returns the names of the properties whose values cannot be changed by a
+	// module that applies these defaults.
+	protectedProperties() []string
+
+	// setProtectedProperties sets the names of the properties whose values cannot be changed by a
+	// module that applies these defaults.
+	setProtectedProperties(protectedProperties []string)
+
 	// Get the structures containing the properties for which defaults can be provided.
 	properties() []interface{}
 
@@ -167,6 +197,18 @@
 	Bazelable
 }
 
+func (d *DefaultsModuleBase) additionalDefaultableProperties() []interface{} {
+	return []interface{}{&d.defaultsProperties}
+}
+
+func (d *DefaultsModuleBase) protectedProperties() []string {
+	return d.defaultsProperties.Protected_properties
+}
+
+func (d *DefaultsModuleBase) setProtectedProperties(protectedProperties []string) {
+	d.defaultsProperties.Protected_properties = protectedProperties
+}
+
 func (d *DefaultsModuleBase) properties() []interface{} {
 	return d.defaultableProperties
 }
@@ -190,6 +232,10 @@
 		&ApexProperties{},
 		&distProperties{})
 
+	// Additional properties of defaults modules that can themselves have
+	// defaults applied.
+	module.AddProperties(module.additionalDefaultableProperties()...)
+
 	// Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
 	InitBazelModule(module)
 	initAndroidModuleBase(module)
@@ -218,6 +264,57 @@
 	// The applicable licenses property for defaults is 'licenses'.
 	setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
 
+	AddLoadHook(module, func(ctx LoadHookContext) {
+
+		protectedProperties := module.protectedProperties()
+		if len(protectedProperties) == 0 {
+			return
+		}
+
+		propertiesAvailable := map[string]struct{}{}
+		propertiesSet := map[string]struct{}{}
+
+		// A defaults tracker which will keep track of which properties have been set on this module.
+		collector := func(defaults DefaultsModule, property string, dstValue interface{}, srcValue interface{}) bool {
+			value := reflect.ValueOf(dstValue)
+			propertiesAvailable[property] = struct{}{}
+			if !value.IsZero() {
+				propertiesSet[property] = struct{}{}
+			}
+			// Skip all the properties so that there are no changes to the defaults.
+			return false
+		}
+
+		// Try and apply this module's defaults to itself, so that the properties can be collected but
+		// skip all the properties so it doesn't actually do anything.
+		module.applySingleDefaultsWithTracker(ctx, module, collector)
+
+		if InList("*", protectedProperties) {
+			if len(protectedProperties) != 1 {
+				ctx.PropertyErrorf("protected_properties", `if specified then "*" must be the only property listed`)
+				return
+			}
+
+			// Do not automatically protect the protected_properties property.
+			delete(propertiesSet, "protected_properties")
+
+			// Or the visibility property.
+			delete(propertiesSet, "visibility")
+
+			// Replace the "*" with the names of all the properties that have been set.
+			protectedProperties = SortedStringKeys(propertiesSet)
+			module.setProtectedProperties(protectedProperties)
+		} else {
+			for _, property := range protectedProperties {
+				if _, ok := propertiesAvailable[property]; !ok {
+					ctx.PropertyErrorf(property, "property is not supported by this module type %q",
+						ctx.ModuleType())
+				} else if _, ok := propertiesSet[property]; !ok {
+					ctx.PropertyErrorf(property, "is not set; protected properties must be explicitly set")
+				}
+			}
+		}
+	})
 }
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
@@ -269,35 +366,204 @@
 	b.setNamespacedVariableProps(dst)
 }
 
+// defaultValueInfo contains information about each default value that applies to a protected
+// property.
+type defaultValueInfo struct {
+	// The DefaultsModule providing the value, which may be defined on that module or applied as a
+	// default from other modules.
+	module Module
+
+	// The default value, as returned by getComparableValue
+	defaultValue reflect.Value
+}
+
+// protectedPropertyInfo contains information about each property that has to be protected when
+// applying defaults.
+type protectedPropertyInfo struct {
+	// True if the property was set on the module to which defaults are applied, this is an error.
+	propertySet bool
+
+	// The original value of the property on the module, as returned by getComparableValue.
+	originalValue reflect.Value
+
+	// A list of defaults for the property that are being applied.
+	defaultValues []defaultValueInfo
+}
+
+// getComparableValue takes a reflect.Value that may be a pointer to another value and returns a
+// reflect.Value to the underlying data or the original if was not a pointer or was nil. The
+// returned values can then be compared for equality.
+func getComparableValue(value reflect.Value) reflect.Value {
+	if value.IsZero() {
+		return value
+	}
+	for value.Kind() == reflect.Ptr {
+		value = value.Elem()
+	}
+	return value
+}
+
 func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
-	defaultsList []Defaults) {
+	defaultsList []DefaultsModule) {
+
+	// Collate information on all the properties protected by each of the default modules applied
+	// to this module.
+	allProtectedProperties := map[string]*protectedPropertyInfo{}
+	for _, defaults := range defaultsList {
+		for _, property := range defaults.protectedProperties() {
+			info := allProtectedProperties[property]
+			if info == nil {
+				info = &protectedPropertyInfo{}
+				allProtectedProperties[property] = info
+			}
+		}
+	}
+
+	// If there are any protected properties then collate information about attempts to change them.
+	var protectedPropertyInfoCollector defaultsTrackerFunc
+	if len(allProtectedProperties) > 0 {
+		protectedPropertyInfoCollector = func(defaults DefaultsModule, property string,
+			dstValue interface{}, srcValue interface{}) bool {
+
+			// If the property is not protected then return immediately.
+			info := allProtectedProperties[property]
+			if info == nil {
+				return true
+			}
+
+			currentValue := reflect.ValueOf(dstValue)
+			if info.defaultValues == nil {
+				info.propertySet = !currentValue.IsZero()
+				info.originalValue = getComparableValue(currentValue)
+			}
+
+			defaultValue := reflect.ValueOf(srcValue)
+			if !defaultValue.IsZero() {
+				info.defaultValues = append(info.defaultValues,
+					defaultValueInfo{defaults, getComparableValue(defaultValue)})
+			}
+
+			return true
+		}
+	}
 
 	for _, defaults := range defaultsList {
 		if ctx.Config().runningAsBp2Build {
 			applyNamespacedVariableDefaults(defaults, ctx)
 		}
-		for _, prop := range defaultable.defaultableProperties {
-			if prop == defaultable.defaultableVariableProperties {
-				defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
-			} else {
-				defaultable.applyDefaultProperties(ctx, defaults, prop)
+
+		defaultable.applySingleDefaultsWithTracker(ctx, defaults, protectedPropertyInfoCollector)
+	}
+
+	// Check the status of any protected properties.
+	for property, info := range allProtectedProperties {
+		if len(info.defaultValues) == 0 {
+			// No defaults were applied to the protected properties. Possibly because this module type
+			// does not support any of them.
+			continue
+		}
+
+		// Check to make sure that there are no conflicts between the defaults.
+		conflictingDefaults := false
+		previousDefaultValue := reflect.ValueOf(false)
+		for _, defaultInfo := range info.defaultValues {
+			defaultValue := defaultInfo.defaultValue
+			if previousDefaultValue.IsZero() {
+				previousDefaultValue = defaultValue
+			} else if !reflect.DeepEqual(previousDefaultValue.Interface(), defaultValue.Interface()) {
+				conflictingDefaults = true
+				break
 			}
 		}
+
+		if conflictingDefaults {
+			var buf bytes.Buffer
+			for _, defaultInfo := range info.defaultValues {
+				buf.WriteString(fmt.Sprintf("\n    defaults module %q provides value %#v",
+					ctx.OtherModuleName(defaultInfo.module), defaultInfo.defaultValue))
+			}
+			result := buf.String()
+			ctx.ModuleErrorf("has conflicting default values for protected property %q:%s", property, result)
+			continue
+		}
+
+		// Now check to see whether there the current module tried to override/append to the defaults.
+		if info.propertySet {
+			originalValue := info.originalValue
+			// Just compare against the first defaults.
+			defaultValue := info.defaultValues[0].defaultValue
+			defaults := info.defaultValues[0].module
+
+			if originalValue.Kind() == reflect.Slice {
+				ctx.ModuleErrorf("attempts to append %q to protected property %q's value of %q defined in module %q",
+					originalValue,
+					property,
+					defaultValue,
+					ctx.OtherModuleName(defaults))
+			} else {
+				same := reflect.DeepEqual(originalValue.Interface(), defaultValue.Interface())
+				message := ""
+				if same {
+					message = fmt.Sprintf(" with a matching value (%#v) so this property can simply be removed.", originalValue)
+				} else {
+					message = fmt.Sprintf(" with a different value (override %#v with %#v) so removing the property may necessitate other changes.", defaultValue, originalValue)
+				}
+				ctx.ModuleErrorf("attempts to override protected property %q defined in module %q%s",
+					property,
+					ctx.OtherModuleName(defaults), message)
+			}
+		}
+	}
+}
+
+func (defaultable *DefaultableModuleBase) applySingleDefaultsWithTracker(ctx EarlyModuleContext, defaults DefaultsModule, tracker defaultsTrackerFunc) {
+	for _, prop := range defaultable.defaultableProperties {
+		var err error
+		if prop == defaultable.defaultableVariableProperties {
+			err = defaultable.applyDefaultVariableProperties(defaults, prop, tracker)
+		} else {
+			err = defaultable.applyDefaultProperties(defaults, prop, tracker)
+		}
+		if err != nil {
+			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+				ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+			} else {
+				panic(err)
+			}
+		}
+	}
+}
+
+// defaultsTrackerFunc is the type of a function that can be used to track how defaults are applied.
+type defaultsTrackerFunc func(defaults DefaultsModule, property string,
+	dstValue interface{}, srcValue interface{}) bool
+
+// filterForTracker wraps a defaultsTrackerFunc in a proptools.ExtendPropertyFilterFunc
+func filterForTracker(defaults DefaultsModule, tracker defaultsTrackerFunc) proptools.ExtendPropertyFilterFunc {
+	if tracker == nil {
+		return nil
+	}
+	return func(property string,
+		dstField, srcField reflect.StructField,
+		dstValue, srcValue interface{}) (bool, error) {
+
+		apply := tracker(defaults, property, dstValue, srcValue)
+		return apply, nil
 	}
 }
 
 // Product variable properties need special handling, the type of the filtered product variable
 // property struct may not be identical between the defaults module and the defaultable module.
 // Use PrependMatchingProperties to apply whichever properties match.
-func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
-	defaults Defaults, defaultableProp interface{}) {
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(defaults DefaultsModule,
+	defaultableProp interface{}, tracker defaultsTrackerFunc) error {
 	if defaultableProp == nil {
-		return
+		return nil
 	}
 
 	defaultsProp := defaults.productVariableProperties()
 	if defaultsProp == nil {
-		return
+		return nil
 	}
 
 	dst := []interface{}{
@@ -307,31 +573,26 @@
 		proptools.CloneEmptyProperties(reflect.ValueOf(defaultsProp)).Interface(),
 	}
 
-	err := proptools.PrependMatchingProperties(dst, defaultsProp, nil)
-	if err != nil {
-		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-		} else {
-			panic(err)
-		}
-	}
+	filter := filterForTracker(defaults, tracker)
+
+	return proptools.PrependMatchingProperties(dst, defaultsProp, filter)
 }
 
-func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
-	defaults Defaults, defaultableProp interface{}) {
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(defaults DefaultsModule,
+	defaultableProp interface{}, checker defaultsTrackerFunc) error {
+
+	filter := filterForTracker(defaults, checker)
 
 	for _, def := range defaults.properties() {
 		if proptools.TypeEqual(defaultableProp, def) {
-			err := proptools.PrependProperties(defaultableProp, def, nil)
+			err := proptools.PrependProperties(defaultableProp, def, filter)
 			if err != nil {
-				if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-					ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-				} else {
-					panic(err)
-				}
+				return err
 			}
 		}
 	}
+
+	return nil
 }
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
@@ -348,12 +609,12 @@
 func defaultsMutator(ctx TopDownMutatorContext) {
 	if defaultable, ok := ctx.Module().(Defaultable); ok {
 		if len(defaultable.defaults().Defaults) > 0 {
-			var defaultsList []Defaults
+			var defaultsList []DefaultsModule
 			seen := make(map[Defaults]bool)
 
 			ctx.WalkDeps(func(module, parent Module) bool {
 				if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
-					if defaults, ok := module.(Defaults); ok {
+					if defaults, ok := module.(DefaultsModule); ok {
 						if !seen[defaults] {
 							seen[defaults] = true
 							defaultsList = append(defaultsList, defaults)
diff --git a/android/defaults_test.go b/android/defaults_test.go
index a7542ab..d80f40c 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -19,7 +19,14 @@
 )
 
 type defaultsTestProperties struct {
-	Foo []string
+	Foo    []string
+	Bar    []string
+	Nested struct {
+		Fizz *bool
+	}
+	Other struct {
+		Buzz *string
+	}
 }
 
 type defaultsTestModule struct {
@@ -130,3 +137,167 @@
 	// TODO: missing transitive defaults is currently not handled
 	_ = missingTransitiveDefaults
 }
+
+func TestProtectedProperties_ProtectedPropertyNotSet(t *testing.T) {
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["foo"],
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
+		"module \"transitive\": foo: is not set; protected properties must be explicitly set")).
+		RunTest(t)
+}
+
+func TestProtectedProperties_ProtectedPropertyNotLeaf(t *testing.T) {
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["nested"],
+			nested: {
+				fizz: true,
+			},
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`\Qmodule "transitive": nested: property is not supported by this module type "defaults"\E`)).
+		RunTest(t)
+}
+
+// TestProtectedProperties_ApplyDefaults makes sure that the protected_properties property has
+// defaults applied.
+func TestProtectedProperties_HasDefaultsApplied(t *testing.T) {
+
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["foo"],
+			foo: ["transitive"],
+		}
+
+		defaults {
+			name: "defaults",
+			defaults: ["transitive"],
+			protected_properties: ["bar"],
+			bar: ["defaults"],
+		}
+	`
+
+	result := GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	defaults := result.Module("defaults", "").(DefaultsModule)
+	AssertDeepEquals(t, "defaults protected properties", []string{"foo", "bar"}, defaults.protectedProperties())
+}
+
+// TestProtectedProperties_ProtectAllProperties makes sure that protected_properties: ["*"] protects
+// all properties.
+func TestProtectedProperties_ProtectAllProperties(t *testing.T) {
+
+	bp := `
+		defaults {
+			name: "transitive",
+			protected_properties: ["other.buzz"],
+			other: {
+				buzz: "transitive",
+			},
+		}
+
+		defaults {
+			name: "defaults",
+			defaults: ["transitive"],
+			visibility: ["//visibility:private"],
+			protected_properties: ["*"],
+			foo: ["other"],
+			bar: ["defaults"],
+			nested: {
+				fizz: true,
+			}
+		}
+	`
+
+	result := GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	defaults := result.Module("defaults", "").(DefaultsModule)
+	AssertDeepEquals(t, "defaults protected properties", []string{"other.buzz", "bar", "foo", "nested.fizz"},
+		defaults.protectedProperties())
+}
+
+func TestProtectedProperties_DetectedOverride(t *testing.T) {
+	bp := `
+		defaults {
+			name: "defaults",
+			protected_properties: ["foo", "nested.fizz"],
+			foo: ["defaults"],
+			nested: {
+				fizz: true,
+			},
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults"],
+			foo: ["module"],
+			nested: {
+				fizz: false,
+			},
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(
+		[]string{
+			`\Qmodule "foo": attempts to append ["module"] to protected property "foo"'s value of ["defaults"] defined in module "defaults"\E`,
+			`\Qmodule "foo": attempts to override protected property "nested.fizz" defined in module "defaults" with a different value (override true with false) so removing the property may necessitate other changes.\E`,
+		})).RunTest(t)
+}
+
+func TestProtectedProperties_DefaultsConflict(t *testing.T) {
+	bp := `
+		defaults {
+			name: "defaults1",
+			protected_properties: ["other.buzz"],
+			other: {
+				buzz: "value",
+			},
+		}
+
+		defaults {
+			name: "defaults2",
+			protected_properties: ["other.buzz"],
+			other: {
+				buzz: "another",
+			},
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults1", "defaults2"],
+		}
+	`
+
+	GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`\Qmodule "foo": has conflicting default values for protected property "other.buzz":
+    defaults module "defaults1" provides value "value"
+    defaults module "defaults2" provides value "another"\E`,
+	)).RunTest(t)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index 485e0b9..9e5769a 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -116,6 +116,23 @@
 	return module
 }
 
+var _ blueprint.JSONActionSupplier = (*fileGroup)(nil)
+
+func (fg *fileGroup) JSONActions() []blueprint.JSONAction {
+	ins := make([]string, 0, len(fg.srcs))
+	outs := make([]string, 0, len(fg.srcs))
+	for _, p := range fg.srcs {
+		ins = append(ins, p.String())
+		outs = append(outs, p.Rel())
+	}
+	return []blueprint.JSONAction{
+		blueprint.JSONAction{
+			Inputs:  ins,
+			Outputs: outs,
+		},
+	}
+}
+
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
 	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
 	if fg.properties.Path != nil {
@@ -161,8 +178,7 @@
 
 	bazelOuts := make(Paths, 0, len(filePaths))
 	for _, p := range filePaths {
-		src := PathForBazelOut(ctx, p)
-		bazelOuts = append(bazelOuts, src)
+		bazelOuts = append(bazelOuts, PathForBazelOutRelative(ctx, ctx.ModuleDir(), p))
 	}
 
 	fg.srcs = bazelOuts
diff --git a/android/gen_notice.go b/android/gen_notice.go
index fda91ac..e2b839f 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -67,7 +67,12 @@
 		if ctx.Failed() {
 			return
 		}
-		out(ctx, gm.output, ctx.ModuleName(gm), proptools.StringDefault(gm.properties.ArtifactName, defaultName), "", modules...)
+		out(ctx, gm.output, ctx.ModuleName(gm),
+			proptools.StringDefault(gm.properties.ArtifactName, defaultName),
+			[]string{
+				ctx.Config().OutDir() + "/",
+				ctx.Config().SoongOutDir() + "/",
+			}, modules...)
 	})
 }
 
diff --git a/android/gen_notice_test.go b/android/gen_notice_test.go
new file mode 100644
index 0000000..4ad2ecf
--- /dev/null
+++ b/android/gen_notice_test.go
@@ -0,0 +1,164 @@
+package android
+
+import (
+	"testing"
+
+	"github.com/google/blueprint"
+)
+
+var genNoticeTests = []struct {
+	name           string
+	fs             MockFS
+	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(`
+				gen_notice {
+					name: "top_notice",
+					for: ["top_rule"],
+				}`),
+			"other/Android.bp": []byte(`
+				mock_genrule {
+					name: "other_rule",
+					dep: ["top_notice"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "top_notice": for: no "top_rule" module exists`,
+		},
+	},
+	{
+		name: "doubly bad gen_notice",
+		fs: map[string][]byte{
+			"top/Android.bp": []byte(`
+				gen_notice {
+					name: "top_notice",
+					for: ["top_rule", "other_rule"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "top_notice": for: modules "top_rule", "other_rule" do not exist`,
+		},
+	},
+	{
+		name: "good gen_notice",
+		fs: map[string][]byte{
+			"top/Android.bp": []byte(`
+				gen_notice {
+					name: "top_notice",
+					for: ["top_rule"],
+				}
+
+				mock_genrule {
+					name: "top_rule",
+					dep: ["top_notice"],
+				}`),
+			"other/Android.bp": []byte(`
+				mock_genrule {
+					name: "other_rule",
+					dep: ["top_notice"],
+				}`),
+		},
+	},
+	{
+		name: "multiple license kinds",
+		fs: map[string][]byte{
+			"top/Android.bp": []byte(`
+				gen_notice {
+					name: "top_notice",
+					for: ["top_rule"],
+				}
+
+				gen_notice {
+					name: "top_html_notice",
+					html: true,
+					for: ["top_rule"],
+				}
+
+				gen_notice {
+					name: "top_xml_notice",
+					xml: true,
+					for: ["top_notice"],
+				}
+
+				mock_genrule {
+					name: "top_rule",
+					dep: [
+						"top_notice",
+						"top_html_notice",
+						"top_xml_notice",
+					],
+				}`),
+			"other/Android.bp": []byte(`
+				mock_genrule {
+					name: "other_rule",
+					dep: ["top_xml_notice"],
+				}`),
+		},
+	},
+}
+
+func TestGenNotice(t *testing.T) {
+	for _, test := range genNoticeTests {
+		t.Run(test.name, func(t *testing.T) {
+			GroupFixturePreparers(
+				PrepareForTestWithGenNotice,
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("mock_genrule", newMockGenruleModule)
+				}),
+				test.fs.AddToFixture(),
+			).
+				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+				RunTest(t)
+		})
+	}
+}
+
+type mockGenruleProperties struct {
+	Dep []string
+}
+
+type mockGenruleModule struct {
+	ModuleBase
+	DefaultableModuleBase
+
+	properties mockGenruleProperties
+}
+
+func newMockGenruleModule() Module {
+	m := &mockGenruleModule{}
+	m.AddProperties(&m.properties)
+	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+	InitDefaultableModule(m)
+	return m
+}
+
+type genruleDepTag struct {
+	blueprint.BaseDependencyTag
+}
+
+func (j *mockGenruleModule) DepsMutator(ctx BottomUpMutatorContext) {
+	m, ok := ctx.Module().(Module)
+	if !ok {
+		return
+	}
+	ctx.AddDependency(m, genruleDepTag{}, j.properties.Dep...)
+}
+
+func (p *mockGenruleModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 6a5b0da..f2ab0a4 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -105,7 +105,7 @@
 
 	if p := base.commonProperties.Effective_package_name; p != nil {
 		args = append(args,
-			"-p "+proptools.NinjaAndShellEscape(*p))
+			`-p `+proptools.NinjaAndShellEscapeIncludingSpaces(*p))
 	}
 
 	args = append(args,
diff --git a/android/licenses.go b/android/licenses.go
index 81c557e..c47b3e6 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -331,6 +331,8 @@
 func licensesMakeVarsProvider(ctx MakeVarsContext) {
 	ctx.Strict("BUILD_LICENSE_METADATA",
 		ctx.Config().HostToolPath(ctx, "build_license_metadata").String())
+	ctx.Strict("COPY_LICENSE_METADATA",
+		ctx.Config().HostToolPath(ctx, "copy_license_metadata").String())
 	ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
 	ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
 	ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
diff --git a/android/makevars.go b/android/makevars.go
index ece7091..a74185a 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -472,7 +472,8 @@
 			fmt.Fprintf(buf, "\tchmod +x $@\n")
 		}
 		if extraFiles := install.extraFiles; extraFiles != nil {
-			fmt.Fprintf(buf, "\tunzip -qDD -d '%s' '%s'\n", extraFiles.dir.String(), extraFiles.zip.String())
+			fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String())
+			fmt.Fprintf(buf, "\t  ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
 		}
 		fmt.Fprintln(buf)
 	}
diff --git a/android/module.go b/android/module.go
index 21d5a3d..2925081 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1177,37 +1177,103 @@
 
 	data := &attrs.Data
 
-	required := depsToLabelList(props.Required)
 	archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
 
 	var enabledProperty bazel.BoolAttribute
-	if props.Enabled != nil {
-		enabledProperty.Value = props.Enabled
+
+	onlyAndroid := false
+	neitherHostNorDevice := false
+
+	osSupport := map[string]bool{}
+
+	// if the target is enabled and supports arch variance, determine the defaults based on the module
+	// type's host or device property and host_supported/device_supported properties
+	if mod.commonProperties.ArchSpecific {
+		moduleSupportsDevice := mod.DeviceSupported()
+		moduleSupportsHost := mod.HostSupported()
+		if moduleSupportsHost && !moduleSupportsDevice {
+			// for host only, we specify as unsupported on android rather than listing all host osSupport
+			// TODO(b/220874839): consider replacing this with a constraint that covers all host osSupport
+			// instead
+			enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
+		} else if moduleSupportsDevice && !moduleSupportsHost {
+			enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(true))
+			// specify as a positive to ensure any target-specific enabled can be resolved
+			// also save that a target is only android, as if there is only the positive restriction on
+			// android, it'll be dropped, so we may need to add it back later
+			onlyAndroid = true
+		} else if !moduleSupportsHost && !moduleSupportsDevice {
+			neitherHostNorDevice = true
+		}
+
+		for _, os := range OsTypeList() {
+			if os.Class == Host {
+				osSupport[os.Name] = moduleSupportsHost
+			} else if os.Class == Device {
+				osSupport[os.Name] = moduleSupportsDevice
+			}
+		}
 	}
 
-	for axis, configToProps := range archVariantProps {
-		for config, _props := range configToProps {
-			if archProps, ok := _props.(*commonProperties); ok {
-				required.SetSelectValue(axis, config, depsToLabelList(archProps.Required).Value)
-				if archProps.Enabled != nil {
-					enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+	if neitherHostNorDevice {
+		// we can't build this, disable
+		enabledProperty.Value = proptools.BoolPtr(false)
+	} else if props.Enabled != nil {
+		enabledProperty.SetValue(props.Enabled)
+		if !*props.Enabled {
+			for os, enabled := range osSupport {
+				if val := enabledProperty.SelectValue(bazel.OsConfigurationAxis, os); enabled && val != nil && *val {
+					// if this should be disabled by default, clear out any enabling we've done
+					enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, os, nil)
 				}
 			}
 		}
 	}
 
-	if enabledPropertyOverrides.Value != nil {
-		enabledProperty.Value = enabledPropertyOverrides.Value
+	required := depsToLabelList(props.Required)
+	for axis, configToProps := range archVariantProps {
+		for config, _props := range configToProps {
+			if archProps, ok := _props.(*commonProperties); ok {
+				// TODO(b/234748998) Remove this requiredFiltered workaround when aapt2 converts successfully
+				requiredFiltered := archProps.Required
+				if name == "apexer" {
+					requiredFiltered = make([]string, 0, len(archProps.Required))
+					for _, req := range archProps.Required {
+						if req != "aapt2" && req != "apexer" {
+							requiredFiltered = append(requiredFiltered, req)
+						}
+					}
+				}
+				required.SetSelectValue(axis, config, depsToLabelList(requiredFiltered).Value)
+				if !neitherHostNorDevice {
+					if archProps.Enabled != nil {
+						if axis != bazel.OsConfigurationAxis || osSupport[config] {
+							enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+						}
+					}
+				}
+			}
+		}
 	}
-	for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
-		configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
-		for cfg, val := range configToBools {
-			enabledProperty.SetSelectValue(axis, cfg, &val)
+
+	if !neitherHostNorDevice {
+		if enabledPropertyOverrides.Value != nil {
+			enabledProperty.Value = enabledPropertyOverrides.Value
+		}
+		for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
+			configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
+			for cfg, val := range configToBools {
+				if axis != bazel.OsConfigurationAxis || osSupport[cfg] {
+					enabledProperty.SetSelectValue(axis, cfg, &val)
+				}
+			}
 		}
 	}
 
 	productConfigEnabledLabels := []bazel.Label{}
-	if !proptools.BoolDefault(enabledProperty.Value, true) {
+	// TODO(b/234497586): Soong config variables and product variables have different overriding behavior, we
+	// should handle it correctly
+	if !proptools.BoolDefault(enabledProperty.Value, true) && !neitherHostNorDevice {
 		// If the module is not enabled by default, then we can check if a
 		// product variable enables it
 		productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
@@ -1224,11 +1290,6 @@
 		productConfigEnabledLabels, nil,
 	})
 
-	moduleSupportsDevice := mod.commonProperties.HostOrDeviceSupported&deviceSupported == deviceSupported
-	if mod.commonProperties.HostOrDeviceSupported != NeitherHostNorDeviceSupported && !moduleSupportsDevice {
-		enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
-	}
-
 	platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute(
 		bazel.LabelList{[]bazel.Label{bazel.Label{Label: "@platforms//:incompatible"}}, nil},
 		bazel.LabelList{[]bazel.Label{}, nil})
@@ -1236,6 +1297,13 @@
 		ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
 	}
 
+	// if android is the only arch/os enabled, then add a restriction to only be compatible with android
+	if platformEnabledAttribute.IsNil() && onlyAndroid {
+		l := bazel.LabelAttribute{}
+		l.SetValue(bazel.Label{Label: bazel.OsConfigurationAxis.SelectKey(Android.Name)})
+		platformEnabledAttribute.Add(&l)
+	}
+
 	data.Append(required)
 
 	constraints := constraintAttributes{}
@@ -3224,8 +3292,9 @@
 
 			extraCmds := ""
 			if extraZip != nil {
-				extraCmds += fmt.Sprintf(" && unzip -qDD -d '%s' '%s'",
+				extraCmds += fmt.Sprintf(" && ( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} )",
 					extraZip.dir.String(), extraZip.zip.String())
+				extraCmds += " || ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )"
 				implicitDeps = append(implicitDeps, extraZip.zip)
 			}
 
diff --git a/android/mutator.go b/android/mutator.go
index f06ecda..9e4aa59 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -93,6 +93,7 @@
 	TopDown(name string, m TopDownMutator) MutatorHandle
 	BottomUp(name string, m BottomUpMutator) MutatorHandle
 	BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
+	Transition(name string, m TransitionMutator)
 }
 
 type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -421,6 +422,182 @@
 	return mutator
 }
 
+type IncomingTransitionContext interface {
+	// Module returns the target of the dependency edge for which the transition
+	// is being computed
+	Module() Module
+
+	// Config returns the configuration for the build.
+	Config() Config
+}
+
+type OutgoingTransitionContext interface {
+	// Module returns the target of the dependency edge for which the transition
+	// is being computed
+	Module() Module
+
+	// DepTag() Returns the dependency tag through which this dependency is
+	// reached
+	DepTag() blueprint.DependencyTag
+}
+
+// Transition mutators implement a top-down mechanism where a module tells its
+// direct dependencies what variation they should be built in but the dependency
+// has the final say.
+//
+// When implementing a transition mutator, one needs to implement four methods:
+//   - Split() that tells what variations a module has by itself
+//   - OutgoingTransition() where a module tells what it wants from its
+//     dependency
+//   - IncomingTransition() where a module has the final say about its own
+//     variation
+//   - Mutate() that changes the state of a module depending on its variation
+//
+// That the effective variation of module B when depended on by module A is the
+// composition the outgoing transition of module A and the incoming transition
+// of module B.
+//
+// the outgoing transition should not take the properties of the dependency into
+// account, only those of the module that depends on it. For this reason, the
+// dependency is not even passed into it as an argument. Likewise, the incoming
+// transition should not take the properties of the depending module into
+// account and is thus not informed about it. This makes for a nice
+// decomposition of the decision logic.
+//
+// A given transition mutator only affects its own variation; other variations
+// stay unchanged along the dependency edges.
+//
+// Soong makes sure that all modules are created in the desired variations and
+// that dependency edges are set up correctly. This ensures that "missing
+// variation" errors do not happen and allows for more flexible changes in the
+// value of the variation among dependency edges (as oppposed to bottom-up
+// mutators where if module A in variation X depends on module B and module B
+// has that variation X, A must depend on variation X of B)
+//
+// The limited power of the context objects passed to individual mutators
+// methods also makes it more difficult to shoot oneself in the foot. Complete
+// safety is not guaranteed because no one prevents individual transition
+// mutators from mutating modules in illegal ways and for e.g. Split() or
+// Mutate() to run their own visitations of the transitive dependency of the
+// module and both of these are bad ideas, but it's better than no guardrails at
+// all.
+//
+// This model is pretty close to Bazel's configuration transitions. The mapping
+// between concepts in Soong and Bazel is as follows:
+//   - Module == configured target
+//   - Variant == configuration
+//   - Variation name == configuration flag
+//   - Variation == configuration flag value
+//   - Outgoing transition == attribute transition
+//   - Incoming transition == rule transition
+//
+// The Split() method does not have a Bazel equivalent and Bazel split
+// transitions do not have a Soong equivalent.
+//
+// Mutate() does not make sense in Bazel due to the different models of the
+// two systems: when creating new variations, Soong clones the old module and
+// thus some way is needed to change it state whereas Bazel creates each
+// configuration of a given configured target anew.
+type TransitionMutator interface {
+	// Split returns the set of variations that should be created for a module no
+	// matter who depends on it. Used when Make depends on a particular variation
+	// or when the module knows its variations just based on information given to
+	// it in the Blueprint file. This method should not mutate the module it is
+	// called on.
+	Split(ctx BaseModuleContext) []string
+
+	// Called on a module to determine which variation it wants from its direct
+	// dependencies. The dependency itself can override this decision. This method
+	// should not mutate the module itself.
+	OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
+
+	// Called on a module to determine which variation it should be in based on
+	// the variation modules that depend on it want. This gives the module a final
+	// say about its own variations. This method should not mutate the module
+	// itself.
+	IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
+
+	// Called after a module was split into multiple variations on each variation.
+	// It should not split the module any further but adding new dependencies is
+	// fine. Unlike all the other methods on TransitionMutator, this method is
+	// allowed to mutate the module.
+	Mutate(ctx BottomUpMutatorContext, variation string)
+}
+
+type androidTransitionMutator struct {
+	finalPhase          bool
+	bazelConversionMode bool
+	mutator             TransitionMutator
+}
+
+func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
+	if m, ok := ctx.Module().(Module); ok {
+		moduleContext := m.base().baseModuleContextFactory(ctx)
+		moduleContext.bazelConversionMode = a.bazelConversionMode
+		return a.mutator.Split(&moduleContext)
+	} else {
+		return []string{""}
+	}
+}
+
+type outgoingTransitionContextImpl struct {
+	bp blueprint.OutgoingTransitionContext
+}
+
+func (c *outgoingTransitionContextImpl) Module() Module {
+	return c.bp.Module().(Module)
+}
+
+func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
+	return c.bp.DepTag()
+}
+
+func (a *androidTransitionMutator) OutgoingTransition(ctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
+	if _, ok := ctx.Module().(Module); ok {
+		return a.mutator.OutgoingTransition(&outgoingTransitionContextImpl{bp: ctx}, sourceVariation)
+	} else {
+		return ""
+	}
+}
+
+type incomingTransitionContextImpl struct {
+	bp blueprint.IncomingTransitionContext
+}
+
+func (c *incomingTransitionContextImpl) Module() Module {
+	return c.bp.Module().(Module)
+}
+
+func (c *incomingTransitionContextImpl) Config() Config {
+	return c.bp.Config().(Config)
+}
+
+func (a *androidTransitionMutator) IncomingTransition(ctx blueprint.IncomingTransitionContext, incomingVariation string) string {
+	if _, ok := ctx.Module().(Module); ok {
+		return a.mutator.IncomingTransition(&incomingTransitionContextImpl{bp: ctx}, incomingVariation)
+	} else {
+		return ""
+	}
+}
+
+func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
+	if am, ok := ctx.Module().(Module); ok {
+		a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase, a.bazelConversionMode), variation)
+	}
+}
+
+func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) {
+	atm := &androidTransitionMutator{
+		finalPhase:          x.finalPhase,
+		bazelConversionMode: x.bazelConversionMode,
+		mutator:             m,
+	}
+	mutator := &mutator{
+		name:              name,
+		transitionMutator: atm}
+	x.mutators = append(x.mutators, mutator)
+}
+
 func (x *registerMutatorsContext) mutatorName(name string) string {
 	if x.bazelConversionMode {
 		return name + "_bp2build"
@@ -456,6 +633,8 @@
 		handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator)
 	} else if mutator.topDownMutator != nil {
 		handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
+	} else if mutator.transitionMutator != nil {
+		blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
 	}
 	if mutator.parallel {
 		handle.Parallel()
diff --git a/android/notices.go b/android/notices.go
index 562a156..b9c1682 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -47,7 +47,9 @@
 }
 
 // buildNoticeOutputFromLicenseMetadata writes out a notice file.
-func buildNoticeOutputFromLicenseMetadata(ctx BuilderContext, tool, ruleName string, outputFile WritablePath, libraryName, stripPrefix string, modules ...Module) {
+func buildNoticeOutputFromLicenseMetadata(
+	ctx BuilderContext, tool, ruleName string, outputFile WritablePath,
+	libraryName string, stripPrefix []string, modules ...Module) {
 	depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
 	rule := NewRuleBuilder(pctx, ctx)
 	if len(modules) == 0 {
@@ -64,8 +66,8 @@
 		BuiltTool(tool).
 		FlagWithOutput("-o ", outputFile).
 		FlagWithDepFile("-d ", depsFile)
-	if stripPrefix != "" {
-		cmd = cmd.FlagWithArg("--strip_prefix ", stripPrefix)
+	if len(stripPrefix) > 0 {
+		cmd = cmd.FlagForEachArg("--strip_prefix ", stripPrefix)
 	}
 	outputs := modulesOutputDirs(ctx, modules...)
 	if len(outputs) > 0 {
@@ -81,20 +83,29 @@
 // BuildNoticeTextOutputFromLicenseMetadata writes out a notice text file based
 // on the license metadata files for the input `modules` defaulting to the
 // current context module if none given.
-func BuildNoticeTextOutputFromLicenseMetadata(ctx BuilderContext, outputFile WritablePath, ruleName, libraryName, stripPrefix string, modules ...Module) {
-	buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...)
+func BuildNoticeTextOutputFromLicenseMetadata(
+	ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
+	stripPrefix []string, modules ...Module) {
+	buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName,
+		outputFile, libraryName, stripPrefix, modules...)
 }
 
 // BuildNoticeHtmlOutputFromLicenseMetadata writes out a notice text file based
 // on the license metadata files for the input `modules` defaulting to the
 // current context module if none given.
-func BuildNoticeHtmlOutputFromLicenseMetadata(ctx BuilderContext, outputFile WritablePath, ruleName, libraryName, stripPrefix string, modules ...Module) {
-	buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...)
+func BuildNoticeHtmlOutputFromLicenseMetadata(
+	ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
+	stripPrefix []string, modules ...Module) {
+	buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName,
+		outputFile, libraryName, stripPrefix, modules...)
 }
 
 // BuildNoticeXmlOutputFromLicenseMetadata writes out a notice text file based
 // on the license metadata files for the input `modules` defaulting to the
 // current context module if none given.
-func BuildNoticeXmlOutputFromLicenseMetadata(ctx BuilderContext, outputFile WritablePath, ruleName, libraryName, stripPrefix string, modules ...Module) {
-	buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...)
+func BuildNoticeXmlOutputFromLicenseMetadata(
+	ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
+	stripPrefix []string, modules ...Module) {
+	buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName,
+		outputFile, libraryName, stripPrefix, modules...)
 }
diff --git a/android/paths.go b/android/paths.go
index e0e5ae5..d5cec9a 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1739,10 +1739,16 @@
 		partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
 	} else {
 		osName := os.String()
-		if os == Linux || os == LinuxMusl {
+		if os == Linux {
 			// instead of linux_glibc
 			osName = "linux"
 		}
+		if os == LinuxMusl && ctx.Config().UseHostMusl() {
+			// When using musl instead of glibc, use "linux" instead of "linux_musl".  When cross
+			// compiling we will still use "linux_musl".
+			osName = "linux"
+		}
+
 		// SOONG_HOST_OUT is set to out/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
 		// and HOST_PREBUILT_ARCH is forcibly set to x86 even on x86_64 hosts. We don't seem
 		// to have a plan to fix it (see the comment in build/make/core/envsetup.mk).
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 5843487..4e4fa42 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -110,6 +110,18 @@
 	return strings.TrimPrefix(name, "prebuilt_")
 }
 
+// RemoveOptionalPrebuiltPrefixFromBazelLabel removes the "prebuilt_" prefix from the *target name* of a Bazel label.
+// This differs from RemoveOptionalPrebuiltPrefix in that it does not remove it from the start of the string, but
+// instead removes it from the target name itself.
+func RemoveOptionalPrebuiltPrefixFromBazelLabel(label string) string {
+	splitLabel := strings.Split(label, ":")
+	bazelModuleNameNoPrebuilt := RemoveOptionalPrebuiltPrefix(splitLabel[1])
+	return strings.Join([]string{
+		splitLabel[0],
+		bazelModuleNameNoPrebuilt,
+	}, ":")
+}
+
 func (p *Prebuilt) Name(name string) string {
 	return PrebuiltNameFromSource(name)
 }
diff --git a/android/register.go b/android/register.go
index c505833..4ff8fff 100644
--- a/android/register.go
+++ b/android/register.go
@@ -96,10 +96,11 @@
 var preSingletons sortableComponents
 
 type mutator struct {
-	name            string
-	bottomUpMutator blueprint.BottomUpMutator
-	topDownMutator  blueprint.TopDownMutator
-	parallel        bool
+	name              string
+	bottomUpMutator   blueprint.BottomUpMutator
+	topDownMutator    blueprint.TopDownMutator
+	transitionMutator blueprint.TransitionMutator
+	parallel          bool
 }
 
 var _ sortableComponent = &mutator{}
diff --git a/android/sdk.go b/android/sdk.go
index 9317910..07b94b2 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -661,6 +661,10 @@
 	// an Android.bp file.
 	RequiresBpProperty() bool
 
+	// SupportedBuildReleases returns the string representation of a set of target build releases that
+	// support this member type.
+	SupportedBuildReleases() string
+
 	// UsableWithSdkAndSdkSnapshot returns true if the member type supports the sdk/sdk_snapshot,
 	// false otherwise.
 	UsableWithSdkAndSdkSnapshot() bool
@@ -670,6 +674,13 @@
 	// host OS variant explicitly and disable all other host OS'es.
 	IsHostOsDependent() bool
 
+	// SupportedLinkages returns the names of the linkage variants supported by this module.
+	SupportedLinkages() []string
+
+	// ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false
+	// otherwise.
+	ArePrebuiltsRequired() bool
+
 	// AddDependencies adds dependencies from the SDK module to all the module variants the member
 	// type contributes to the SDK. `names` is the list of module names given in the member type
 	// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
@@ -733,6 +744,9 @@
 
 	// SupportedTraits returns the set of traits supported by this member type.
 	SupportedTraits() SdkMemberTraitSet
+
+	// Overrides returns whether type overrides other SdkMemberType
+	Overrides(SdkMemberType) bool
 }
 
 var _ sdkRegisterable = (SdkMemberType)(nil)
@@ -756,11 +770,28 @@
 type SdkMemberTypeBase struct {
 	PropertyName string
 
+	// Property names that this SdkMemberTypeBase can override, this is useful when a module type is a
+	// superset of another module type.
+	OverridesPropertyNames map[string]bool
+
+	// The names of linkage variants supported by this module.
+	SupportedLinkageNames []string
+
 	// When set to true BpPropertyNotRequired indicates that the member type does not require the
 	// property to be specifiable in an Android.bp file.
 	BpPropertyNotRequired bool
 
-	SupportsSdk     bool
+	// The name of the first targeted build release.
+	//
+	// If not specified then it is assumed to be available on all targeted build releases.
+	SupportedBuildReleaseSpecification string
+
+	// Set to true if this must be usable with the sdk/sdk_snapshot module types. Otherwise, it will
+	// only be usable with module_exports/module_exports_snapshots module types.
+	SupportsSdk bool
+
+	// Set to true if prebuilt host artifacts of this member may be specific to the host OS. Only
+	// applicable to modules where HostSupported() is true.
 	HostOsDependent bool
 
 	// When set to true UseSourceModuleTypeInSnapshot indicates that the member type creates a source
@@ -768,6 +799,11 @@
 	// code from automatically adding a prefer: true flag.
 	UseSourceModuleTypeInSnapshot bool
 
+	// Set to proptools.BoolPtr(false) if this member does not generate prebuilts but is only provided
+	// to allow the sdk to gather members from this member's dependencies. If not specified then
+	// defaults to true.
+	PrebuiltsRequired *bool
+
 	// The list of supported traits.
 	Traits []SdkMemberTrait
 }
@@ -780,6 +816,10 @@
 	return !b.BpPropertyNotRequired
 }
 
+func (b *SdkMemberTypeBase) SupportedBuildReleases() string {
+	return b.SupportedBuildReleaseSpecification
+}
+
 func (b *SdkMemberTypeBase) UsableWithSdkAndSdkSnapshot() bool {
 	return b.SupportsSdk
 }
@@ -788,6 +828,10 @@
 	return b.HostOsDependent
 }
 
+func (b *SdkMemberTypeBase) ArePrebuiltsRequired() bool {
+	return proptools.BoolDefault(b.PrebuiltsRequired, true)
+}
+
 func (b *SdkMemberTypeBase) UsesSourceModuleTypeInSnapshot() bool {
 	return b.UseSourceModuleTypeInSnapshot
 }
@@ -796,6 +840,14 @@
 	return NewSdkMemberTraitSet(b.Traits)
 }
 
+func (b *SdkMemberTypeBase) Overrides(other SdkMemberType) bool {
+	return b.OverridesPropertyNames[other.SdkPropertyName()]
+}
+
+func (b *SdkMemberTypeBase) SupportedLinkages() []string {
+	return b.SupportedLinkageNames
+}
+
 // registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
 // modules.
 var registeredModuleExportsMemberTypes = &sdkRegistry{}
diff --git a/android/util.go b/android/util.go
index 47c4583..a0f7160 100644
--- a/android/util.go
+++ b/android/util.go
@@ -32,6 +32,12 @@
 // JoinWithPrefix prepends the prefix to each string in the list and
 // returns them joined together with " " as separator.
 func JoinWithPrefix(strs []string, prefix string) string {
+	return JoinWithPrefixAndSeparator(strs, prefix, " ")
+}
+
+// JoinWithPrefixAndSeparator prepends the prefix to each string in the list and
+// returns them joined together with the given separator.
+func JoinWithPrefixAndSeparator(strs []string, prefix string, sep string) string {
 	if len(strs) == 0 {
 		return ""
 	}
@@ -40,7 +46,7 @@
 	buf.WriteString(prefix)
 	buf.WriteString(strs[0])
 	for i := 1; i < len(strs); i++ {
-		buf.WriteString(" ")
+		buf.WriteString(sep)
 		buf.WriteString(prefix)
 		buf.WriteString(strs[i])
 	}
diff --git a/android/variable.go b/android/variable.go
index 9478c0c..50fc304 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -200,6 +200,7 @@
 	Platform_min_supported_target_sdk_version *string  `json:",omitempty"`
 	Platform_base_os                          *string  `json:",omitempty"`
 	Platform_version_last_stable              *string  `json:",omitempty"`
+	Platform_version_known_codenames          *string  `json:",omitempty"`
 
 	DeviceName                            *string  `json:",omitempty"`
 	DeviceProduct                         *string  `json:",omitempty"`
@@ -428,6 +429,7 @@
 
 	ShippingApiLevel *string `json:",omitempty"`
 
+	BuildBrokenDepfile                 *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
 	BuildBrokenVendorPropertyNamespace bool     `json:",omitempty"`
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 9519be0..280dae8 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -124,7 +124,12 @@
 	s.CopySpecsToDir(ctx, builder, packageSpecs, dir)
 
 	noticeFile := android.PathForModuleOut(ctx, "NOTICES.txt")
-	android.BuildNoticeTextOutputFromLicenseMetadata(ctx, noticeFile, "", "", outputZipFile.String())
+	android.BuildNoticeTextOutputFromLicenseMetadata(
+		ctx, noticeFile, "", "",
+		[]string{
+			android.PathForModuleInstall(ctx, "sdk-repo").String() + "/",
+			outputZipFile.String(),
+		})
 	builder.Command().Text("cp").
 		Input(noticeFile).
 		Text(filepath.Join(dir.String(), "NOTICE.txt"))
diff --git a/apex/Android.bp b/apex/Android.bp
index d3417c2..6533c61 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -23,8 +23,10 @@
     srcs: [
         "androidmk.go",
         "apex.go",
+        "apex_sdk_member.go",
         "apex_singleton.go",
         "builder.go",
+        "constants.go",
         "deapexer.go",
         "key.go",
         "prebuilt.go",
diff --git a/apex/androidmk.go b/apex/androidmk.go
index e094a12..938c8ed 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -412,6 +412,7 @@
 					fmt.Fprintln(w, ".PHONY:", goal)
 					fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
 						goal, a.installedFilesFile.String(), distFile)
+					fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String())
 				}
 				for _, dist := range data.Entries.GetDistForGoals(a) {
 					fmt.Fprintf(w, dist)
diff --git a/apex/apex.go b/apex/apex.go
index 21725ff..7b6707c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -606,6 +606,18 @@
 	// replacement. This is needed because some prebuilt modules do not provide all the information
 	// needed by the apex.
 	sourceOnly bool
+
+	// If not-nil and an APEX is a member of an SDK then dependencies of that APEX with this tag will
+	// also be added as exported members of that SDK.
+	memberType android.SdkMemberType
+}
+
+func (d *dependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType {
+	return d.memberType
+}
+
+func (d *dependencyTag) ExportMember() bool {
+	return true
 }
 
 func (d *dependencyTag) String() string {
@@ -617,6 +629,7 @@
 }
 
 var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
+var _ android.SdkMemberDependencyTag = &dependencyTag{}
 
 var (
 	androidAppTag   = &dependencyTag{name: "androidApp", payload: true}
@@ -624,8 +637,8 @@
 	certificateTag  = &dependencyTag{name: "certificate"}
 	executableTag   = &dependencyTag{name: "executable", payload: true}
 	fsTag           = &dependencyTag{name: "filesystem", payload: true}
-	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true}
-	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true}
+	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
+	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType}
 	compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
 	javaLibTag      = &dependencyTag{name: "javaLib", payload: true}
 	jniLibTag       = &dependencyTag{name: "jniLib", payload: true}
@@ -1338,7 +1351,7 @@
 var _ android.DepIsInSameApex = (*apexBundle)(nil)
 
 // Implements android.DepInInSameApex
-func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
 	// direct deps of an APEX bundle are all part of the APEX bundle
 	// TODO(jiyong): shouldn't we look into the payload field of the dependencyTag?
 	return true
@@ -1468,19 +1481,19 @@
 	}
 }
 
-func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
+func (a *apexBundle) IsSanitizerEnabled(config android.Config, sanitizerName string) bool {
 	if android.InList(sanitizerName, a.properties.SanitizerNames) {
 		return true
 	}
 
 	// Then follow the global setting
-	globalSanitizerNames := []string{}
+	var globalSanitizerNames []string
 	if a.Host() {
-		globalSanitizerNames = ctx.Config().SanitizeHost()
+		globalSanitizerNames = config.SanitizeHost()
 	} else {
-		arches := ctx.Config().SanitizeDeviceArch()
+		arches := config.SanitizeDeviceArch()
 		if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
-			globalSanitizerNames = ctx.Config().SanitizeDevice()
+			globalSanitizerNames = config.SanitizeDevice()
 		}
 	}
 	return android.InList(sanitizerName, globalSanitizerNames)
@@ -1790,6 +1803,382 @@
 	}
 }
 
+type visitorContext struct {
+	// all the files that will be included in this APEX
+	filesInfo []apexFile
+
+	// native lib dependencies
+	provideNativeLibs []string
+	requireNativeLibs []string
+
+	handleSpecialLibs bool
+}
+
+func (vctx *visitorContext) normalizeFileInfo() {
+	encountered := make(map[string]apexFile)
+	for _, f := range vctx.filesInfo {
+		dest := filepath.Join(f.installDir, f.builtFile.Base())
+		if e, ok := encountered[dest]; !ok {
+			encountered[dest] = f
+		} else {
+			// If a module is directly included and also transitively depended on
+			// consider it as directly included.
+			e.transitiveDep = e.transitiveDep && f.transitiveDep
+			encountered[dest] = e
+		}
+	}
+	vctx.filesInfo = vctx.filesInfo[:0]
+	for _, v := range encountered {
+		vctx.filesInfo = append(vctx.filesInfo, v)
+	}
+	sort.Slice(vctx.filesInfo, func(i, j int) bool {
+		// Sort by destination path so as to ensure consistent ordering even if the source of the files
+		// changes.
+		return vctx.filesInfo[i].path() < vctx.filesInfo[j].path()
+	})
+}
+
+func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent blueprint.Module) bool {
+	depTag := ctx.OtherModuleDependencyTag(child)
+	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+		return false
+	}
+	if mod, ok := child.(android.Module); ok && !mod.Enabled() {
+		return false
+	}
+	depName := ctx.OtherModuleName(child)
+	if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+		switch depTag {
+		case sharedLibTag, jniLibTag:
+			isJniLib := depTag == jniLibTag
+			switch ch := child.(type) {
+			case *cc.Module:
+				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+				fi.isJniLib = isJniLib
+				vctx.filesInfo = append(vctx.filesInfo, fi)
+				// Collect the list of stub-providing libs except:
+				// - VNDK libs are only for vendors
+				// - bootstrap bionic libs are treated as provided by system
+				if ch.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(ch.BaseModuleName(), ctx.Config()) {
+					vctx.provideNativeLibs = append(vctx.provideNativeLibs, fi.stem())
+				}
+				return true // track transitive dependencies
+			case *rust.Module:
+				fi := apexFileForRustLibrary(ctx, ch)
+				fi.isJniLib = isJniLib
+				vctx.filesInfo = append(vctx.filesInfo, fi)
+				return true // track transitive dependencies
+			default:
+				propertyName := "native_shared_libs"
+				if isJniLib {
+					propertyName = "jni_libs"
+				}
+				ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
+			}
+		case executableTag:
+			switch ch := child.(type) {
+			case *cc.Module:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
+				return true // track transitive dependencies
+			case *python.Module:
+				if ch.HostToolPath().Valid() {
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
+				}
+			case bootstrap.GoBinaryTool:
+				if a.Host() {
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForGoBinary(ctx, depName, ch))
+				}
+			case *rust.Module:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
+				return true // track transitive dependencies
+			default:
+				ctx.PropertyErrorf("binaries",
+					"%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
+			}
+		case shBinaryTag:
+			if csh, ok := child.(*sh.ShBinary); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, csh))
+			} else {
+				ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
+			}
+		case bcpfTag:
+			bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
+			if !ok {
+				ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
+				return false
+			}
+
+			vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
+			for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
+				a.requiredDeps = append(a.requiredDeps, makeModuleName)
+			}
+			return true
+		case sscpfTag:
+			if _, ok := child.(*java.SystemServerClasspathModule); !ok {
+				ctx.PropertyErrorf("systemserverclasspath_fragments",
+					"%q is not a systemserverclasspath_fragment module", depName)
+				return false
+			}
+			if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+				vctx.filesInfo = append(vctx.filesInfo, *af)
+			}
+			return true
+		case javaLibTag:
+			switch child.(type) {
+			case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
+				af := apexFileForJavaModule(ctx, child.(javaModule))
+				if !af.ok() {
+					ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+					return false
+				}
+				vctx.filesInfo = append(vctx.filesInfo, af)
+				return true // track transitive dependencies
+			default:
+				ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
+			}
+		case androidAppTag:
+			switch ap := child.(type) {
+			case *java.AndroidApp:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				return true // track transitive dependencies
+			case *java.AndroidAppImport:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+			case *java.AndroidTestHelperApp:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+			case *java.AndroidAppSet:
+				appDir := "app"
+				if ap.Privileged() {
+					appDir = "priv-app"
+				}
+				// TODO(b/224589412, b/226559955): Ensure that the dirname is
+				// suffixed so that PackageManager correctly invalidates the
+				// existing installed apk in favour of the new APK-in-APEX.
+				// See bugs for more information.
+				appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
+				af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
+				af.certificate = java.PresignedCertificate
+				vctx.filesInfo = append(vctx.filesInfo, af)
+			default:
+				ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
+			}
+		case rroTag:
+			if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+			} else {
+				ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
+			}
+		case bpfTag:
+			if bpfProgram, ok := child.(bpf.BpfModule); ok {
+				filesToCopy, _ := bpfProgram.OutputFiles("")
+				apex_sub_dir := bpfProgram.SubDir()
+				for _, bpfFile := range filesToCopy {
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
+				}
+			} else {
+				ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
+			}
+		case fsTag:
+			if fs, ok := child.(filesystem.Filesystem); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+			} else {
+				ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
+			}
+		case prebuiltTag:
+			if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+			} else {
+				ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
+			}
+		case compatConfigTag:
+			if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
+			} else {
+				ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
+			}
+		case testTag:
+			if ccTest, ok := child.(*cc.Module); ok {
+				if ccTest.IsTestPerSrcAllTestsVariation() {
+					// Multiple-output test module (where `test_per_src: true`).
+					//
+					// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+					// We do not add this variation to `filesInfo`, as it has no output;
+					// however, we do add the other variations of this module as indirect
+					// dependencies (see below).
+				} else {
+					// Single-output test module (where `test_per_src: false`).
+					af := apexFileForExecutable(ctx, ccTest)
+					af.class = nativeTest
+					vctx.filesInfo = append(vctx.filesInfo, af)
+				}
+				return true // track transitive dependencies
+			} else {
+				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+			}
+		case keyTag:
+			if key, ok := child.(*apexKey); ok {
+				a.privateKeyFile = key.privateKeyFile
+				a.publicKeyFile = key.publicKeyFile
+			} else {
+				ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+			}
+		case certificateTag:
+			if dep, ok := child.(*java.AndroidAppCertificate); ok {
+				a.containerCertificateFile = dep.Certificate.Pem
+				a.containerPrivateKeyFile = dep.Certificate.Key
+			} else {
+				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+			}
+		case android.PrebuiltDepTag:
+			// If the prebuilt is force disabled, remember to delete the prebuilt file
+			// that might have been installed in the previous builds
+			if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
+				a.prebuiltFileToDelete = prebuilt.InstallFilename()
+			}
+		}
+		return false
+	}
+
+	if a.vndkApex {
+		return false
+	}
+
+	// indirect dependencies
+	am, ok := child.(android.ApexModule)
+	if !ok {
+		return false
+	}
+	// We cannot use a switch statement on `depTag` here as the checked
+	// tags used below are private (e.g. `cc.sharedDepTag`).
+	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+		if ch, ok := child.(*cc.Module); ok {
+			if ch.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && ch.IsVndk() {
+				vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk")
+				return false
+			}
+			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+			af.transitiveDep = true
+
+			// Always track transitive dependencies for host.
+			if a.Host() {
+				vctx.filesInfo = append(vctx.filesInfo, af)
+				return true
+			}
+
+			abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+			if !abInfo.Contents.DirectlyInApex(depName) && (ch.IsStubs() || ch.HasStubsVariants()) {
+				// If the dependency is a stubs lib, don't include it in this APEX,
+				// but make sure that the lib is installed on the device.
+				// In case no APEX is having the lib, the lib is installed to the system
+				// partition.
+				//
+				// Always include if we are a host-apex however since those won't have any
+				// system libraries.
+				if !am.DirectlyInAnyApex() {
+					// we need a module name for Make
+					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
+					if !android.InList(name, a.requiredDeps) {
+						a.requiredDeps = append(a.requiredDeps, name)
+					}
+				}
+				vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
+				// Don't track further
+				return false
+			}
+
+			// If the dep is not considered to be in the same
+			// apex, don't add it to filesInfo so that it is not
+			// included in this APEX.
+			// TODO(jiyong): move this to at the top of the
+			// else-if clause for the indirect dependencies.
+			// Currently, that's impossible because we would
+			// like to record requiredNativeLibs even when
+			// DepIsInSameAPex is false. We also shouldn't do
+			// this for host.
+			//
+			// TODO(jiyong): explain why the same module is passed in twice.
+			// Switching the first am to parent breaks lots of tests.
+			if !android.IsDepInSameApex(ctx, am, am) {
+				return false
+			}
+
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		} else if rm, ok := child.(*rust.Module); ok {
+			af := apexFileForRustLibrary(ctx, rm)
+			af.transitiveDep = true
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		}
+	} else if cc.IsTestPerSrcDepTag(depTag) {
+		if ch, ok := child.(*cc.Module); ok {
+			af := apexFileForExecutable(ctx, ch)
+			// Handle modules created as `test_per_src` variations of a single test module:
+			// use the name of the generated test binary (`fileToCopy`) instead of the name
+			// of the original test module (`depName`, shared by all `test_per_src`
+			// variations of that module).
+			af.androidMkModuleName = filepath.Base(af.builtFile.String())
+			// these are not considered transitive dep
+			af.transitiveDep = false
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		}
+	} else if cc.IsHeaderDepTag(depTag) {
+		// nothing
+	} else if java.IsJniDepTag(depTag) {
+		// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
+	} else if java.IsXmlPermissionsFileDepTag(depTag) {
+		if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+			vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+		}
+	} else if rust.IsDylibDepTag(depTag) {
+		if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
+			af := apexFileForRustLibrary(ctx, rustm)
+			af.transitiveDep = true
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		}
+	} else if rust.IsRlibDepTag(depTag) {
+		// Rlib is statically linked, but it might have shared lib
+		// dependencies. Track them.
+		return true
+	} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
+		// Add the contents of the bootclasspath fragment to the apex.
+		switch child.(type) {
+		case *java.Library, *java.SdkLibrary:
+			javaModule := child.(javaModule)
+			af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
+			if !af.ok() {
+				ctx.PropertyErrorf("bootclasspath_fragments",
+					"bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
+				return false
+			}
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		default:
+			ctx.PropertyErrorf("bootclasspath_fragments",
+				"bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+		}
+	} else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
+		// Add the contents of the systemserverclasspath fragment to the apex.
+		switch child.(type) {
+		case *java.Library, *java.SdkLibrary:
+			af := apexFileForJavaModule(ctx, child.(javaModule))
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		default:
+			ctx.PropertyErrorf("systemserverclasspath_fragments",
+				"systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+		}
+	} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
+		// nothing
+	} else if depTag == android.DarwinUniversalVariantTag {
+		// nothing
+	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+	}
+	return false
+}
+
 // Creates build rules for an APEX. It consists of the following major steps:
 //
 // 1) do some validity checks such as apex_available, min_sdk_version, etc.
@@ -1812,386 +2201,23 @@
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 2) traverse the dependency tree to collect apexFile structs from them.
 
-	// all the files that will be included in this APEX
-	var filesInfo []apexFile
-
-	// native lib dependencies
-	var provideNativeLibs []string
-	var requireNativeLibs []string
-
-	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
-
 	// Collect the module directory for IDE info in java/jdeps.go.
 	a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
 
 	// TODO(jiyong): do this using WalkPayloadDeps
 	// TODO(jiyong): make this clean!!!
-	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
-		depTag := ctx.OtherModuleDependencyTag(child)
-		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
-			return false
-		}
-		if mod, ok := child.(android.Module); ok && !mod.Enabled() {
-			return false
-		}
-		depName := ctx.OtherModuleName(child)
-		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
-			switch depTag {
-			case sharedLibTag, jniLibTag:
-				isJniLib := depTag == jniLibTag
-				if c, ok := child.(*cc.Module); ok {
-					fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
-					fi.isJniLib = isJniLib
-					filesInfo = append(filesInfo, fi)
-					// Collect the list of stub-providing libs except:
-					// - VNDK libs are only for vendors
-					// - bootstrap bionic libs are treated as provided by system
-					if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
-						provideNativeLibs = append(provideNativeLibs, fi.stem())
-					}
-					return true // track transitive dependencies
-				} else if r, ok := child.(*rust.Module); ok {
-					fi := apexFileForRustLibrary(ctx, r)
-					fi.isJniLib = isJniLib
-					filesInfo = append(filesInfo, fi)
-					return true // track transitive dependencies
-				} else {
-					propertyName := "native_shared_libs"
-					if isJniLib {
-						propertyName = "jni_libs"
-					}
-					ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
-				}
-			case executableTag:
-				if cc, ok := child.(*cc.Module); ok {
-					filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
-					return true // track transitive dependencies
-				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
-					filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
-				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
-					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
-				} else if rust, ok := child.(*rust.Module); ok {
-					filesInfo = append(filesInfo, apexFileForRustExecutable(ctx, rust))
-					return true // track transitive dependencies
-				} else {
-					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
-				}
-			case shBinaryTag:
-				if sh, ok := child.(*sh.ShBinary); ok {
-					filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
-				} else {
-					ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
-				}
-			case bcpfTag:
-				{
-					bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
-					if !ok {
-						ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
-						return false
-					}
-
-					filesToAdd := apexBootclasspathFragmentFiles(ctx, child)
-					filesInfo = append(filesInfo, filesToAdd...)
-					for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
-						a.requiredDeps = append(a.requiredDeps, makeModuleName)
-					}
-					return true
-				}
-			case sscpfTag:
-				{
-					if _, ok := child.(*java.SystemServerClasspathModule); !ok {
-						ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
-						return false
-					}
-					if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
-						filesInfo = append(filesInfo, *af)
-					}
-					return true
-				}
-			case javaLibTag:
-				switch child.(type) {
-				case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
-					af := apexFileForJavaModule(ctx, child.(javaModule))
-					if !af.ok() {
-						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
-						return false
-					}
-					filesInfo = append(filesInfo, af)
-					return true // track transitive dependencies
-				default:
-					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
-				}
-			case androidAppTag:
-				if ap, ok := child.(*java.AndroidApp); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
-					return true // track transitive dependencies
-				} else if ap, ok := child.(*java.AndroidAppImport); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
-				} else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
-				} else if ap, ok := child.(*java.AndroidAppSet); ok {
-					appDir := "app"
-					if ap.Privileged() {
-						appDir = "priv-app"
-					}
-					// TODO(b/224589412, b/226559955): Ensure that the dirname is
-					// suffixed so that PackageManager correctly invalidates the
-					// existing installed apk in favour of the new APK-in-APEX.
-					// See bugs for more information.
-					appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
-					af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
-					af.certificate = java.PresignedCertificate
-					filesInfo = append(filesInfo, af)
-				} else {
-					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
-				}
-			case rroTag:
-				if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
-					filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
-				} else {
-					ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
-				}
-			case bpfTag:
-				if bpfProgram, ok := child.(bpf.BpfModule); ok {
-					filesToCopy, _ := bpfProgram.OutputFiles("")
-					apex_sub_dir := bpfProgram.SubDir()
-					for _, bpfFile := range filesToCopy {
-						filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
-					}
-				} else {
-					ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
-				}
-			case fsTag:
-				if fs, ok := child.(filesystem.Filesystem); ok {
-					filesInfo = append(filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
-				} else {
-					ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
-				}
-			case prebuiltTag:
-				if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
-				} else {
-					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
-				}
-			case compatConfigTag:
-				if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
-					filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
-				} else {
-					ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
-				}
-			case testTag:
-				if ccTest, ok := child.(*cc.Module); ok {
-					if ccTest.IsTestPerSrcAllTestsVariation() {
-						// Multiple-output test module (where `test_per_src: true`).
-						//
-						// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
-						// We do not add this variation to `filesInfo`, as it has no output;
-						// however, we do add the other variations of this module as indirect
-						// dependencies (see below).
-					} else {
-						// Single-output test module (where `test_per_src: false`).
-						af := apexFileForExecutable(ctx, ccTest)
-						af.class = nativeTest
-						filesInfo = append(filesInfo, af)
-					}
-					return true // track transitive dependencies
-				} else {
-					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
-				}
-			case keyTag:
-				if key, ok := child.(*apexKey); ok {
-					a.privateKeyFile = key.privateKeyFile
-					a.publicKeyFile = key.publicKeyFile
-				} else {
-					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
-				}
-				return false
-			case certificateTag:
-				if dep, ok := child.(*java.AndroidAppCertificate); ok {
-					a.containerCertificateFile = dep.Certificate.Pem
-					a.containerPrivateKeyFile = dep.Certificate.Key
-				} else {
-					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
-				}
-			case android.PrebuiltDepTag:
-				// If the prebuilt is force disabled, remember to delete the prebuilt file
-				// that might have been installed in the previous builds
-				if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
-					a.prebuiltFileToDelete = prebuilt.InstallFilename()
-				}
-			}
-		} else if !a.vndkApex {
-			// indirect dependencies
-			if am, ok := child.(android.ApexModule); ok {
-				// We cannot use a switch statement on `depTag` here as the checked
-				// tags used below are private (e.g. `cc.sharedDepTag`).
-				if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
-					if cc, ok := child.(*cc.Module); ok {
-						if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
-							requireNativeLibs = append(requireNativeLibs, ":vndk")
-							return false
-						}
-						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
-						af.transitiveDep = true
-
-						// Always track transitive dependencies for host.
-						if a.Host() {
-							filesInfo = append(filesInfo, af)
-							return true
-						}
-
-						abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
-						if !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
-							// If the dependency is a stubs lib, don't include it in this APEX,
-							// but make sure that the lib is installed on the device.
-							// In case no APEX is having the lib, the lib is installed to the system
-							// partition.
-							//
-							// Always include if we are a host-apex however since those won't have any
-							// system libraries.
-							if !am.DirectlyInAnyApex() {
-								// we need a module name for Make
-								name := cc.ImplementationModuleNameForMake(ctx) + cc.Properties.SubName
-								if !android.InList(name, a.requiredDeps) {
-									a.requiredDeps = append(a.requiredDeps, name)
-								}
-							}
-							requireNativeLibs = append(requireNativeLibs, af.stem())
-							// Don't track further
-							return false
-						}
-
-						// If the dep is not considered to be in the same
-						// apex, don't add it to filesInfo so that it is not
-						// included in this APEX.
-						// TODO(jiyong): move this to at the top of the
-						// else-if clause for the indirect dependencies.
-						// Currently, that's impossible because we would
-						// like to record requiredNativeLibs even when
-						// DepIsInSameAPex is false. We also shouldn't do
-						// this for host.
-						//
-						// TODO(jiyong): explain why the same module is passed in twice.
-						// Switching the first am to parent breaks lots of tests.
-						if !android.IsDepInSameApex(ctx, am, am) {
-							return false
-						}
-
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					} else if rm, ok := child.(*rust.Module); ok {
-						af := apexFileForRustLibrary(ctx, rm)
-						af.transitiveDep = true
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if cc.IsTestPerSrcDepTag(depTag) {
-					if cc, ok := child.(*cc.Module); ok {
-						af := apexFileForExecutable(ctx, cc)
-						// Handle modules created as `test_per_src` variations of a single test module:
-						// use the name of the generated test binary (`fileToCopy`) instead of the name
-						// of the original test module (`depName`, shared by all `test_per_src`
-						// variations of that module).
-						af.androidMkModuleName = filepath.Base(af.builtFile.String())
-						// these are not considered transitive dep
-						af.transitiveDep = false
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if cc.IsHeaderDepTag(depTag) {
-					// nothing
-				} else if java.IsJniDepTag(depTag) {
-					// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
-					return false
-				} else if java.IsXmlPermissionsFileDepTag(depTag) {
-					if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
-					}
-				} else if rust.IsDylibDepTag(depTag) {
-					if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
-						af := apexFileForRustLibrary(ctx, rustm)
-						af.transitiveDep = true
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if rust.IsRlibDepTag(depTag) {
-					// Rlib is statically linked, but it might have shared lib
-					// dependencies. Track them.
-					return true
-				} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
-					// Add the contents of the bootclasspath fragment to the apex.
-					switch child.(type) {
-					case *java.Library, *java.SdkLibrary:
-						javaModule := child.(javaModule)
-						af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
-						if !af.ok() {
-							ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
-							return false
-						}
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					default:
-						ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
-					}
-				} else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
-					// Add the contents of the systemserverclasspath fragment to the apex.
-					switch child.(type) {
-					case *java.Library, *java.SdkLibrary:
-						af := apexFileForJavaModule(ctx, child.(javaModule))
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					default:
-						ctx.PropertyErrorf("systemserverclasspath_fragments", "systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
-					}
-				} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
-					// nothing
-				} else if depTag == android.DarwinUniversalVariantTag {
-					// nothing
-				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
-					ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
-				}
-			}
-		}
-		return false
-	})
+	vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)}
+	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
+	vctx.normalizeFileInfo()
 	if a.privateKeyFile == nil {
 		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
 		return
 	}
 
-	// Remove duplicates in filesInfo
-	removeDup := func(filesInfo []apexFile) []apexFile {
-		encountered := make(map[string]apexFile)
-		for _, f := range filesInfo {
-			dest := filepath.Join(f.installDir, f.builtFile.Base())
-			if e, ok := encountered[dest]; !ok {
-				encountered[dest] = f
-			} else {
-				// If a module is directly included and also transitively depended on
-				// consider it as directly included.
-				e.transitiveDep = e.transitiveDep && f.transitiveDep
-				encountered[dest] = e
-			}
-		}
-		var result []apexFile
-		for _, v := range encountered {
-			result = append(result, v)
-		}
-		return result
-	}
-	filesInfo = removeDup(filesInfo)
-
-	// Sort to have consistent build rules
-	sort.Slice(filesInfo, func(i, j int) bool {
-		// Sort by destination path so as to ensure consistent ordering even if the source of the files
-		// changes.
-		return filesInfo[i].path() < filesInfo[j].path()
-	})
-
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 3) some fields in apexBundle struct are configured
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
-	a.filesInfo = filesInfo
+	a.filesInfo = vctx.filesInfo
 
 	// Set suffix and primaryApexType depending on the ApexType
 	buildFlattenedAsDefault := ctx.Config().FlattenApex()
@@ -2267,7 +2293,7 @@
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
-	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+	a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
 	if a.properties.ApexType == flattenedApex {
 		a.buildFlattenedApex(ctx)
 	} else {
@@ -2458,7 +2484,7 @@
 	android.BazelModuleBase
 }
 
-func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 }
 
@@ -2523,13 +2549,36 @@
 		}
 
 		// Prebuilts
-		prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, overridableProperties.Prebuilts)
-		attrs.Prebuilts = bazel.MakeLabelListAttribute(prebuiltsLabelList)
+		if overridableProperties.Prebuilts != nil {
+			prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, overridableProperties.Prebuilts)
+			attrs.Prebuilts = bazel.MakeLabelListAttribute(prebuiltsLabelList)
+		}
 
 		// Compressible
 		if overridableProperties.Compressible != nil {
 			attrs.Compressible = bazel.BoolAttribute{Value: overridableProperties.Compressible}
 		}
+
+		// Package name
+		//
+		// e.g. com.android.adbd's package name is com.android.adbd, but
+		// com.google.android.adbd overrides the package name to com.google.android.adbd
+		//
+		// TODO: this can be overridden from the product configuration, see
+		// getOverrideManifestPackageName and
+		// PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES.
+		//
+		// Instead of generating the BUILD files differently based on the product config
+		// at the point of conversion, this should be handled by the BUILD file loading
+		// from the soong_injection's product_vars, so product config is decoupled from bp2build.
+		if overridableProperties.Package_name != "" {
+			attrs.Package_name = &overridableProperties.Package_name
+		}
+
+		// Logging parent
+		if overridableProperties.Logging_parent != "" {
+			attrs.Logging_parent = &overridableProperties.Logging_parent
+		}
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: o.Name()}, &attrs)
@@ -3471,6 +3520,8 @@
 	Native_shared_libs_32 bazel.LabelListAttribute
 	Native_shared_libs_64 bazel.LabelListAttribute
 	Compressible          bazel.BoolAttribute
+	Package_name          *string
+	Logging_parent        *string
 }
 
 type convertedNativeSharedLibs struct {
@@ -3565,6 +3616,16 @@
 		compressibleAttribute.Value = a.overridableProperties.Compressible
 	}
 
+	var packageName *string
+	if a.overridableProperties.Package_name != "" {
+		packageName = &a.overridableProperties.Package_name
+	}
+
+	var loggingParent *string
+	if a.overridableProperties.Logging_parent != "" {
+		loggingParent = &a.overridableProperties.Logging_parent
+	}
+
 	attrs := bazelApexBundleAttributes{
 		Manifest:              manifestLabelAttribute,
 		Android_manifest:      androidManifestLabelAttribute,
@@ -3579,6 +3640,8 @@
 		Binaries:              binariesLabelListAttribute,
 		Prebuilts:             prebuiltsLabelListAttribute,
 		Compressible:          compressibleAttribute,
+		Package_name:          packageName,
+		Logging_parent:        loggingParent,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/apex/apex_sdk_member.go b/apex/apex_sdk_member.go
new file mode 100644
index 0000000..284158f
--- /dev/null
+++ b/apex/apex_sdk_member.go
@@ -0,0 +1,58 @@
+// 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.
+
+package apex
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+// This file contains support for using apex modules within an sdk.
+
+func init() {
+	// Register sdk member types.
+	android.RegisterSdkMemberType(&apexSdkMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "apexes",
+			SupportsSdk:  true,
+
+			// The apexes property does not need to be included in the snapshot as adding an apex to an
+			// sdk does not produce any prebuilts of the apex.
+			PrebuiltsRequired: proptools.BoolPtr(false),
+		},
+	})
+}
+
+type apexSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *apexSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *apexSdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*apexBundle)
+	return ok
+}
+
+func (mt *apexSdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	panic("Sdk does not create prebuilts of the apexes in its snapshot")
+}
+
+func (mt *apexSdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	panic("Sdk does not create prebuilts of the apexes in its snapshot")
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 07372a3..dbe9180 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6227,6 +6227,9 @@
 			name: "mybootclasspath_fragment",
 			contents: ["bcplib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -6241,6 +6244,9 @@
 			name: "override_bootclasspath_fragment",
 			contents: ["override_bcplib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -7294,6 +7300,9 @@
 			apex_available: [
 				"some-non-updatable-apex",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -7352,6 +7361,9 @@
 			apex_available: [
 				"com.android.art.debug",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex_key {
@@ -7428,7 +7440,7 @@
 	return result.TestContext
 }
 
-func TestDuplicateDeapexeresFromPrebuiltApexes(t *testing.T) {
+func TestDuplicateDeapexersFromPrebuiltApexes(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
 		java.PrepareForTestWithJavaDefaultModules,
 		PrepareForTestWithApexBuildComponents,
@@ -7497,6 +7509,107 @@
 	})
 }
 
+func TestDuplicateButEquivalentDeapexersFromPrebuiltApexes(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithApexBuildComponents,
+	)
+
+	bpBase := `
+		apex_set {
+			name: "com.android.myapex",
+			installable: true,
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			set: "myapex.apks",
+		}
+
+		apex_set {
+			name: "com.android.myapex_compressed",
+			apex_name: "com.android.myapex",
+			installable: true,
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			set: "myapex_compressed.apks",
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			apex_available: [
+				"com.android.myapex",
+				"com.android.myapex_compressed",
+			],
+			hidden_api: {
+				annotation_flags: "annotation-flags.csv",
+				metadata: "metadata.csv",
+				index: "index.csv",
+				signature_patterns: "signature_patterns.csv",
+			},
+			%s
+		}
+	`
+
+	t.Run("java_import", func(t *testing.T) {
+		result := preparers.RunTestWithBp(t,
+			fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
+			java_import {
+				name: "libfoo",
+				jars: ["libfoo.jar"],
+				apex_available: [
+					"com.android.myapex",
+					"com.android.myapex_compressed",
+				],
+			}
+		`)
+
+		module := result.Module("libfoo", "android_common_com.android.myapex")
+		usesLibraryDep := module.(java.UsesLibraryDependency)
+		android.AssertPathRelativeToTopEquals(t, "dex jar path",
+			"out/soong/.intermediates/com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
+			usesLibraryDep.DexJarBuildPath().Path())
+	})
+
+	t.Run("java_sdk_library_import", func(t *testing.T) {
+		result := preparers.RunTestWithBp(t,
+			fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
+			java_sdk_library_import {
+				name: "libfoo",
+				public: {
+					jars: ["libbar.jar"],
+				},
+				apex_available: [
+					"com.android.myapex",
+					"com.android.myapex_compressed",
+				],
+				compile_dex: true,
+			}
+		`)
+
+		module := result.Module("libfoo", "android_common_com.android.myapex")
+		usesLibraryDep := module.(java.UsesLibraryDependency)
+		android.AssertPathRelativeToTopEquals(t, "dex jar path",
+			"out/soong/.intermediates/com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
+			usesLibraryDep.DexJarBuildPath().Path())
+	})
+
+	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
+		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
+			image_name: "art",
+			contents: ["libfoo"],
+		`)+`
+			java_sdk_library_import {
+				name: "libfoo",
+				public: {
+					jars: ["libbar.jar"],
+				},
+				apex_available: [
+					"com.android.myapex",
+					"com.android.myapex_compressed",
+				],
+				compile_dex: true,
+			}
+		`)
+	})
+}
+
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
@@ -8695,6 +8808,9 @@
 			name: "mybootclasspathfragment",
 			contents: ["mybootclasspathlib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -9015,6 +9131,9 @@
 				name: "mybootclasspathfragment",
 				contents: ["mybootclasspathlib"],
 				apex_available: ["myapex"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_sdk_library {
@@ -9115,6 +9234,9 @@
 					name: "mybootclasspathfragment",
 					contents: ["mybootclasspathlib"],
 					apex_available: ["myapex"],
+					hidden_api: {
+						split_packages: ["*"],
+					},
 				}
 
 				java_sdk_library {
@@ -9334,6 +9456,9 @@
 			name: "mybootclasspathfragment",
 			contents: ["myjavalib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 		java_library {
 			name: "myjavalib",
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index ce6b7f7..b298dac 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -110,6 +110,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 `,
 	)
@@ -209,6 +212,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		bootclasspath_fragment {
@@ -220,6 +226,9 @@
 							module: "art-bootclasspath-fragment",
 					},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 `,
 	)
@@ -361,6 +370,9 @@
 				apex_available: [
 					"com.android.art",
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 		`, contentsInsert(contents))
 
@@ -853,6 +865,9 @@
 			apex_available: [
 				"myapex",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
@@ -959,6 +974,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex {
@@ -1010,6 +1028,9 @@
 					module: "art-bootclasspath-fragment",
 				},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
@@ -1123,6 +1144,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex {
@@ -1175,6 +1199,9 @@
 					module: "art-bootclasspath-fragment",
 				},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
@@ -1282,6 +1309,9 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex {
@@ -1334,6 +1364,9 @@
 					module: "art-bootclasspath-fragment",
 				},
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 	`)
 
diff --git a/apex/builder.go b/apex/builder.go
index 9119363..e3c4476 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -82,6 +82,11 @@
 		Description: "prepare ${out}",
 	}, "provideNativeLibs", "requireNativeLibs", "opt")
 
+	stripCommentsApexManifestRule = pctx.StaticRule("stripCommentsApexManifestRule", blueprint.RuleParams{
+		Command:     `sed '/^\s*\/\//d' $in > $out`,
+		Description: "strip lines starting with // ${in}=>${out}",
+	})
+
 	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
 		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
 		CommandDeps: []string{"${conv_apex_manifest}"},
@@ -107,6 +112,7 @@
 			`--canned_fs_config ${canned_fs_config} ` +
 			`--include_build_info ` +
 			`--payload_type image ` +
+			`--apex_version ${apex_version} ` +
 			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
 			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}", "${make_erofs}",
@@ -114,7 +120,7 @@
 		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", "payload_fs_type", "apex_version")
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
@@ -122,12 +128,13 @@
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
 			`--payload_type zip ` +
+			`--apex_version ${apex_version} ` +
 			`${image_dir} ${out} `,
 		CommandDeps:    []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "ZipAPEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest")
+	}, "tool_path", "image_dir", "copy_commands", "manifest", "apex_version")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
@@ -205,10 +212,17 @@
 		optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
 	}
 
+	manifestJsonCommentsStripped := android.PathForModuleOut(ctx, "apex_manifest_comments_stripped.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripCommentsApexManifestRule,
+		Input:  src,
+		Output: manifestJsonCommentsStripped,
+	})
+
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
-		Input:  src,
+		Input:  manifestJsonCommentsStripped,
 		Output: manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
@@ -618,7 +632,12 @@
 
 		// Create a NOTICE file, and embed it as an asset file in the APEX.
 		a.htmlGzNotice = android.PathForModuleOut(ctx, "NOTICE.html.gz")
-		android.BuildNoticeHtmlOutputFromLicenseMetadata(ctx, a.htmlGzNotice, "", "", unsignedOutputFile.String())
+		android.BuildNoticeHtmlOutputFromLicenseMetadata(
+			ctx, a.htmlGzNotice, "", "",
+			[]string{
+				android.PathForModuleInstall(ctx).String() + "/",
+				android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/",
+			})
 		noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
 		builder := android.NewRuleBuilder(pctx, ctx)
 		builder.Command().Text("cp").
@@ -665,6 +684,7 @@
 				"file_contexts":    fileContexts.String(),
 				"canned_fs_config": cannedFsConfig.String(),
 				"key":              a.privateKeyFile.String(),
+				"apex_version":     defaultManifestVersion,
 				"opt_flags":        strings.Join(optFlags, " "),
 			},
 		})
@@ -761,6 +781,7 @@
 				"image_dir":     imageDir.String(),
 				"copy_commands": strings.Join(copyCommands, " && "),
 				"manifest":      a.manifestPbOut.String(),
+				"apex_version":  defaultManifestVersion,
 			},
 		})
 	}
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 60f18bd..9142eed 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -88,6 +88,9 @@
 				"baz",
 				"quuz",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -134,6 +137,9 @@
 			contents: [
 				"bar",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
diff --git a/apex/constants.go b/apex/constants.go
new file mode 100644
index 0000000..c68edb7
--- /dev/null
+++ b/apex/constants.go
@@ -0,0 +1,36 @@
+// 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.
+
+package apex
+
+// This file contains branch specific constants. They are stored in a separate
+// file to minimise the potential of merge conflicts between branches when
+// the code from the package is changed.
+
+// The default manifest version for all the modules on this branch.
+// This version code will be used only if there is no version field in the
+// module's apex_manifest.json. Release branches have their version injected
+// into apex_manifest.json by the tooling and will not use the version set
+// here. Developers can also set the version field locally in the
+// apex_manifest.json to build a module with a specific version.
+//
+// The value follows the schema from go/mainline-version-codes, and is chosen
+// based on the branch such that the builds from testing and development
+// branches will have a version higher than the prebuilts.
+// Versions per branch:
+// * x-dev           - xx0090000 (where xx is the branch SDK level)
+// * AOSP            - xx9990000
+// * x-mainline-prod - xx9990000
+// * master          - 990090000
+const defaultManifestVersion = "339990000"
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 06c39ee..4b48da8 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -125,6 +125,7 @@
 					unsupported_packages: [
 							"bar-unsupported-packages.txt",
 					],
+					split_packages: ["*"],
 				},
 			}
 
@@ -274,6 +275,9 @@
 				"baz",
 				"quuz",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -317,6 +321,9 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["bar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		apex_key {
@@ -482,6 +489,9 @@
 			contents: [
 				"foo", "bar",
 			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		prebuilt_bootclasspath_fragment {
@@ -599,6 +609,9 @@
 				generate_classpaths_proto: false,
 				contents: ["foo"],
 				apex_available: ["myapex"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_library {
@@ -656,6 +669,9 @@
 				contents: [
 					"foo",
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			platform_bootclasspath {
@@ -696,6 +712,9 @@
 			bootclasspath_fragment {
 				name: "not-in-apex-fragment",
 				contents: ["foo"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			platform_bootclasspath {
@@ -746,6 +765,9 @@
 				name: "apex-fragment",
 				contents: ["foo", "bar"],
 				apex_available:[ "myapex" ],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			platform_bootclasspath {
diff --git a/bazel/aquery.go b/bazel/aquery.go
index ee09d0b..ae2b107 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -16,28 +16,32 @@
 
 import (
 	"crypto/sha256"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"path/filepath"
 	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
 )
 
+type artifactId int
+type depsetId int
+type pathFragmentId int
+
 // artifact contains relevant portions of Bazel's aquery proto, Artifact.
 // Represents a single artifact, whether it's a source file or a derived output file.
 type artifact struct {
-	Id             int
-	PathFragmentId int
+	Id             artifactId
+	PathFragmentId pathFragmentId
 }
 
 type pathFragment struct {
-	Id       int
+	Id       pathFragmentId
 	Label    string
-	ParentId int
+	ParentId pathFragmentId
 }
 
 // KeyValuePair represents Bazel's aquery proto, KeyValuePair.
@@ -63,9 +67,9 @@
 // Represents a data structure containing one or more files. Depsets in Bazel are an efficient
 // data structure for storing large numbers of file paths.
 type depSetOfFiles struct {
-	Id                  int
-	DirectArtifactIds   []int
-	TransitiveDepSetIds []int
+	Id                  depsetId
+	DirectArtifactIds   []artifactId
+	TransitiveDepSetIds []depsetId
 }
 
 // action contains relevant portions of Bazel's aquery proto, Action.
@@ -73,11 +77,12 @@
 type action struct {
 	Arguments            []string
 	EnvironmentVariables []KeyValuePair
-	InputDepSetIds       []int
+	InputDepSetIds       []depsetId
 	Mnemonic             string
-	OutputIds            []int
+	OutputIds            []artifactId
 	TemplateContent      string
 	Substitutions        []KeyValuePair
+	FileContents         string
 }
 
 // actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -105,46 +110,56 @@
 	// input path string, but not both.
 	InputDepsetHashes []string
 	InputPaths        []string
+	FileContents      string
 }
 
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
 // less readable Bazel structures (depset and path fragment).
 type aqueryArtifactHandler struct {
+	// Switches to true if any depset contains only `bazelToolsDependencySentinel`
+	bazelToolsDependencySentinelNeeded bool
 	// Maps depset id to AqueryDepset, a representation of depset which is
 	// post-processed for middleman artifact handling, unhandled artifact
 	// dropping, content hashing, etc.
-	depsetIdToAqueryDepset map[int]AqueryDepset
+	depsetIdToAqueryDepset map[depsetId]AqueryDepset
 	// Maps content hash to AqueryDepset.
 	depsetHashToAqueryDepset map[string]AqueryDepset
 
 	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
 	// may be an expensive operation.
 	depsetHashToArtifactPathsCache map[string][]string
-	// Maps artifact ContentHash to fully expanded path.
-	artifactIdToPath map[int]string
+	// Maps artifact ids to fully expanded paths.
+	artifactIdToPath map[artifactId]string
 }
 
 // The tokens should be substituted with the value specified here, instead of the
 // one returned in 'substitutions' of TemplateExpand action.
-var TemplateActionOverriddenTokens = map[string]string{
+var templateActionOverriddenTokens = map[string]string{
 	// Uses "python3" for %python_binary% instead of the value returned by aquery
 	// which is "py3wrapper.sh". See removePy3wrapperScript.
 	"%python_binary%": "python3",
 }
 
-// This pattern matches the MANIFEST file created for a py_binary target.
-var manifestFilePattern = regexp.MustCompile(".*/.+\\.runfiles/MANIFEST$")
-
 // The file name of py3wrapper.sh, which is used by py_binary targets.
 const py3wrapperFileName = "/py3wrapper.sh"
 
-func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
-	pathFragments := map[int]pathFragment{}
-	for _, pathFragment := range aqueryResult.PathFragments {
-		pathFragments[pathFragment.Id] = pathFragment
-	}
+// A file to be put into depsets that are otherwise empty
+const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
 
-	artifactIdToPath := map[int]string{}
+func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
+	m := map[K]V{}
+	for _, v := range values {
+		m[keyFn(v)] = v
+	}
+	return m
+}
+
+func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
+	pathFragments := indexBy(aqueryResult.PathFragments, func(pf pathFragment) pathFragmentId {
+		return pf.Id
+	})
+
+	artifactIdToPath := map[artifactId]string{}
 	for _, artifact := range aqueryResult.Artifacts {
 		artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
 		if err != nil {
@@ -155,10 +170,10 @@
 
 	// Map middleman artifact ContentHash to input artifact depset ID.
 	// Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
-	// if we find a middleman action which has outputs [foo, bar], and output [baz_middleman], then,
+	// if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
 	// for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
 	// that action instead.
-	middlemanIdToDepsetIds := map[int][]int{}
+	middlemanIdToDepsetIds := map[artifactId][]depsetId{}
 	for _, actionEntry := range aqueryResult.Actions {
 		if actionEntry.Mnemonic == "Middleman" {
 			for _, outputId := range actionEntry.OutputIds {
@@ -167,19 +182,12 @@
 		}
 	}
 
-	// Store all depset IDs to validate all depset links are resolvable.
-	depsetIds := map[int]bool{}
-	for _, depset := range aqueryResult.DepSetOfFiles {
-		depsetIds[depset.Id] = true
-	}
-
-	depsetIdToDepset := map[int]depSetOfFiles{}
-	for _, depset := range aqueryResult.DepSetOfFiles {
-		depsetIdToDepset[depset.Id] = depset
-	}
+	depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d depSetOfFiles) depsetId {
+		return d.Id
+	})
 
 	aqueryHandler := aqueryArtifactHandler{
-		depsetIdToAqueryDepset:         map[int]AqueryDepset{},
+		depsetIdToAqueryDepset:         map[depsetId]AqueryDepset{},
 		depsetHashToAqueryDepset:       map[string]AqueryDepset{},
 		depsetHashToArtifactPathsCache: map[string][]string{},
 		artifactIdToPath:               artifactIdToPath,
@@ -198,12 +206,12 @@
 
 // Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
 // depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[int][]int, depsetIdToDepset map[int]depSetOfFiles) (AqueryDepset, error) {
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
 	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
 		return aqueryDepset, nil
 	}
 	transitiveDepsetIds := depset.TransitiveDepSetIds
-	directArtifactPaths := []string{}
+	var directArtifactPaths []string
 	for _, artifactId := range depset.DirectArtifactIds {
 		path, pathExists := a.artifactIdToPath[artifactId]
 		if !pathExists {
@@ -214,25 +222,20 @@
 		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[artifactId]; isMiddleman {
 			// Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
 			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
-		} else if strings.HasSuffix(path, py3wrapperFileName) || manifestFilePattern.MatchString(path) {
+		} else if strings.HasSuffix(path, py3wrapperFileName) ||
+			strings.HasPrefix(path, "../bazel_tools") {
 			// Drop these artifacts.
 			// See go/python-binary-host-mixed-build for more details.
-			// 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
-			// Bazel py_binary targets, so there is no Ninja build statements generated for creating it.
-			// 2) For MANIFEST file, SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
-			// but it doesn't contain sufficient information so no Ninja build statements are generated
-			// for creating it.
-			// So in mixed build mode, when these two are used as input of some Ninja build statement,
-			// since there is no build statement to create them, they should be removed from input paths.
-			// TODO(b/197135294): Clean up this custom runfiles handling logic when
-			// SourceSymlinkManifest and SymlinkTree actions are supported.
+			// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
+			// TemplateExpandAction handles everything necessary to launch a Pythin application.
+			// 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
+			// containing depset to always be considered newer than their outputs.
 		} else {
-			// TODO(b/216194240): Filter out bazel tools.
 			directArtifactPaths = append(directArtifactPaths, path)
 		}
 	}
 
-	childDepsetHashes := []string{}
+	var childDepsetHashes []string
 	for _, childDepsetId := range transitiveDepsetIds {
 		childDepset, exists := depsetIdToDepset[childDepsetId]
 		if !exists {
@@ -244,6 +247,13 @@
 		}
 		childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
 	}
+	if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
+		// We could omit this depset altogether but that requires cleanup on
+		// transitive dependents.
+		// As a simpler alternative, we use this sentinel file as a dependency.
+		directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
+		a.bazelToolsDependencySentinelNeeded = true
+	}
 	aqueryDepset := AqueryDepset{
 		ContentHash:            depsetContentHash(directArtifactPaths, childDepsetHashes),
 		DirectArtifacts:        directArtifactPaths,
@@ -258,8 +268,8 @@
 // input paths contained in these depsets.
 // This is a potentially expensive operation, and should not be invoked except
 // for actions which need specialized input handling.
-func (a *aqueryArtifactHandler) getInputPaths(depsetIds []int) ([]string, error) {
-	inputPaths := []string{}
+func (a *aqueryArtifactHandler) getInputPaths(depsetIds []depsetId) ([]string, error) {
+	var inputPaths []string
 
 	for _, inputDepSetId := range depsetIds {
 		depset := a.depsetIdToAqueryDepset[inputDepSetId]
@@ -291,12 +301,12 @@
 		a.depsetHashToArtifactPathsCache[depsetHash] = result
 		return result, nil
 	} else {
-		return nil, fmt.Errorf("undefined input depset hash %d", depsetHash)
+		return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
 	}
 }
 
 // AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
-// which should be registered (and output  to a ninja file) to correspond with Bazel's
+// which should be registered (and output to a ninja file) to correspond with Bazel's
 // action graph, as described by the given action graph json proto.
 // BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
 // are one-to-one with Bazel's depSetOfFiles objects.
@@ -312,6 +322,13 @@
 	}
 
 	var buildStatements []BuildStatement
+	if aqueryHandler.bazelToolsDependencySentinelNeeded {
+		buildStatements = append(buildStatements, BuildStatement{
+			Command:     fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
+			OutputPaths: []string{bazelToolsDependencySentinel},
+			Mnemonic:    bazelToolsDependencySentinel,
+		})
+	}
 
 	for _, actionEntry := range aqueryResult.Actions {
 		if shouldSkipAction(actionEntry) {
@@ -319,12 +336,14 @@
 		}
 
 		var buildStatement BuildStatement
-		if isSymlinkAction(actionEntry) {
+		if actionEntry.isSymlinkAction() {
 			buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
-		} else if isTemplateExpandAction(actionEntry) && len(actionEntry.Arguments) < 1 {
+		} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
 			buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
-		} else if isPythonZipperAction(actionEntry) {
-			buildStatement, err = aqueryHandler.pythonZipperActionBuildStatement(actionEntry, buildStatements)
+		} else if actionEntry.isFileWriteAction() {
+			buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
+		} else if actionEntry.isSymlinkTreeAction() {
+			buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
 		} else if len(actionEntry.Arguments) < 1 {
 			return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
 		} else {
@@ -338,12 +357,12 @@
 	}
 
 	depsetsByHash := map[string]AqueryDepset{}
-	depsets := []AqueryDepset{}
+	var depsets []AqueryDepset
 	for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
 		if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
 			// Two depsets collide on hash. Ensure that their contents are identical.
 			if !reflect.DeepEqual(aqueryDepset, prevEntry) {
-				return nil, nil, fmt.Errorf("Two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
+				return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
 			}
 		} else {
 			depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
@@ -386,15 +405,15 @@
 	h := sha256.New()
 	// Use newline as delimiter, as paths cannot contain newline.
 	h.Write([]byte(strings.Join(directPaths, "\n")))
-	h.Write([]byte(strings.Join(transitiveDepsetHashes, "\n")))
-	fullHash := fmt.Sprintf("%016x", h.Sum(nil))
+	h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
+	fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
 	return fullHash
 }
 
-func (aqueryHandler *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []int) ([]string, error) {
-	hashes := []string{}
+func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []depsetId) ([]string, error) {
+	var hashes []string
 	for _, depsetId := range inputDepsetIds {
-		if aqueryDepset, exists := aqueryHandler.depsetIdToAqueryDepset[depsetId]; !exists {
+		if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
 			return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
 		} else {
 			hashes = append(hashes, aqueryDepset.ContentHash)
@@ -403,13 +422,13 @@
 	return hashes, nil
 }
 
-func (aqueryHandler *aqueryArtifactHandler) normalActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry action) (BuildStatement, error) {
 	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
-	inputDepsetHashes, err := aqueryHandler.depsetContentHashes(actionEntry.InputDepSetIds)
+	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
 		return BuildStatement{}, err
 	}
-	outputPaths, depfile, err := aqueryHandler.getOutputPaths(actionEntry)
+	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
 		return BuildStatement{}, err
 	}
@@ -425,56 +444,8 @@
 	return buildStatement, nil
 }
 
-func (aqueryHandler *aqueryArtifactHandler) pythonZipperActionBuildStatement(actionEntry action, prevBuildStatements []BuildStatement) (BuildStatement, error) {
-	inputPaths, err := aqueryHandler.getInputPaths(actionEntry.InputDepSetIds)
-	if err != nil {
-		return BuildStatement{}, err
-	}
-	outputPaths, depfile, err := aqueryHandler.getOutputPaths(actionEntry)
-	if err != nil {
-		return BuildStatement{}, err
-	}
-
-	if len(inputPaths) < 1 || len(outputPaths) != 1 {
-		return BuildStatement{}, fmt.Errorf("Expect 1+ input and 1 output to python zipper action, got: input %q, output %q", inputPaths, outputPaths)
-	}
-	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
-	inputPaths, command = removePy3wrapperScript(inputPaths, command)
-	command = addCommandForPyBinaryRunfilesDir(command, inputPaths[0], outputPaths[0])
-	// Add the python zip file as input of the corresponding python binary stub script in Ninja build statements.
-	// In Ninja build statements, the outputs of dependents of a python binary have python binary stub script as input,
-	// which is not sufficient without the python zip file from which runfiles directory is created for py_binary.
-	//
-	// The following logic relies on that Bazel aquery output returns actions in the order that
-	// PythonZipper is after TemplateAction of creating Python binary stub script. If later Bazel doesn't return actions
-	// in that order, the following logic might not find the build statement generated for Python binary
-	// stub script and the build might fail. So the check of pyBinaryFound is added to help debug in case later Bazel might change aquery output.
-	// See go/python-binary-host-mixed-build for more details.
-	pythonZipFilePath := outputPaths[0]
-	pyBinaryFound := false
-	for i, _ := range prevBuildStatements {
-		if len(prevBuildStatements[i].OutputPaths) == 1 && prevBuildStatements[i].OutputPaths[0]+".zip" == pythonZipFilePath {
-			prevBuildStatements[i].InputPaths = append(prevBuildStatements[i].InputPaths, pythonZipFilePath)
-			pyBinaryFound = true
-		}
-	}
-	if !pyBinaryFound {
-		return BuildStatement{}, fmt.Errorf("Could not find the correspondinging Python binary stub script of PythonZipper: %q", outputPaths)
-	}
-
-	buildStatement := BuildStatement{
-		Command:     command,
-		Depfile:     depfile,
-		OutputPaths: outputPaths,
-		InputPaths:  inputPaths,
-		Env:         actionEntry.EnvironmentVariables,
-		Mnemonic:    actionEntry.Mnemonic,
-	}
-	return buildStatement, nil
-}
-
-func (aqueryHandler *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
-	outputPaths, depfile, err := aqueryHandler.getOutputPaths(actionEntry)
+func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
+	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
 		return BuildStatement{}, err
 	}
@@ -489,7 +460,7 @@
 	// See go/python-binary-host-mixed-build for more details.
 	command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
 		escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
-	inputDepsetHashes, err := aqueryHandler.depsetContentHashes(actionEntry.InputDepSetIds)
+	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
 		return BuildStatement{}, err
 	}
@@ -505,13 +476,54 @@
 	return buildStatement, nil
 }
 
-func (aqueryHandler *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
-	outputPaths, depfile, err := aqueryHandler.getOutputPaths(actionEntry)
+func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry action) (BuildStatement, error) {
+	outputPaths, _, err := a.getOutputPaths(actionEntry)
+	var depsetHashes []string
+	if err == nil {
+		depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
+	}
+	if err != nil {
+		return BuildStatement{}, err
+	}
+	return BuildStatement{
+		Depfile:           nil,
+		OutputPaths:       outputPaths,
+		Env:               actionEntry.EnvironmentVariables,
+		Mnemonic:          actionEntry.Mnemonic,
+		InputDepsetHashes: depsetHashes,
+		FileContents:      actionEntry.FileContents,
+	}, nil
+}
+
+func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry action) (BuildStatement, error) {
+	outputPaths, _, err := a.getOutputPaths(actionEntry)
+	if err != nil {
+		return BuildStatement{}, err
+	}
+	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
+	if err != nil {
+		return BuildStatement{}, err
+	}
+	if len(inputPaths) != 1 || len(outputPaths) != 1 {
+		return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+	}
+	// The actual command is generated in bazelSingleton.GenerateBuildActions
+	return BuildStatement{
+		Depfile:     nil,
+		OutputPaths: outputPaths,
+		Env:         actionEntry.EnvironmentVariables,
+		Mnemonic:    actionEntry.Mnemonic,
+		InputPaths:  inputPaths,
+	}, nil
+}
+
+func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
+	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
 		return BuildStatement{}, err
 	}
 
-	inputPaths, err := aqueryHandler.getInputPaths(actionEntry.InputDepSetIds)
+	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
 	if err != nil {
 		return BuildStatement{}, err
 	}
@@ -538,9 +550,9 @@
 	return buildStatement, nil
 }
 
-func (aqueryHandler *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths []string, depfile *string, err error) {
+func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths []string, depfile *string, err error) {
 	for _, outputId := range actionEntry.OutputIds {
-		outputPath, exists := aqueryHandler.artifactIdToPath[outputId]
+		outputPath, exists := a.artifactIdToPath[outputId]
 		if !exists {
 			err = fmt.Errorf("undefined outputId %d", outputId)
 			return
@@ -565,7 +577,7 @@
 	replacerString := []string{}
 	for _, pair := range actionEntry.Substitutions {
 		value := pair.Value
-		if val, ok := TemplateActionOverriddenTokens[pair.Key]; ok {
+		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
 			value = val
 		}
 		replacerString = append(replacerString, pair.Key, value)
@@ -587,83 +599,45 @@
 	return replacer.Replace(str)
 }
 
-// removePy3wrapperScript removes py3wrapper.sh from the input paths and command of the action of
-// creating python zip file in mixed build mode. py3wrapper.sh is returned as input by aquery but
-// there is no action returned by aquery for creating it. So in mixed build "python3" is used
-// as the PYTHON_BINARY in python binary stub script, and py3wrapper.sh is not needed and should be
-// removed from input paths and command of creating python zip file.
-// See go/python-binary-host-mixed-build for more details.
-// TODO(b/205879240) remove this after py3wrapper.sh could be created in the mixed build mode.
-func removePy3wrapperScript(inputPaths []string, command string) (newInputPaths []string, newCommand string) {
-	// Remove from inputs
-	filteredInputPaths := []string{}
-	for _, path := range inputPaths {
-		if !strings.HasSuffix(path, py3wrapperFileName) {
-			filteredInputPaths = append(filteredInputPaths, path)
-		}
-	}
-	newInputPaths = filteredInputPaths
-
-	// Remove from command line
-	var re = regexp.MustCompile(`\S*` + py3wrapperFileName)
-	newCommand = re.ReplaceAllString(command, "")
-	return
+func (a action) isSymlinkAction() bool {
+	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
 }
 
-// addCommandForPyBinaryRunfilesDir adds commands creating python binary runfiles directory.
-// runfiles directory is created by using MANIFEST file and MANIFEST file is the output of
-// SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
-// but since SourceSymlinkManifest doesn't contain sufficient information
-// so MANIFEST file could not be created, which also blocks the creation of runfiles directory.
-// See go/python-binary-host-mixed-build for more details.
-// TODO(b/197135294) create runfiles directory from MANIFEST file once it can be created from SourceSymlinkManifest action.
-func addCommandForPyBinaryRunfilesDir(oldCommand string, zipperCommandPath, zipFilePath string) string {
-	// Unzip the zip file, zipFilePath looks like <python_binary>.zip
-	runfilesDirName := zipFilePath[0:len(zipFilePath)-4] + ".runfiles"
-	command := fmt.Sprintf("%s x %s -d %s", zipperCommandPath, zipFilePath, runfilesDirName)
-	// Create a symbolic link in <python_binary>.runfiles/, which is the expected structure
-	// when running the python binary stub script.
-	command += fmt.Sprintf(" && ln -sf runfiles/__main__ %s", runfilesDirName)
-	return oldCommand + " && " + command
-}
-
-func isSymlinkAction(a action) bool {
-	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
-}
-
-func isTemplateExpandAction(a action) bool {
+func (a action) isTemplateExpandAction() bool {
 	return a.Mnemonic == "TemplateExpand"
 }
 
-func isPythonZipperAction(a action) bool {
-	return a.Mnemonic == "PythonZipper"
+func (a action) isFileWriteAction() bool {
+	return a.Mnemonic == "FileWrite" || a.Mnemonic == "SourceSymlinkManifest"
+}
+
+func (a action) isSymlinkTreeAction() bool {
+	return a.Mnemonic == "SymlinkTree"
 }
 
 func shouldSkipAction(a action) bool {
-	// TODO(b/180945121): Handle complex symlink actions.
-	if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
-		return true
-	}
 	// Middleman actions are not handled like other actions; they are handled separately as a
 	// preparatory step so that their inputs may be relayed to actions depending on middleman
 	// artifacts.
 	if a.Mnemonic == "Middleman" {
 		return true
 	}
+	// PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
+	if a.Mnemonic == "PythonZipper" {
+		return true
+	}
 	// Skip "Fail" actions, which are placeholder actions designed to always fail.
 	if a.Mnemonic == "Fail" {
 		return true
 	}
-	// TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
-	// about the contents that are written.
-	if a.Mnemonic == "FileWrite" {
+	if a.Mnemonic == "BaselineCoverage" {
 		return true
 	}
 	return false
 }
 
-func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
-	labels := []string{}
+func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]pathFragment) (string, error) {
+	var labels []string
 	currId := id
 	// Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
 	for currId > 0 {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 740a1f1..3a2bf0f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"sort"
 	"testing"
 )
 
@@ -24,28 +25,14 @@
 	// This input string is retrieved from a real build of bionic-related genrules.
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 6
-  }, {
-    "id": 3,
-    "pathFragmentId": 8
-  }, {
-    "id": 4,
-    "pathFragmentId": 12
-  }, {
-    "id": 5,
-    "pathFragmentId": 19
-  }, {
-    "id": 6,
-    "pathFragmentId": 20
-  }, {
-    "id": 7,
-    "pathFragmentId": 21
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 6 },
+    { "id": 3, "pathFragmentId": 8 },
+    { "id": 4, "pathFragmentId": 12 },
+    { "id": 5, "pathFragmentId": 19 },
+    { "id": 6, "pathFragmentId": 20 },
+    { "id": 7, "pathFragmentId": 21 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
@@ -99,132 +86,48 @@
     "outputIds": [7],
     "primaryOutputId": 7
   }],
-  "targets": [{
-    "id": 1,
-    "label": "@sourceroot//bionic/libc:syscalls-arm",
-    "ruleClassId": 1
-  }, {
-    "id": 2,
-    "label": "@sourceroot//bionic/libc:syscalls-x86",
-    "ruleClassId": 1
-  }, {
-    "id": 3,
-    "label": "@sourceroot//bionic/libc:syscalls-x86_64",
-    "ruleClassId": 1
-  }, {
-    "id": 4,
-    "label": "@sourceroot//bionic/libc:syscalls-arm64",
-    "ruleClassId": 1
-  }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2, 3]
-  }, {
-    "id": 2,
-    "directArtifactIds": [1, 2, 3]
-  }, {
-    "id": 3,
-    "directArtifactIds": [1, 2, 3]
-  }, {
-    "id": 4,
-    "directArtifactIds": [1, 2, 3]
-  }],
+  "targets": [
+    { "id": 1, "label": "@sourceroot//bionic/libc:syscalls-arm", "ruleClassId": 1 },
+    { "id": 2, "label": "@sourceroot//bionic/libc:syscalls-x86", "ruleClassId": 1 },
+    { "id": 3, "label": "@sourceroot//bionic/libc:syscalls-x86_64", "ruleClassId": 1 },
+    { "id": 4, "label": "@sourceroot//bionic/libc:syscalls-arm64", "ruleClassId": 1 }],
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2, 3] },
+    { "id": 2, "directArtifactIds": [1, 2, 3] },
+    { "id": 3, "directArtifactIds": [1, 2, 3] },
+    { "id": 4, "directArtifactIds": [1, 2, 3] }],
   "configuration": [{
     "id": 1,
     "mnemonic": "k8-fastbuild",
     "platformName": "k8",
     "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
   }],
-  "ruleClasses": [{
-    "id": 1,
-    "name": "genrule"
-  }],
-  "pathFragments": [{
-    "id": 5,
-    "label": ".."
-  }, {
-    "id": 4,
-    "label": "sourceroot",
-    "parentId": 5
-  }, {
-    "id": 3,
-    "label": "bionic",
-    "parentId": 4
-  }, {
-    "id": 2,
-    "label": "libc",
-    "parentId": 3
-  }, {
-    "id": 1,
-    "label": "SYSCALLS.TXT",
-    "parentId": 2
-  }, {
-    "id": 7,
-    "label": "tools",
-    "parentId": 2
-  }, {
-    "id": 6,
-    "label": "gensyscalls.py",
-    "parentId": 7
-  }, {
-    "id": 11,
-    "label": "bazel_tools",
-    "parentId": 5
-  }, {
-    "id": 10,
-    "label": "tools",
-    "parentId": 11
-  }, {
-    "id": 9,
-    "label": "genrule",
-    "parentId": 10
-  }, {
-    "id": 8,
-    "label": "genrule-setup.sh",
-    "parentId": 9
-  }, {
-    "id": 18,
-    "label": "bazel-out"
-  }, {
-    "id": 17,
-    "label": "sourceroot",
-    "parentId": 18
-  }, {
-    "id": 16,
-    "label": "k8-fastbuild",
-    "parentId": 17
-  }, {
-    "id": 15,
-    "label": "bin",
-    "parentId": 16
-  }, {
-    "id": 14,
-    "label": "bionic",
-    "parentId": 15
-  }, {
-    "id": 13,
-    "label": "libc",
-    "parentId": 14
-  }, {
-    "id": 12,
-    "label": "syscalls-arm.S",
-    "parentId": 13
-  }, {
-    "id": 19,
-    "label": "syscalls-x86.S",
-    "parentId": 13
-  }, {
-    "id": 20,
-    "label": "syscalls-x86_64.S",
-    "parentId": 13
-  }, {
-    "id": 21,
-    "label": "syscalls-arm64.S",
-    "parentId": 13
-  }]
+  "ruleClasses": [{ "id": 1, "name": "genrule"}],
+  "pathFragments": [
+    { "id": 5, "label": ".." },
+    { "id": 4, "label": "sourceroot", "parentId": 5 },
+    { "id": 3, "label": "bionic", "parentId": 4 },
+    { "id": 2, "label": "libc", "parentId": 3 },
+    { "id": 1, "label": "SYSCALLS.TXT", "parentId": 2 },
+    { "id": 7, "label": "tools", "parentId": 2 },
+    { "id": 6, "label": "gensyscalls.py", "parentId": 7 },
+    { "id": 11, "label": "bazel_tools", "parentId": 5 },
+    { "id": 10, "label": "tools", "parentId": 11 },
+    { "id": 9, "label": "genrule", "parentId": 10 },
+    { "id": 8, "label": "genrule-setup.sh", "parentId": 9 },
+    { "id": 18, "label": "bazel-out" },
+    { "id": 17, "label": "sourceroot", "parentId": 18 },
+    { "id": 16, "label": "k8-fastbuild", "parentId": 17 },
+    { "id": 15, "label": "bin", "parentId": 16 },
+    { "id": 14, "label": "bionic", "parentId": 15 },
+    { "id": 13, "label": "libc", "parentId": 14 },
+    { "id": 12, "label": "syscalls-arm.S", "parentId": 13 },
+    { "id": 19, "label": "syscalls-x86.S", "parentId": 13 },
+    { "id": 20, "label": "syscalls-x86_64.S", "parentId": 13 },
+    { "id": 21, "label": "syscalls-arm64.S", "parentId": 13 }]
 }`
 	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
-	expectedBuildStatements := []BuildStatement{}
+	var expectedBuildStatements []BuildStatement
 	for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
 		expectedBuildStatements = append(expectedBuildStatements,
 			BuildStatement{
@@ -235,7 +138,7 @@
 					fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
 				},
 				Env: []KeyValuePair{
-					KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
+					{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
 				},
 				Mnemonic: "Genrule",
 			})
@@ -245,7 +148,6 @@
 	expectedFlattenedInputs := []string{
 		"../sourceroot/bionic/libc/SYSCALLS.TXT",
 		"../sourceroot/bionic/libc/tools/gensyscalls.py",
-		"../bazel_tools/tools/genrule/genrule-setup.sh",
 	}
 	// In this example, each depset should have the same expected inputs.
 	for _, actualDepset := range actualDepsets {
@@ -259,13 +161,9 @@
 func TestInvalidOutputId(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -275,17 +173,11 @@
     "outputIds": [3],
     "primaryOutputId": 3
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -295,13 +187,9 @@
 func TestInvalidInputDepsetIdFromAction(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -311,17 +199,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -331,13 +213,9 @@
 func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -347,18 +225,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2],
-    "transitiveDepSetIds": [42]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2], "transitiveDepSetIds": [42] }],
+  "pathFragments": [
+    { "id": 1, "label": "one"},
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -368,13 +239,9 @@
 func TestInvalidInputArtifactId(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -384,17 +251,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 3]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 3] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -404,13 +265,9 @@
 func TestInvalidPathFragmentId(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -420,18 +277,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two",
-		"parentId": 3
-  }]
+  "depSetOfFiles": [
+     { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    {  "id": 1, "label": "one" },
+    {  "id": 2, "label": "two", "parentId": 3 }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -441,16 +291,10 @@
 func TestDepfiles(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -460,20 +304,12 @@
     "outputIds": [2, 3],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2, 3]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }, {
-    "id": 3,
-    "label": "two.d"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2, 3] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" },
+    { "id": 3, "label": "two.d" }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -496,19 +332,11 @@
 func TestMultipleDepfiles(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }, {
-    "id": 4,
-    "pathFragmentId": 4
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 },
+    { "id": 4, "pathFragmentId": 4 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -522,19 +350,11 @@
     "id": 1,
     "directArtifactIds": [1, 2, 3, 4]
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }, {
-    "id": 3,
-    "label": "two.d"
-  }, {
-    "id": 4,
-    "label": "other.d"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" },
+    { "id": 3, "label": "two.d" },
+    { "id": 4, "label": "other.d" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -546,70 +366,28 @@
 	// a single action with many inputs given via a deep depset.
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 7
-  }, {
-    "id": 3,
-    "pathFragmentId": 8
-  }, {
-    "id": 4,
-    "pathFragmentId": 9
-  }, {
-    "id": 5,
-    "pathFragmentId": 10
-  }, {
-    "id": 6,
-    "pathFragmentId": 11
-  }, {
-    "id": 7,
-    "pathFragmentId": 12
-  }, {
-    "id": 8,
-    "pathFragmentId": 13
-  }, {
-    "id": 9,
-    "pathFragmentId": 14
-  }, {
-    "id": 10,
-    "pathFragmentId": 15
-  }, {
-    "id": 11,
-    "pathFragmentId": 16
-  }, {
-    "id": 12,
-    "pathFragmentId": 17
-  }, {
-    "id": 13,
-    "pathFragmentId": 18
-  }, {
-    "id": 14,
-    "pathFragmentId": 19
-  }, {
-    "id": 15,
-    "pathFragmentId": 20
-  }, {
-    "id": 16,
-    "pathFragmentId": 21
-  }, {
-    "id": 17,
-    "pathFragmentId": 22
-  }, {
-    "id": 18,
-    "pathFragmentId": 23
-  }, {
-    "id": 19,
-    "pathFragmentId": 24
-  }, {
-    "id": 20,
-    "pathFragmentId": 25
-  }, {
-    "id": 21,
-    "pathFragmentId": 26
-  }],
+  "artifacts": [
+   { "id": 1, "pathFragmentId": 1 },
+   { "id": 2, "pathFragmentId": 7 },
+   { "id": 3, "pathFragmentId": 8 },
+   { "id": 4, "pathFragmentId": 9 },
+   { "id": 5, "pathFragmentId": 10 },
+   { "id": 6, "pathFragmentId": 11 },
+   { "id": 7, "pathFragmentId": 12 },
+   { "id": 8, "pathFragmentId": 13 },
+   { "id": 9, "pathFragmentId": 14 },
+   { "id": 10, "pathFragmentId": 15 },
+   { "id": 11, "pathFragmentId": 16 },
+   { "id": 12, "pathFragmentId": 17 },
+   { "id": 13, "pathFragmentId": 18 },
+   { "id": 14, "pathFragmentId": 19 },
+   { "id": 15, "pathFragmentId": 20 },
+   { "id": 16, "pathFragmentId": 21 },
+   { "id": 17, "pathFragmentId": 22 },
+   { "id": 18, "pathFragmentId": 23 },
+   { "id": 19, "pathFragmentId": 24 },
+   { "id": 20, "pathFragmentId": 25 },
+   { "id": 21, "pathFragmentId": 26 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
@@ -620,134 +398,45 @@
     "outputIds": [21],
     "primaryOutputId": 21
   }],
-  "depSetOfFiles": [{
-    "id": 3,
-    "directArtifactIds": [1, 2, 3, 4, 5]
-  }, {
-    "id": 4,
-    "directArtifactIds": [6, 7, 8, 9, 10]
-  }, {
-    "id": 2,
-    "transitiveDepSetIds": [3, 4],
-    "directArtifactIds": [11, 12, 13, 14, 15]
-  }, {
-    "id": 5,
-    "directArtifactIds": [16, 17, 18, 19]
-  }, {
-    "id": 1,
-    "transitiveDepSetIds": [2, 5],
-    "directArtifactIds": [20]
-  }],
-  "pathFragments": [{
-    "id": 6,
-    "label": "bazel-out"
-  }, {
-    "id": 5,
-    "label": "sourceroot",
-    "parentId": 6
-  }, {
-    "id": 4,
-    "label": "k8-fastbuild",
-    "parentId": 5
-  }, {
-    "id": 3,
-    "label": "bin",
-    "parentId": 4
-  }, {
-    "id": 2,
-    "label": "testpkg",
-    "parentId": 3
-  }, {
-    "id": 1,
-    "label": "test_1",
-    "parentId": 2
-  }, {
-    "id": 7,
-    "label": "test_2",
-    "parentId": 2
-  }, {
-    "id": 8,
-    "label": "test_3",
-    "parentId": 2
-  }, {
-    "id": 9,
-    "label": "test_4",
-    "parentId": 2
-  }, {
-    "id": 10,
-    "label": "test_5",
-    "parentId": 2
-  }, {
-    "id": 11,
-    "label": "test_6",
-    "parentId": 2
-  }, {
-    "id": 12,
-    "label": "test_7",
-    "parentId": 2
-  }, {
-    "id": 13,
-    "label": "test_8",
-    "parentId": 2
-  }, {
-    "id": 14,
-    "label": "test_9",
-    "parentId": 2
-  }, {
-    "id": 15,
-    "label": "test_10",
-    "parentId": 2
-  }, {
-    "id": 16,
-    "label": "test_11",
-    "parentId": 2
-  }, {
-    "id": 17,
-    "label": "test_12",
-    "parentId": 2
-  }, {
-    "id": 18,
-    "label": "test_13",
-    "parentId": 2
-  }, {
-    "id": 19,
-    "label": "test_14",
-    "parentId": 2
-  }, {
-    "id": 20,
-    "label": "test_15",
-    "parentId": 2
-  }, {
-    "id": 21,
-    "label": "test_16",
-    "parentId": 2
-  }, {
-    "id": 22,
-    "label": "test_17",
-    "parentId": 2
-  }, {
-    "id": 23,
-    "label": "test_18",
-    "parentId": 2
-  }, {
-    "id": 24,
-    "label": "test_19",
-    "parentId": 2
-  }, {
-    "id": 25,
-    "label": "test_root",
-    "parentId": 2
-  }, {
-    "id": 26,
-    "label": "test_out",
-    "parentId": 2
-  }]
+  "depSetOfFiles": [
+    { "id": 3, "directArtifactIds": [1, 2, 3, 4, 5] },
+    { "id": 4, "directArtifactIds": [6, 7, 8, 9, 10] },
+    { "id": 2, "transitiveDepSetIds": [3, 4], "directArtifactIds": [11, 12, 13, 14, 15] },
+    { "id": 5, "directArtifactIds": [16, 17, 18, 19] },
+    { "id": 1, "transitiveDepSetIds": [2, 5], "directArtifactIds": [20] }],
+  "pathFragments": [
+    { "id": 6, "label": "bazel-out" },
+    { "id": 5, "label": "sourceroot", "parentId": 6 },
+    { "id": 4, "label": "k8-fastbuild", "parentId": 5 },
+    { "id": 3, "label": "bin", "parentId": 4 },
+    { "id": 2, "label": "testpkg", "parentId": 3 },
+    { "id": 1, "label": "test_1", "parentId": 2 },
+    { "id": 7, "label": "test_2", "parentId": 2 },
+    { "id": 8, "label": "test_3", "parentId": 2 },
+    { "id": 9, "label": "test_4", "parentId": 2 },
+    { "id": 10, "label": "test_5", "parentId": 2 },
+    { "id": 11, "label": "test_6", "parentId": 2 },
+    { "id": 12, "label": "test_7", "parentId": 2 },
+    { "id": 13, "label": "test_8", "parentId": 2 },
+    { "id": 14, "label": "test_9", "parentId": 2 },
+    { "id": 15, "label": "test_10", "parentId": 2 },
+    { "id": 16, "label": "test_11", "parentId": 2 },
+    { "id": 17, "label": "test_12", "parentId": 2 },
+    { "id": 18, "label": "test_13", "parentId": 2 },
+    { "id": 19, "label": "test_14", "parentId": 2 },
+    { "id": 20, "label": "test_15", "parentId": 2 },
+    { "id": 21, "label": "test_16", "parentId": 2 },
+    { "id": 22, "label": "test_17", "parentId": 2 },
+    { "id": 23, "label": "test_18", "parentId": 2 },
+    { "id": 24, "label": "test_19", "parentId": 2 },
+    { "id": 25, "label": "test_root", "parentId": 2 },
+    { "id": 26,"label": "test_out", "parentId": 2 }]
 }`
 
 	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
 
 	expectedBuildStatements := []BuildStatement{
-		BuildStatement{
+		{
 			Command:     "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
 			OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
 			Mnemonic:    "Action",
@@ -758,7 +447,7 @@
 	// Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
 	// are given via a deep depset, but the depset is flattened when returned as a
 	// BuildStatement slice.
-	expectedFlattenedInputs := []string{}
+	var expectedFlattenedInputs []string
 	for i := 1; i < 20; i++ {
 		expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
 	}
@@ -771,54 +460,149 @@
 	}
 }
 
+func TestSymlinkTree(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "SymlinkTree",
+    "configurationId": 1,
+    "inputDepSetIds": [1],
+    "outputIds": [2],
+    "primaryOutputId": 2,
+    "executionPlatform": "//build/bazel/platforms:linux_x86_64"
+  }],
+  "pathFragments": [
+    { "id": 1, "label": "foo.manifest" },
+    { "id": 2, "label": "foo.runfiles/MANIFEST" }],
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }]
+}
+`
+	actual, _, err := AqueryBuildStatements([]byte(inputString))
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+	}
+	assertBuildStatements(t, []BuildStatement{
+		{
+			Command:     "",
+			OutputPaths: []string{"foo.runfiles/MANIFEST"},
+			Mnemonic:    "SymlinkTree",
+			InputPaths:  []string{"foo.manifest"},
+		},
+	}, actual)
+}
+
+func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
+	const inputString = `{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 10
+  }, {
+    "id": 2,
+    "pathFragmentId": 20
+  }, {
+    "id": 3,
+    "pathFragmentId": 30
+  }, {
+    "id": 4,
+    "pathFragmentId": 40
+  }],
+  "depSetOfFiles": [{
+    "id": 1111,
+    "directArtifactIds": [3 , 4]
+  }],
+  "actions": [{
+    "targetId": 100,
+    "actionKey": "x",
+    "inputDepSetIds": [1111],
+    "mnemonic": "x",
+    "arguments": ["bogus", "command"],
+    "outputIds": [2],
+    "primaryOutputId": 1
+  }],
+  "pathFragments": [{
+    "id": 10,
+    "label": "input"
+  }, {
+    "id": 20,
+    "label": "output"
+  }, {
+    "id": 30,
+    "label": "dep1",
+    "parentId": 50
+  }, {
+    "id": 40,
+    "label": "dep2",
+    "parentId": 60
+  }, {
+    "id": 50,
+    "label": "bazel_tools",
+    "parentId": 60
+  }, {
+    "id": 60,
+    "label": ".."
+  }]
+}`
+	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
+	if len(actualDepsets) != 1 {
+		t.Errorf("expected 1 depset but found %#v", actualDepsets)
+		return
+	}
+	dep2Found := false
+	for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
+		if dep == "../bazel_tools/dep1" {
+			t.Errorf("dependency %s expected to be removed but still exists", dep)
+		} else if dep == "../dep2" {
+			dep2Found = true
+		}
+	}
+	if !dep2Found {
+		t.Errorf("dependency ../dep2 expected but not found")
+	}
+
+	expectedBuildStatement := BuildStatement{
+		Command:     "bogus command",
+		OutputPaths: []string{"output"},
+		Mnemonic:    "x",
+	}
+	buildStatementFound := false
+	for _, actualBuildStatement := range actualBuildStatements {
+		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+			buildStatementFound = true
+			break
+		}
+	}
+	if !buildStatementFound {
+		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+		return
+	}
+}
+
 func TestMiddlemenAction(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }, {
-    "id": 4,
-    "pathFragmentId": 4
-  }, {
-    "id": 5,
-    "pathFragmentId": 5
-  }, {
-    "id": 6,
-    "pathFragmentId": 6
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "middleinput_one"
-  }, {
-    "id": 2,
-    "label": "middleinput_two"
-  }, {
-    "id": 3,
-    "label": "middleman_artifact"
-  }, {
-    "id": 4,
-    "label": "maininput_one"
-  }, {
-    "id": 5,
-    "label": "maininput_two"
-  }, {
-    "id": 6,
-    "label": "output"
-  }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }, {
-    "id": 2,
-    "directArtifactIds": [3, 4, 5]
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 },
+    { "id": 4, "pathFragmentId": 4 },
+    { "id": 5, "pathFragmentId": 5 },
+    { "id": 6, "pathFragmentId": 6 }],
+  "pathFragments": [
+    { "id": 1, "label": "middleinput_one" },
+    { "id": 2, "label": "middleinput_two" },
+    { "id": 3, "label": "middleman_artifact" },
+    { "id": 4, "label": "maininput_one" },
+    { "id": 5, "label": "maininput_two" },
+    { "id": 6, "label": "output" }],
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] },
+    { "id": 2, "directArtifactIds": [3, 4, 5] }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -847,8 +631,8 @@
 	}
 
 	expectedDepsetFiles := [][]string{
-		[]string{"middleinput_one", "middleinput_two"},
-		[]string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
+		{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
+		{"middleinput_one", "middleinput_two"},
 	}
 	assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
 
@@ -876,7 +660,7 @@
 	for _, depset := range allDepsets {
 		depsetsByHash[depset.ContentHash] = depset
 	}
-	result := []string{}
+	var result []string
 	for _, depsetId := range depsetHashesToFlatten {
 		result = append(result, flattenDepset(depsetId, depsetsByHash)...)
 	}
@@ -886,7 +670,7 @@
 // Returns the contents of a given depset in post order.
 func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
 	depset := allDepsets[depsetHashToFlatten]
-	result := []string{}
+	var result []string
 	for _, depsetId := range depset.TransitiveDepSetHashes {
 		result = append(result, flattenDepset(depsetId, allDepsets)...)
 	}
@@ -897,7 +681,7 @@
 func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
 	t.Helper()
 	if len(actualDepsets) != len(expectedDepsetFiles) {
-		t.Errorf("Expected %d depsets, but got %d depsets", expectedDepsetFiles, actualDepsets)
+		t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
 	}
 	for i, actualDepset := range actualDepsets {
 		actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
@@ -910,13 +694,9 @@
 func TestSimpleSymlink(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 3
-  }, {
-    "id": 2,
-    "pathFragmentId": 5
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 3 },
+    { "id": 2, "pathFragmentId": 5 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -925,30 +705,14 @@
     "outputIds": [2],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "file_subdir",
-    "parentId": 1
-  }, {
-    "id": 3,
-    "label": "file",
-    "parentId": 2
-  }, {
-    "id": 4,
-    "label": "symlink_subdir",
-    "parentId": 1
-  }, {
-    "id": 5,
-    "label": "symlink",
-    "parentId": 4
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "file_subdir", "parentId": 1 },
+    { "id": 3, "label": "file", "parentId": 2 },
+    { "id": 4, "label": "symlink_subdir", "parentId": 1 },
+    { "id": 5, "label": "symlink", "parentId": 4 }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -958,7 +722,7 @@
 	}
 
 	expectedBuildStatements := []BuildStatement{
-		BuildStatement{
+		{
 			Command: "mkdir -p one/symlink_subdir && " +
 				"rm -f one/symlink_subdir/symlink && " +
 				"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
@@ -974,13 +738,9 @@
 func TestSymlinkQuotesPaths(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 3
-  }, {
-    "id": 2,
-    "pathFragmentId": 5
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 3 },
+    { "id": 2, "pathFragmentId": 5 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -989,30 +749,14 @@
     "outputIds": [2],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "file subdir",
-    "parentId": 1
-  }, {
-    "id": 3,
-    "label": "file",
-    "parentId": 2
-  }, {
-    "id": 4,
-    "label": "symlink subdir",
-    "parentId": 1
-  }, {
-    "id": 5,
-    "label": "symlink",
-    "parentId": 4
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "file subdir", "parentId": 1 },
+    { "id": 3, "label": "file", "parentId": 2 },
+    { "id": 4, "label": "symlink subdir", "parentId": 1 },
+    { "id": 5, "label": "symlink", "parentId": 4 }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1022,7 +766,7 @@
 	}
 
 	expectedBuildStatements := []BuildStatement{
-		BuildStatement{
+		{
 			Command: "mkdir -p 'one/symlink subdir' && " +
 				"rm -f 'one/symlink subdir/symlink' && " +
 				"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
@@ -1038,16 +782,10 @@
 func TestSymlinkMultipleInputs(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1056,20 +794,11 @@
     "outputIds": [3],
     "primaryOutputId": 3
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1,2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "file"
-  }, {
-    "id": 2,
-    "label": "other_file"
-  }, {
-    "id": 3,
-    "label": "symlink"
-  }]
+  "depSetOfFiles": [{ "id": 1, "directArtifactIds": [1,2] }],
+  "pathFragments": [
+    { "id": 1, "label": "file" },
+    { "id": 2, "label": "other_file" },
+    { "id": 3, "label": "symlink" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1079,16 +808,10 @@
 func TestSymlinkMultipleOutputs(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1097,20 +820,12 @@
     "outputIds": [2,3],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "file"
-  }, {
-    "id": 2,
-    "label": "symlink"
-  }, {
-    "id": 3,
-    "label": "other_symlink"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }],
+  "pathFragments": [
+    { "id": 1, "label": "file" },
+    { "id": 2, "label": "symlink" },
+    { "id": 3,  "label": "other_symlink" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1133,18 +848,12 @@
     "primaryOutputId": 1,
     "executionPlatform": "//build/bazel/platforms:linux_x86_64",
     "templateContent": "Test template substitutions: %token1%, %python_binary%",
-    "substitutions": [{
-      "key": "%token1%",
-      "value": "abcd"
-    },{
-      "key": "%python_binary%",
-      "value": "python3"
-    }]
+    "substitutions": [
+      { "key": "%token1%", "value": "abcd" },
+      { "key": "%python_binary%", "value": "python3" }]
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "template_file"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "template_file" }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1154,7 +863,7 @@
 	}
 
 	expectedBuildStatements := []BuildStatement{
-		BuildStatement{
+		{
 			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
 				"chmod a+x template_file'",
 			OutputPaths: []string{"template_file"},
@@ -1167,10 +876,8 @@
 func TestTemplateExpandActionNoOutput(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1179,291 +886,79 @@
     "primaryOutputId": 1,
     "executionPlatform": "//build/bazel/platforms:linux_x86_64",
     "templateContent": "Test template substitutions: %token1%, %python_binary%",
-    "substitutions": [{
-      "key": "%token1%",
-      "value": "abcd"
-    },{
-      "key": "%python_binary%",
-      "value": "python3"
-    }]
+    "substitutions": [
+      { "key": "%token1%", "value": "abcd" },
+      { "key": "%python_binary%", "value": "python3" }]
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "template_file"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "template_file" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
 	assertError(t, err, `Expect 1 output to template expand action, got: output []`)
 }
 
-func TestPythonZipperActionSuccess(t *testing.T) {
+func TestFileWrite(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  },{
-    "id": 2,
-    "pathFragmentId": 2
-  },{
-    "id": 3,
-    "pathFragmentId": 3
-  },{
-    "id": 4,
-    "pathFragmentId": 4
-  },{
-    "id": 5,
-    "pathFragmentId": 10
-  },{
-    "id": 10,
-    "pathFragmentId": 20
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
-    "mnemonic": "TemplateExpand",
+    "mnemonic": "FileWrite",
     "configurationId": 1,
     "outputIds": [1],
     "primaryOutputId": 1,
     "executionPlatform": "//build/bazel/platforms:linux_x86_64",
-    "templateContent": "Test template substitutions: %token1%, %python_binary%",
-    "substitutions": [{
-      "key": "%token1%",
-      "value": "abcd"
-    },{
-      "key": "%python_binary%",
-      "value": "python3"
-    }]
-  },{
-    "targetId": 1,
-    "actionKey": "x",
-    "mnemonic": "PythonZipper",
-    "configurationId": 1,
-    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
-    "outputIds": [2],
-    "inputDepSetIds": [1],
-    "primaryOutputId": 2
+    "fileContents": "file data\n"
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [4, 3, 5]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "python_binary"
-  },{
-    "id": 2,
-    "label": "python_binary.zip"
-  },{
-    "id": 3,
-    "label": "python_binary.py"
-  },{
-    "id": 9,
-    "label": ".."
-  }, {
-    "id": 8,
-    "label": "bazel_tools",
-    "parentId": 9
-  }, {
-    "id": 7,
-    "label": "tools",
-    "parentId": 8
-  }, {
-    "id": 6,
-    "label": "zip",
-    "parentId": 7
-  }, {
-    "id": 5,
-    "label": "zipper",
-    "parentId": 6
-  }, {
-    "id": 4,
-    "label": "zipper",
-    "parentId": 5
-  },{
-    "id": 16,
-    "label": "bazel-out"
-  },{
-    "id": 15,
-    "label": "bazel_tools",
-    "parentId": 16
-  }, {
-    "id": 14,
-    "label": "k8-fastbuild",
-    "parentId": 15
-  }, {
-    "id": 13,
-    "label": "bin",
-    "parentId": 14
-  }, {
-    "id": 12,
-    "label": "tools",
-    "parentId": 13
-  }, {
-    "id": 11,
-    "label": "python",
-    "parentId": 12
-  }, {
-    "id": 10,
-    "label": "py3wrapper.sh",
-    "parentId": 11
-  },{
-    "id": 20,
-    "label": "python_binary"
-  }]
-}`
+  "pathFragments": [
+    { "id": 1, "label": "foo.manifest" }]
+}
+`
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
-
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
+	assertBuildStatements(t, []BuildStatement{
+		{
+			OutputPaths:  []string{"foo.manifest"},
+			Mnemonic:     "FileWrite",
+			FileContents: "file data\n",
+		},
+	}, actual)
+}
 
-	expectedBuildStatements := []BuildStatement{
-		BuildStatement{
-			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > python_binary && " +
-				"chmod a+x python_binary'",
-			InputPaths:  []string{"python_binary.zip"},
-			OutputPaths: []string{"python_binary"},
-			Mnemonic:    "TemplateExpand",
-		},
-		BuildStatement{
-			Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " +
-				"__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py  && " +
-				"../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles",
-			InputPaths:  []string{"../bazel_tools/tools/zip/zipper/zipper", "python_binary.py"},
-			OutputPaths: []string{"python_binary.zip"},
-			Mnemonic:    "PythonZipper",
-		},
+func TestSourceSymlinkManifest(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "SourceSymlinkManifest",
+    "configurationId": 1,
+    "outputIds": [1],
+    "primaryOutputId": 1,
+    "executionPlatform": "//build/bazel/platforms:linux_x86_64",
+    "fileContents": "symlink target\n"
+  }],
+  "pathFragments": [
+    { "id": 1, "label": "foo.manifest" }]
+}
+`
+	actual, _, err := AqueryBuildStatements([]byte(inputString))
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, expectedBuildStatements, actual)
-}
-
-func TestPythonZipperActionNoInput(t *testing.T) {
-	const inputString = `
-{
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  },{
-    "id": 2,
-    "pathFragmentId": 2
-  }],
-  "actions": [{
-    "targetId": 1,
-    "actionKey": "x",
-    "mnemonic": "PythonZipper",
-    "configurationId": 1,
-    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
-    "outputIds": [2],
-    "primaryOutputId": 2
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "python_binary"
-  },{
-    "id": 2,
-    "label": "python_binary.zip"
-  }]
-}`
-	_, _, err := AqueryBuildStatements([]byte(inputString))
-	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
-}
-
-func TestPythonZipperActionNoOutput(t *testing.T) {
-	const inputString = `
-{
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  },{
-    "id": 2,
-    "pathFragmentId": 2
-  },{
-    "id": 3,
-    "pathFragmentId": 3
-  },{
-    "id": 4,
-    "pathFragmentId": 4
-  },{
-    "id": 5,
-    "pathFragmentId": 10
-  }],
-  "actions": [{
-    "targetId": 1,
-    "actionKey": "x",
-    "mnemonic": "PythonZipper",
-    "configurationId": 1,
-    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
-    "inputDepSetIds": [1]
-  }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [4, 3, 5]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "python_binary"
-  },{
-    "id": 2,
-    "label": "python_binary.zip"
-  },{
-    "id": 3,
-    "label": "python_binary.py"
-  },{
-    "id": 9,
-    "label": ".."
-  }, {
-    "id": 8,
-    "label": "bazel_tools",
-    "parentId": 9
-  }, {
-    "id": 7,
-    "label": "tools",
-    "parentId": 8
-  }, {
-    "id": 6,
-    "label": "zip",
-    "parentId": 7
-  }, {
-    "id": 5,
-    "label": "zipper",
-    "parentId": 6
-  }, {
-    "id": 4,
-    "label": "zipper",
-    "parentId": 5
-  },{
-    "id": 16,
-    "label": "bazel-out"
-  },{
-    "id": 15,
-    "label": "bazel_tools",
-    "parentId": 16
-  }, {
-    "id": 14,
-    "label": "k8-fastbuild",
-    "parentId": 15
-  }, {
-    "id": 13,
-    "label": "bin",
-    "parentId": 14
-  }, {
-    "id": 12,
-    "label": "tools",
-    "parentId": 13
-  }, {
-    "id": 11,
-    "label": "python",
-    "parentId": 12
-  }, {
-    "id": 10,
-    "label": "py3wrapper.sh",
-    "parentId": 11
-  }]
-}`
-	_, _, err := AqueryBuildStatements([]byte(inputString))
-	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["../bazel_tools/tools/zip/zipper/zipper" "python_binary.py"], output []`)
+	assertBuildStatements(t, []BuildStatement{
+		{
+			OutputPaths: []string{"foo.manifest"},
+			Mnemonic:    "SourceSymlinkManifest",
+		},
+	}, actual)
 }
 
 func assertError(t *testing.T, err error, expected string) {
@@ -1484,50 +979,54 @@
 			len(expected), len(actual), expected, actual)
 		return
 	}
-ACTUAL_LOOP:
-	for _, actualStatement := range actual {
-		for _, expectedStatement := range expected {
-			if buildStatementEquals(actualStatement, expectedStatement) {
-				continue ACTUAL_LOOP
-			}
+	type compareFn = func(i int, j int) bool
+	byCommand := func(slice []BuildStatement) compareFn {
+		return func(i int, j int) bool {
+			return slice[i].Command < slice[j].Command
 		}
-		t.Errorf("unexpected build statement %#v.\n expected: %#v",
-			actualStatement, expected)
-		return
+	}
+	sort.SliceStable(expected, byCommand(expected))
+	sort.SliceStable(actual, byCommand(actual))
+	for i, actualStatement := range actual {
+		expectedStatement := expected[i]
+		if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
+			t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
+				differingField, actualStatement, expectedStatement)
+			return
+		}
 	}
 }
 
-func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
+func buildStatementEquals(first BuildStatement, second BuildStatement) string {
 	if first.Mnemonic != second.Mnemonic {
-		return false
+		return "Mnemonic"
 	}
 	if first.Command != second.Command {
-		return false
+		return "Command"
 	}
 	// Ordering is significant for environment variables.
 	if !reflect.DeepEqual(first.Env, second.Env) {
-		return false
+		return "Env"
 	}
 	// Ordering is irrelevant for input and output paths, so compare sets.
-	if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
-		return false
+	if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
+		return "InputPaths"
 	}
-	if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
-		return false
+	if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
+		return "OutputPaths"
 	}
-	if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
-		return false
+	if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
+		return "SymlinkPaths"
 	}
 	if first.Depfile != second.Depfile {
-		return false
+		return "Depfile"
 	}
-	return true
+	return ""
 }
 
-func stringSet(stringSlice []string) map[string]struct{} {
-	stringMap := make(map[string]struct{})
-	for _, s := range stringSlice {
-		stringMap[s] = struct{}{}
-	}
-	return stringMap
+func sortedStrings(stringSlice []string) []string {
+	sorted := make([]string, len(stringSlice))
+	copy(sorted, stringSlice)
+	sort.Strings(sorted)
+	return sorted
 }
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 7355ac7..0ab49eb 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -43,6 +43,8 @@
 	osArchDarwinX86_64      = "darwin_x86_64"
 	osArchLinuxX86          = "linux_glibc_x86"
 	osArchLinuxX86_64       = "linux_glibc_x86_64"
+	osArchLinuxMuslArm      = "linux_musl_arm"
+	osArchLinuxMuslArm64    = "linux_musl_arm64"
 	osArchLinuxMuslX86      = "linux_musl_x86"
 	osArchLinuxMuslX86_64   = "linux_musl_x86_64"
 	osArchLinuxBionicArm64  = "linux_bionic_arm64"
@@ -101,6 +103,8 @@
 		osArchDarwinX86_64:         "//build/bazel/platforms/os_arch:darwin_x86_64",
 		osArchLinuxX86:             "//build/bazel/platforms/os_arch:linux_glibc_x86",
 		osArchLinuxX86_64:          "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
+		osArchLinuxMuslArm:         "//build/bazel/platforms/os_arch:linux_musl_arm",
+		osArchLinuxMuslArm64:       "//build/bazel/platforms/os_arch:linux_musl_arm64",
 		osArchLinuxMuslX86:         "//build/bazel/platforms/os_arch:linux_musl_x86",
 		osArchLinuxMuslX86_64:      "//build/bazel/platforms/os_arch:linux_musl_x86_64",
 		osArchLinuxBionicArm64:     "//build/bazel/platforms/os_arch:linux_bionic_arm64",
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 5d00b0b..f5435f2 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -132,7 +132,7 @@
 sharedLibraries = []
 rootSharedLibraries = []
 
-shared_info_tag = "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
+shared_info_tag = "@_builtins//:common/cc/experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
 if shared_info_tag in providers(target):
   shared_info = providers(target)[shared_info_tag]
   for lib in shared_info.linker_input.libraries:
diff --git a/bazel/properties.go b/bazel/properties.go
index f956031..e29b9e1 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -409,6 +409,11 @@
 	return false
 }
 
+// SetValue sets value for the no config axis
+func (ba *BoolAttribute) SetValue(value *bool) {
+	ba.SetSelectValue(NoConfigAxis, "", value)
+}
+
 // SetSelectValue sets value for the given axis/config.
 func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
 	axis.validateConfig(config)
@@ -652,6 +657,11 @@
 	}
 }
 
+// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
+func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
+	return MakeLabelListAttribute(MakeLabelList([]Label{value}))
+}
+
 func (lla *LabelListAttribute) SetValue(list LabelList) {
 	lla.SetSelectValue(NoConfigAxis, "", list)
 }
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index e0ce194..34548ed 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -51,6 +51,7 @@
         "conversion_test.go",
         "filegroup_conversion_test.go",
         "genrule_conversion_test.go",
+        "gensrcs_conversion_test.go",
         "java_binary_host_conversion_test.go",
         "java_import_conversion_test.go",
         "java_library_conversion_test.go",
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
index 035a352..173b4e4 100644
--- a/bp2build/android_app_certificate_conversion_test.go
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -42,7 +42,7 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("android_app_certificate", "com.android.apogee.cert", attrNameToString{
+			makeBazelTargetNoRestrictions("android_app_certificate", "com.android.apogee.cert", attrNameToString{
 				"certificate": `"chamber_of_secrets_dir"`,
 			}),
 		}})
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index d15dc0c..7bc379f 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -18,6 +18,7 @@
 	"android/soong/android"
 	"android/soong/apex"
 	"android/soong/cc"
+	"android/soong/etc"
 	"android/soong/java"
 	"android/soong/sh"
 
@@ -39,6 +40,7 @@
 	ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
 	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
 }
 
 func runOverrideApexTestCase(t *testing.T, tc bp2buildTestCase) {
@@ -57,6 +59,7 @@
 	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("apex", apex.BundleFactory)
+	ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
 }
 
 func TestApexBundleSimple(t *testing.T) {
@@ -89,13 +92,13 @@
 	bazel_module: { bp2build_available: false },
 }
 
-cc_library {
-	name: "pretend_prebuilt_1",
+prebuilt_etc {
+	name: "prebuilt_1",
 	bazel_module: { bp2build_available: false },
 }
 
-cc_library {
-	name: "pretend_prebuilt_2",
+prebuilt_etc {
+	name: "prebuilt_2",
 	bazel_module: { bp2build_available: false },
 }
 
@@ -130,9 +133,11 @@
 		"sh_binary_2",
 	],
 	prebuilts: [
-	    "pretend_prebuilt_1",
-	    "pretend_prebuilt_2",
+	    "prebuilt_1",
+	    "prebuilt_2",
 	],
+	package_name: "com.android.apogee.test.package",
+	logging_parent: "logging.parent",
 }
 `,
 		expectedBazelTargets: []string{
@@ -164,11 +169,13 @@
         "//conditions:default": [],
     })`,
 				"prebuilts": `[
-        ":pretend_prebuilt_1",
-        ":pretend_prebuilt_2",
+        ":prebuilt_1",
+        ":prebuilt_2",
     ]`,
-				"updatable":    "False",
-				"compressible": "False",
+				"updatable":      "False",
+				"compressible":   "False",
+				"package_name":   `"com.android.apogee.test.package"`,
+				"logging_parent": `"logging.parent"`,
 			}),
 		}})
 }
@@ -547,13 +554,13 @@
 	bazel_module: { bp2build_available: false },
 }
 
-cc_library {
-	name: "pretend_prebuilt_1",
+prebuilt_etc {
+	name: "prebuilt_1",
 	bazel_module: { bp2build_available: false },
 }
 
-cc_library {
-	name: "pretend_prebuilt_2",
+prebuilt_etc {
+	name: "prebuilt_2",
 	bazel_module: { bp2build_available: false },
 }
 
@@ -588,8 +595,8 @@
 		"sh_binary_2",
 	],
 	prebuilts: [
-	    "pretend_prebuilt_1",
-	    "pretend_prebuilt_2",
+	    "prebuilt_1",
+	    "prebuilt_2",
 	],
 	bazel_module: { bp2build_available: false },
 }
@@ -782,3 +789,230 @@
 			}),
 		}})
 }
+
+func TestApexBundleSimple_packageNameOverride(t *testing.T) {
+	runOverrideApexTestCase(t, bp2buildTestCase{
+		description:                "override_apex - override package name",
+		moduleTypeUnderTest:        "override_apex",
+		moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+		filesystem: map[string]string{
+			"system/sepolicy/apex/Android.bp": `
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [ "apogee-file_contexts", ],
+	bazel_module: { bp2build_available: false },
+}`,
+		},
+		blueprint: `
+apex {
+	name: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	package_name: "com.google.android.apogee",
+}
+`,
+		expectedBazelTargets: []string{
+			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"`,
+			}),
+		}})
+}
+
+func TestApexBundleSimple_NoPrebuiltsOverride(t *testing.T) {
+	runOverrideApexTestCase(t, bp2buildTestCase{
+		description:                "override_apex - no override",
+		moduleTypeUnderTest:        "override_apex",
+		moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+		filesystem: map[string]string{
+			"system/sepolicy/apex/Android.bp": `
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [ "apogee-file_contexts", ],
+	bazel_module: { bp2build_available: false },
+}`,
+		},
+		blueprint: `
+prebuilt_etc {
+	name: "prebuilt_file",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+    prebuilts: ["prebuilt_file"]
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":      `"apex_manifest.json"`,
+				"prebuilts":     `[":prebuilt_file"]`,
+			}),
+		}})
+}
+
+func TestApexBundleSimple_PrebuiltsOverride(t *testing.T) {
+	runOverrideApexTestCase(t, bp2buildTestCase{
+		description:                "override_apex - ooverride",
+		moduleTypeUnderTest:        "override_apex",
+		moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+		filesystem: map[string]string{
+			"system/sepolicy/apex/Android.bp": `
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [ "apogee-file_contexts", ],
+	bazel_module: { bp2build_available: false },
+}`,
+		},
+		blueprint: `
+prebuilt_etc {
+	name: "prebuilt_file",
+	bazel_module: { bp2build_available: false },
+}
+
+prebuilt_etc {
+	name: "prebuilt_file2",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+    prebuilts: ["prebuilt_file"]
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+    prebuilts: ["prebuilt_file2"]
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":      `"apex_manifest.json"`,
+				"prebuilts":     `[":prebuilt_file2"]`,
+			}),
+		}})
+}
+
+func TestApexBundleSimple_PrebuiltsOverrideEmptyList(t *testing.T) {
+	runOverrideApexTestCase(t, bp2buildTestCase{
+		description:                "override_apex - override with empty list",
+		moduleTypeUnderTest:        "override_apex",
+		moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+		filesystem: map[string]string{
+			"system/sepolicy/apex/Android.bp": `
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [ "apogee-file_contexts", ],
+	bazel_module: { bp2build_available: false },
+}`,
+		},
+		blueprint: `
+prebuilt_etc {
+	name: "prebuilt_file",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+    prebuilts: ["prebuilt_file"]
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+    prebuilts: [],
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.google.android.apogee", attrNameToString{
+				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":      `"apex_manifest.json"`,
+				"prebuilts":     `[]`,
+			}),
+		}})
+}
+
+func TestApexBundleSimple_NoLoggingParentOverride(t *testing.T) {
+	runOverrideApexTestCase(t, bp2buildTestCase{
+		description:                "override_apex - logging_parent - no override",
+		moduleTypeUnderTest:        "override_apex",
+		moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+		filesystem: map[string]string{
+			"system/sepolicy/apex/Android.bp": `
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [ "apogee-file_contexts", ],
+	bazel_module: { bp2build_available: false },
+}`,
+		},
+		blueprint: `
+apex {
+	name: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+	logging_parent: "foo.bar.baz",
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+}
+`,
+		expectedBazelTargets: []string{
+			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"`,
+			}),
+		}})
+}
+
+func TestApexBundleSimple_LoggingParentOverride(t *testing.T) {
+	runOverrideApexTestCase(t, bp2buildTestCase{
+		description:                "override_apex - logging_parent - override",
+		moduleTypeUnderTest:        "override_apex",
+		moduleTypeUnderTestFactory: apex.OverrideApexFactory,
+		filesystem: map[string]string{
+			"system/sepolicy/apex/Android.bp": `
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [ "apogee-file_contexts", ],
+	bazel_module: { bp2build_available: false },
+}`,
+		},
+		blueprint: `
+apex {
+	name: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+	logging_parent: "foo.bar.baz",
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	logging_parent: "foo.bar.baz.override",
+}
+`,
+		expectedBazelTargets: []string{
+			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/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 1d949901..dfa96a2 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -42,7 +42,7 @@
         private_key: "com.android.apogee.pem",
 }
 `,
-		expectedBazelTargets: []string{makeBazelTarget("apex_key", "com.android.apogee.key", attrNameToString{
+		expectedBazelTargets: []string{makeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", attrNameToString{
 			"private_key": `"com.android.apogee.pem"`,
 			"public_key":  `"com.android.apogee.avbpubkey"`,
 		}),
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 0f6e139..19209f6 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -201,7 +201,7 @@
 			config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 			ctx := android.NewTestContext(config)
 
-			ctx.RegisterModuleType("custom", customModuleFactory)
+			ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 			ctx.Register()
 
 			_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -501,6 +501,215 @@
 	}
 }
 
+func TestBp2buildHostAndDevice(t *testing.T) {
+	testCases := []bp2buildTestCase{
+		{
+			description:                "host and device, device only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device, both",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: true,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{}),
+			},
+		},
+		{
+			description:                "host and device, host explicitly disabled",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device, neither",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, neither, cannot override with product_var",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		product_variables: { unbundled_build: { enabled: true } },
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, both, disabled overrided with product_var",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: true,
+		device_supported: true,
+		enabled: false,
+		product_variables: { unbundled_build: { enabled: true } },
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["//build/bazel/product_variables:unbundled_build"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, neither, cannot override with arch enabled",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		arch: { x86: { enabled: true } },
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, host only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: true,
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+			},
+		},
+		{
+			description:                "host only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostSupported,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+			},
+		},
+		{
+			description:                "device only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryDeviceSupported,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device default, default",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{}),
+			},
+		},
+		{
+			description:                "host and device default, device only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device default, host only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+			},
+		},
+		{
+			description:                "host and device default, neither",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			runBp2BuildTestCaseSimple(t, tc)
+		})
+	}
+}
+
 func TestLoadStatements(t *testing.T) {
 	testCases := []struct {
 		bazelTargets           BazelTargets
@@ -610,6 +819,7 @@
 		{
 			bp: `custom {
     name: "bar",
+    host_supported: true,
     one_to_many_prop: true,
     bazel_module: { bp2build_available: true  },
 }`,
@@ -634,7 +844,7 @@
 	for _, testCase := range testCases {
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType("custom", customModuleFactory)
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -680,7 +890,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
 			},
 		},
 		{
@@ -693,7 +903,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
 			},
 		},
 		{
@@ -706,7 +916,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a",
         "b",
@@ -725,7 +935,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `["b"]`,
 				}),
 			},
@@ -740,7 +950,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "other/a.txt",
         "other/b.txt",
@@ -772,7 +982,7 @@
 				"other/file":         "",
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a.txt",
         "b.txt",
@@ -801,7 +1011,7 @@
 }`,
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "//other:foo",
         "c",
@@ -884,7 +1094,7 @@
 		{
 			description:                "generates more than 1 target if needed",
 			moduleTypeUnderTest:        "custom",
-			moduleTypeUnderTestFactory: customModuleFactory,
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
 			bp: `custom {
     name: "foo",
     one_to_many_prop: true,
@@ -1092,7 +1302,7 @@
 				"other/BUILD.bazel": `// definition for fg_bar`,
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
 				`// definition for fg_bar`,
 			},
 		},
@@ -1118,7 +1328,7 @@
         },
     }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_bar", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_bar", map[string]string{}),
 				`// BUILD file`,
 			},
 		},
@@ -1203,7 +1413,7 @@
 				"dir/f.txt":      "",
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a.txt",
         "b.txt",
@@ -1234,7 +1444,7 @@
 				"dir/subdir/f.txt":      "",
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a.txt",
         "//dir/subdir:e.txt",
@@ -1265,7 +1475,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"data": `[":reqd"]`,
 				}),
 			},
@@ -1337,7 +1547,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"data": `[":reqd"]`,
 				}),
 			},
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 037564b..4794269 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -34,10 +34,11 @@
 	attrs attrNameToString
 }
 
-func generateBazelTargetsForTest(targets []testBazelTarget) []string {
+func generateBazelTargetsForTest(targets []testBazelTarget, hod android.HostOrDeviceSupported) []string {
 	ret := make([]string, 0, len(targets))
 	for _, t := range targets {
-		ret = append(ret, makeBazelTarget(t.typ, t.name, t.attrs))
+		attrs := t.attrs.clone()
+		ret = append(ret, makeBazelTargetHostOrDevice(t.typ, t.name, attrs, hod))
 	}
 	return ret
 }
@@ -65,42 +66,33 @@
 	runCcHostBinaryTestCase(t, tc)
 }
 
-func runCcBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
 	t.Helper()
 	moduleTypeUnderTest := "cc_binary"
-	testCase := bp2buildTestCase{
-		expectedBazelTargets:       generateBazelTargetsForTest(tc.targets),
-		moduleTypeUnderTest:        moduleTypeUnderTest,
-		moduleTypeUnderTestFactory: cc.BinaryFactory,
-		description:                fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
-		blueprint:                  binaryReplacer.Replace(tc.blueprint),
-	}
-	t.Run(testCase.description, func(t *testing.T) {
+
+	description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+	t.Run(description, func(t *testing.T) {
 		t.Helper()
-		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
+		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
+			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets, android.DeviceSupported),
+			moduleTypeUnderTest:        moduleTypeUnderTest,
+			moduleTypeUnderTestFactory: cc.BinaryFactory,
+			description:                description,
+			blueprint:                  binaryReplacer.Replace(testCase.blueprint),
+		})
 	})
 }
 
-func runCcHostBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcHostBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
 	t.Helper()
-	testCase := tc
-	for i, tar := range testCase.targets {
-		switch tar.typ {
-		case "cc_binary", "proto_library", "cc_lite_proto_library":
-			tar.attrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-		testCase.targets[i] = tar
-	}
 	moduleTypeUnderTest := "cc_binary_host"
-	t.Run(testCase.description, func(t *testing.T) {
+	description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+	t.Run(description, func(t *testing.T) {
 		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
-			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets),
+			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets, android.HostSupported),
 			moduleTypeUnderTest:        moduleTypeUnderTest,
 			moduleTypeUnderTestFactory: cc.BinaryHostFactory,
-			description:                fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
+			description:                description,
 			blueprint:                  hostBinaryReplacer.Replace(testCase.blueprint),
 		})
 	})
@@ -505,3 +497,49 @@
 		},
 	})
 }
+
+func TestCcBinaryConvertLex(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: `.l and .ll sources converted to .c and .cc`,
+		blueprint: `
+{rule_name} {
+    name: "foo",
+		srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+		lex: { flags: ["--foo_opt", "--bar_opt"] },
+		include_build_directory: false,
+}
+`,
+		targets: []testBazelTarget{
+			{"genlex", "foo_genlex_l", attrNameToString{
+				"srcs": `[
+        "foo1.l",
+        "foo2.l",
+    ]`,
+				"lexopts": `[
+        "--foo_opt",
+        "--bar_opt",
+    ]`,
+			}},
+			{"genlex", "foo_genlex_ll", attrNameToString{
+				"srcs": `[
+        "bar1.ll",
+        "bar2.ll",
+    ]`,
+				"lexopts": `[
+        "--foo_opt",
+        "--bar_opt",
+    ]`,
+			}},
+			{"cc_binary", "foo", attrNameToString{
+				"srcs": `[
+        "bar.cc",
+        ":foo_genlex_ll",
+    ]`,
+				"srcs_c": `[
+        "foo.c",
+        ":foo_genlex_l",
+    ]`,
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index ab92981..2cc2207 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -959,11 +959,12 @@
 		"features": `[
         "disable_pack_relocations",
         "-no_undefined_symbols",
+        "-coverage",
     ]`,
 		"srcs": `["a.cpp"]`,
 	})...)
 	expected_targets = append(expected_targets, makeCcLibraryTargets("b", attrNameToString{
-		"features": `select({
+		"features": `["-coverage"] + select({
         "//build/bazel/platforms/arch:x86_64": [
             "disable_pack_relocations",
             "-no_undefined_symbols",
@@ -994,6 +995,7 @@
     pack_relocations: false,
     allow_undefined_symbols: true,
     include_build_directory: false,
+    native_coverage: false,
 }
 
 cc_library {
@@ -1006,6 +1008,7 @@
         },
     },
     include_build_directory: false,
+    native_coverage: false,
 }
 
 cc_library {
@@ -1879,76 +1882,78 @@
 		// not set, only emit if gnu_extensions is disabled. the default (gnu+17
 		// is set in the toolchain.)
 		{cpp_std: "", gnu_extensions: "", bazel_cpp_std: ""},
-		{cpp_std: "", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c99"},
+		{cpp_std: "", gnu_extensions: "false", bazel_cpp_std: "cpp_std_default_no_gnu", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "", gnu_extensions: "true", bazel_cpp_std: ""},
 		// experimental defaults to gnu++2a
-		{cpp_std: "experimental", gnu_extensions: "", bazel_cpp_std: "gnu++2a"},
-		{cpp_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c99"},
-		{cpp_std: "experimental", gnu_extensions: "true", bazel_cpp_std: "gnu++2a"},
+		{cpp_std: "experimental", gnu_extensions: "", bazel_cpp_std: "cpp_std_experimental"},
+		{cpp_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "cpp_std_experimental_no_gnu", bazel_c_std: "c_std_default_no_gnu"},
+		{cpp_std: "experimental", gnu_extensions: "true", bazel_cpp_std: "cpp_std_experimental"},
 		// Explicitly setting a c++ std does not use replace gnu++ std even if
 		// gnu_extensions is true.
 		// "c++11",
 		{cpp_std: "c++11", gnu_extensions: "", bazel_cpp_std: "c++11"},
-		{cpp_std: "c++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c99"},
+		{cpp_std: "c++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "c++11", gnu_extensions: "true", bazel_cpp_std: "c++11"},
 		// "c++17",
 		{cpp_std: "c++17", gnu_extensions: "", bazel_cpp_std: "c++17"},
-		{cpp_std: "c++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c99"},
+		{cpp_std: "c++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "c++17", gnu_extensions: "true", bazel_cpp_std: "c++17"},
 		// "c++2a",
 		{cpp_std: "c++2a", gnu_extensions: "", bazel_cpp_std: "c++2a"},
-		{cpp_std: "c++2a", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c99"},
+		{cpp_std: "c++2a", gnu_extensions: "false", bazel_cpp_std: "c++2a", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "c++2a", gnu_extensions: "true", bazel_cpp_std: "c++2a"},
 		// "c++98",
 		{cpp_std: "c++98", gnu_extensions: "", bazel_cpp_std: "c++98"},
-		{cpp_std: "c++98", gnu_extensions: "false", bazel_cpp_std: "c++98", bazel_c_std: "c99"},
+		{cpp_std: "c++98", gnu_extensions: "false", bazel_cpp_std: "c++98", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "c++98", gnu_extensions: "true", bazel_cpp_std: "c++98"},
 		// gnu++ is replaced with c++ if gnu_extensions is explicitly false.
 		// "gnu++11",
 		{cpp_std: "gnu++11", gnu_extensions: "", bazel_cpp_std: "gnu++11"},
-		{cpp_std: "gnu++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c99"},
+		{cpp_std: "gnu++11", gnu_extensions: "false", bazel_cpp_std: "c++11", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "gnu++11", gnu_extensions: "true", bazel_cpp_std: "gnu++11"},
 		// "gnu++17",
 		{cpp_std: "gnu++17", gnu_extensions: "", bazel_cpp_std: "gnu++17"},
-		{cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c99"},
+		{cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c_std_default_no_gnu"},
 		{cpp_std: "gnu++17", gnu_extensions: "true", bazel_cpp_std: "gnu++17"},
 
 		// some c_std test cases
-		{c_std: "experimental", gnu_extensions: "", bazel_c_std: "gnu17"},
-		{c_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c17"},
-		{c_std: "experimental", gnu_extensions: "true", bazel_c_std: "gnu17"},
+		{c_std: "experimental", gnu_extensions: "", bazel_c_std: "c_std_experimental"},
+		{c_std: "experimental", gnu_extensions: "false", bazel_cpp_std: "cpp_std_default_no_gnu", bazel_c_std: "c_std_experimental_no_gnu"},
+		{c_std: "experimental", gnu_extensions: "true", bazel_c_std: "c_std_experimental"},
 		{c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "", bazel_cpp_std: "gnu++17", bazel_c_std: "gnu11"},
 		{c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "false", bazel_cpp_std: "c++17", bazel_c_std: "c11"},
 		{c_std: "gnu11", cpp_std: "gnu++17", gnu_extensions: "true", bazel_cpp_std: "gnu++17", bazel_c_std: "gnu11"},
 	}
 	for i, tc := range testCases {
-		name_prefix := fmt.Sprintf("a_%v", i)
-		cppStdProp := ""
-		if tc.cpp_std != "" {
-			cppStdProp = fmt.Sprintf("    cpp_std: \"%s\",", tc.cpp_std)
-		}
-		cStdProp := ""
-		if tc.c_std != "" {
-			cStdProp = fmt.Sprintf("    c_std: \"%s\",", tc.c_std)
-		}
-		gnuExtensionsProp := ""
-		if tc.gnu_extensions != "" {
-			gnuExtensionsProp = fmt.Sprintf("    gnu_extensions: %s,", tc.gnu_extensions)
-		}
-		attrs := attrNameToString{}
-		if tc.bazel_cpp_std != "" {
-			attrs["cpp_std"] = fmt.Sprintf(`"%s"`, tc.bazel_cpp_std)
-		}
-		if tc.bazel_c_std != "" {
-			attrs["c_std"] = fmt.Sprintf(`"%s"`, tc.bazel_c_std)
-		}
+		name := fmt.Sprintf("cpp std: %q, c std: %q, gnu_extensions: %q", tc.cpp_std, tc.c_std, tc.gnu_extensions)
+		t.Run(name, func(t *testing.T) {
+			name_prefix := fmt.Sprintf("a_%v", i)
+			cppStdProp := ""
+			if tc.cpp_std != "" {
+				cppStdProp = fmt.Sprintf("    cpp_std: \"%s\",", tc.cpp_std)
+			}
+			cStdProp := ""
+			if tc.c_std != "" {
+				cStdProp = fmt.Sprintf("    c_std: \"%s\",", tc.c_std)
+			}
+			gnuExtensionsProp := ""
+			if tc.gnu_extensions != "" {
+				gnuExtensionsProp = fmt.Sprintf("    gnu_extensions: %s,", tc.gnu_extensions)
+			}
+			attrs := attrNameToString{}
+			if tc.bazel_cpp_std != "" {
+				attrs["cpp_std"] = fmt.Sprintf(`"%s"`, tc.bazel_cpp_std)
+			}
+			if tc.bazel_c_std != "" {
+				attrs["c_std"] = fmt.Sprintf(`"%s"`, tc.bazel_c_std)
+			}
 
-		runCcLibraryTestCase(t, bp2buildTestCase{
-			description: fmt.Sprintf(
-				"cc_library with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
-			moduleTypeUnderTest:        "cc_library",
-			moduleTypeUnderTestFactory: cc.LibraryFactory,
-			blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
+			runCcLibraryTestCase(t, bp2buildTestCase{
+				description: fmt.Sprintf(
+					"cc_library with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+				moduleTypeUnderTest:        "cc_library",
+				moduleTypeUnderTestFactory: cc.LibraryFactory,
+				blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
 cc_library {
 	name: "%s_full",
 %s // cpp_std: *string
@@ -1957,15 +1962,15 @@
 	include_build_directory: false,
 }
 `, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
-			expectedBazelTargets: makeCcLibraryTargets(name_prefix+"_full", attrs),
-		})
+				expectedBazelTargets: makeCcLibraryTargets(name_prefix+"_full", attrs),
+			})
 
-		runCcLibraryStaticTestCase(t, bp2buildTestCase{
-			description: fmt.Sprintf(
-				"cc_library_static with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
-			moduleTypeUnderTest:        "cc_library_static",
-			moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
-			blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
+			runCcLibraryStaticTestCase(t, bp2buildTestCase{
+				description: fmt.Sprintf(
+					"cc_library_static with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+				moduleTypeUnderTest:        "cc_library_static",
+				moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+				blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
 cc_library_static {
 	name: "%s_static",
 %s // cpp_std: *string
@@ -1974,17 +1979,17 @@
 	include_build_directory: false,
 }
 `, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
-			expectedBazelTargets: []string{
-				makeBazelTarget("cc_library_static", name_prefix+"_static", attrs),
-			},
-		})
+				expectedBazelTargets: []string{
+					makeBazelTarget("cc_library_static", name_prefix+"_static", attrs),
+				},
+			})
 
-		runCcLibrarySharedTestCase(t, bp2buildTestCase{
-			description: fmt.Sprintf(
-				"cc_library_shared with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
-			moduleTypeUnderTest:        "cc_library_shared",
-			moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
-			blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
+			runCcLibrarySharedTestCase(t, bp2buildTestCase{
+				description: fmt.Sprintf(
+					"cc_library_shared with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+				moduleTypeUnderTest:        "cc_library_shared",
+				moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
+				blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
 cc_library_shared {
 	name: "%s_shared",
 %s // cpp_std: *string
@@ -1993,9 +1998,10 @@
 	include_build_directory: false,
 }
 `, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
-			expectedBazelTargets: []string{
-				makeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs),
-			},
+				expectedBazelTargets: []string{
+					makeBazelTarget("cc_library_shared", name_prefix+"_shared", attrs),
+				},
+			})
 		})
 	}
 }
@@ -2278,6 +2284,7 @@
 		blueprint: soongCcProtoPreamble + `cc_library {
 	name: "foo",
 	srcs: ["foo.cpp"],
+	host_supported: true,
 	target: {
 		darwin: {
 			enabled: false,
@@ -2313,6 +2320,7 @@
 	name: "foo",
 	srcs: ["foo.cpp"],
   enabled: false,
+	host_supported: true,
 	target: {
 		darwin: {
 			enabled: true,
@@ -2377,6 +2385,7 @@
 		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcProtoPreamble + `cc_library {
 	name: "foo",
+	host_supported: true,
 	srcs: ["foo.cpp"],
 	shared: {
 		enabled: false
@@ -2457,3 +2466,51 @@
 		}),
 	})
 }
+
+func TestCcLibraryConvertLex(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem: map[string]string{
+			"foo.c":   "",
+			"bar.cc":  "",
+			"foo1.l":  "",
+			"bar1.ll": "",
+			"foo2.l":  "",
+			"bar2.ll": "",
+		},
+		blueprint: `cc_library {
+	name: "foo_lib",
+	srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+	lex: { flags: ["--foo_flags"] },
+	include_build_directory: false,
+	bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: append([]string{
+			makeBazelTarget("genlex", "foo_lib_genlex_l", attrNameToString{
+				"srcs": `[
+        "foo1.l",
+        "foo2.l",
+    ]`,
+				"lexopts": `["--foo_flags"]`,
+			}),
+			makeBazelTarget("genlex", "foo_lib_genlex_ll", attrNameToString{
+				"srcs": `[
+        "bar1.ll",
+        "bar2.ll",
+    ]`,
+				"lexopts": `["--foo_flags"]`,
+			}),
+		},
+			makeCcLibraryTargets("foo_lib", attrNameToString{
+				"srcs": `[
+        "bar.cc",
+        ":foo_lib_genlex_ll",
+    ]`,
+				"srcs_c": `[
+        "foo.c",
+        ":foo_lib_genlex_l",
+    ]`,
+			})...),
+	})
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index e5bb120..641984b 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -84,18 +84,6 @@
 		},
 		blueprint: soongCcLibraryHeadersPreamble + `
 cc_library_headers {
-    name: "lib-1",
-    export_include_dirs: ["lib-1"],
-    bazel_module: { bp2build_available: false },
-}
-
-cc_library_headers {
-    name: "lib-2",
-    export_include_dirs: ["lib-2"],
-    bazel_module: { bp2build_available: false },
-}
-
-cc_library_headers {
     name: "foo_headers",
     export_include_dirs: ["dir-1", "dir-2"],
     header_libs: ["lib-1", "lib-2"],
@@ -128,12 +116,8 @@
         "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"],
         "//conditions:default": [],
     })`,
-				"implementation_deps": `[
-        ":lib-1",
-        ":lib-2",
-    ]`,
-        "sdk_version": `"current"`,
-        "min_sdk_version": `"29"`,
+				"sdk_version":     `"current"`,
+				"min_sdk_version": `"29"`,
 			}),
 		},
 	})
@@ -173,18 +157,34 @@
 cc_library_headers {
     name: "foo_headers",
     header_libs: ["base-lib"],
+		export_header_lib_headers: ["base-lib"],
     target: {
-        android: { header_libs: ["android-lib"] },
-        darwin: { header_libs: ["darwin-lib"] },
-        linux_bionic: { header_libs: ["linux_bionic-lib"] },
-        linux_glibc: { header_libs: ["linux-lib"] },
-        windows: { header_libs: ["windows-lib"] },
+        android: {
+						header_libs: ["android-lib"],
+						export_header_lib_headers: ["android-lib"],
+				},
+        darwin: {
+						header_libs: ["darwin-lib"],
+						export_header_lib_headers: ["darwin-lib"],
+				},
+        linux_bionic: {
+						header_libs: ["linux_bionic-lib"],
+						export_header_lib_headers: ["linux_bionic-lib"],
+				},
+        linux_glibc: {
+						header_libs: ["linux-lib"],
+						export_header_lib_headers: ["linux-lib"],
+				},
+        windows: {
+						header_libs: ["windows-lib"],
+						export_header_lib_headers: ["windows-lib"],
+				},
     },
     include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
-				"implementation_deps": `[":base-lib"] + select({
+				"deps": `[":base-lib"] + select({
         "//build/bazel/platforms/os:android": [":android-lib"],
         "//build/bazel/platforms/os:darwin": [":darwin-lib"],
         "//build/bazel/platforms/os:linux": [":linux-lib"],
@@ -228,10 +228,6 @@
         "//build/bazel/platforms/os:android": [":exported-lib"],
         "//conditions:default": [],
     })`,
-				"implementation_deps": `select({
-        "//build/bazel/platforms/os:android": [":android-lib"],
-        "//conditions:default": [],
-    })`,
 			}),
 		},
 	})
@@ -328,3 +324,69 @@
 		},
 	})
 }
+
+func TestCcLibraryHeadersExportedStaticLibHeadersReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                "cc_library_headers exported_static_lib_headers is reexported",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		export_static_lib_headers: ["foo_export"],
+		static_libs: ["foo_export", "foo_no_reexport"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHeadersExportedSharedLibHeadersReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                "cc_library_headers exported_shared_lib_headers is reexported",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		export_shared_lib_headers: ["foo_export"],
+		shared_libs: ["foo_export", "foo_no_reexport"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHeadersExportedHeaderLibHeadersReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                "cc_library_headers exported_header_lib_headers is reexported",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		export_header_lib_headers: ["foo_export"],
+		header_libs: ["foo_export", "foo_no_reexport"],
+    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 7c2c100..be09616 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -520,3 +520,52 @@
 		})},
 	})
 }
+
+func TestCcLibrarySharedConvertLex(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                "cc_library_shared with lex files",
+		moduleTypeUnderTest:        "cc_library_shared",
+		moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		filesystem: map[string]string{
+			"foo.c":   "",
+			"bar.cc":  "",
+			"foo1.l":  "",
+			"bar1.ll": "",
+			"foo2.l":  "",
+			"bar2.ll": "",
+		},
+		blueprint: `cc_library_shared {
+	name: "foo_lib",
+	srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+	lex: { flags: ["--foo_flags"] },
+	include_build_directory: false,
+	bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("genlex", "foo_lib_genlex_l", attrNameToString{
+				"srcs": `[
+        "foo1.l",
+        "foo2.l",
+    ]`,
+				"lexopts": `["--foo_flags"]`,
+			}),
+			makeBazelTarget("genlex", "foo_lib_genlex_ll", attrNameToString{
+				"srcs": `[
+        "bar1.ll",
+        "bar2.ll",
+    ]`,
+				"lexopts": `["--foo_flags"]`,
+			}),
+			makeBazelTarget("cc_library_shared", "foo_lib", attrNameToString{
+				"srcs": `[
+        "bar.cc",
+        ":foo_lib_genlex_ll",
+    ]`,
+				"srcs_c": `[
+        "foo.c",
+        ":foo_lib_genlex_l",
+    ]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index be10e86..36c46a4 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -18,6 +18,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/genrule"
+	"fmt"
 
 	"testing"
 )
@@ -204,8 +205,8 @@
         ":whole_static_lib_1",
         ":whole_static_lib_2",
     ]`,
-        "sdk_version": `"current"`,
-        "min_sdk_version": `"29"`,
+				"sdk_version":     `"current"`,
+				"min_sdk_version": `"29"`,
 			}),
 		},
 	})
@@ -1489,3 +1490,72 @@
 		},
 	})
 }
+
+func TestCcLibraryStaticStl(t *testing.T) {
+	testCases := []struct {
+		desc string
+		prop string
+		attr attrNameToString
+	}{
+		{
+			desc: "c++_shared deduped to libc++",
+			prop: `stl: "c++_shared",`,
+			attr: attrNameToString{
+				"stl": `"libc++"`,
+			},
+		},
+		{
+			desc: "libc++ to libc++",
+			prop: `stl: "libc++",`,
+			attr: attrNameToString{
+				"stl": `"libc++"`,
+			},
+		},
+		{
+			desc: "c++_static to libc++_static",
+			prop: `stl: "c++_static",`,
+			attr: attrNameToString{
+				"stl": `"libc++_static"`,
+			},
+		},
+		{
+			desc: "libc++_static to libc++_static",
+			prop: `stl: "libc++_static",`,
+			attr: attrNameToString{
+				"stl": `"libc++_static"`,
+			},
+		},
+		{
+			desc: "system to system",
+			prop: `stl: "system",`,
+			attr: attrNameToString{
+				"stl": `"system"`,
+			},
+		},
+		{
+			desc: "none to none",
+			prop: `stl: "none",`,
+			attr: attrNameToString{
+				"stl": `"none"`,
+			},
+		},
+		{
+			desc: "empty to empty",
+			attr: attrNameToString{},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(*testing.T) {
+			runCcLibraryStaticTestCase(t, bp2buildTestCase{
+				blueprint: fmt.Sprintf(`cc_library_static {
+	name: "foo",
+	include_build_directory: false,
+	%s
+}`, tc.prop),
+				expectedBazelTargets: []string{
+					makeBazelTarget("cc_library_static", "foo", tc.attr),
+				},
+			})
+		})
+	}
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index ea58086..52688d9 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -414,6 +414,7 @@
             "bionic_arm64.cpp",
         ],
         "//build/bazel/platforms/os_arch:linux_glibc_x86": ["linux_x86.cpp"],
+        "//build/bazel/platforms/os_arch:linux_musl_arm64": ["linux_arm64.cpp"],
         "//build/bazel/platforms/os_arch:linux_musl_x86": ["linux_x86.cpp"],
         "//conditions:default": [],
     })`,
diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go
index 3feb1f1..59839c8 100644
--- a/bp2build/cc_prebuilt_library_static_test.go
+++ b/bp2build/cc_prebuilt_library_static_test.go
@@ -96,3 +96,52 @@
 			expectedErr: fmt.Errorf("Expected at most one source file"),
 		})
 }
+
+func TestCcLibraryStaticConvertLex(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                "cc_library_static with lex files",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		filesystem: map[string]string{
+			"foo.c":   "",
+			"bar.cc":  "",
+			"foo1.l":  "",
+			"bar1.ll": "",
+			"foo2.l":  "",
+			"bar2.ll": "",
+		},
+		blueprint: `cc_library_static {
+	name: "foo_lib",
+	srcs: ["foo.c", "bar.cc", "foo1.l", "foo2.l", "bar1.ll", "bar2.ll"],
+	lex: { flags: ["--foo_flags"] },
+	include_build_directory: false,
+	bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("genlex", "foo_lib_genlex_l", attrNameToString{
+				"srcs": `[
+        "foo1.l",
+        "foo2.l",
+    ]`,
+				"lexopts": `["--foo_flags"]`,
+			}),
+			makeBazelTarget("genlex", "foo_lib_genlex_ll", attrNameToString{
+				"srcs": `[
+        "bar1.ll",
+        "bar2.ll",
+    ]`,
+				"lexopts": `["--foo_flags"]`,
+			}),
+			makeBazelTarget("cc_library_static", "foo_lib", attrNameToString{
+				"srcs": `[
+        "bar.cc",
+        ":foo_lib_genlex_ll",
+    ]`,
+				"srcs_c": `[
+        "foo.c",
+        ":foo_lib_genlex_l",
+    ]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 9244b99..4504892 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -56,6 +56,7 @@
 		moduleType string
 		factory    android.ModuleFactory
 		genDir     string
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -66,16 +67,19 @@
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
 			genDir:     "$(RULEDIR)",
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
 			genDir:     "$(RULEDIR)",
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
 			genDir:     "$(RULEDIR)",
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -104,15 +108,8 @@
 			"tools": `[":foo.tool"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -131,6 +128,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -139,14 +137,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -183,18 +184,9 @@
 			"srcs": `["foo_tool.in"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			compatibilityAttrs := `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-			fooAttrs["target_compatible_with"] = compatibilityAttrs
-			fooToolsAttrs["target_compatible_with"] = compatibilityAttrs
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", fooAttrs),
-			makeBazelTarget("genrule", "foo.tools", fooToolsAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", fooAttrs, tc.hod),
+			makeBazelTargetHostOrDevice("genrule", "foo.tools", fooToolsAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -213,6 +205,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -221,14 +214,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -249,15 +245,8 @@
 			"tools": `["//other:foo.tool"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -277,6 +266,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -285,14 +275,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -313,15 +306,8 @@
 			"tools": `["//other:foo.tool"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -341,6 +327,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -349,14 +336,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -380,15 +370,8 @@
     ]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -408,6 +391,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -416,14 +400,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -447,15 +434,8 @@
     ]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -475,6 +455,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -483,14 +464,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -509,15 +493,8 @@
 			"srcs": `["foo.in"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -549,7 +526,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd":  `"do-something $(SRCS) $(OUTS)"`,
 					"outs": `["out"]`,
 					"srcs": `["in1"]`,
@@ -574,7 +551,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd": `"do-something $(SRCS) $(OUTS)"`,
 					"outs": `[
         "out-from-defaults",
@@ -607,7 +584,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd":  `"cp $(SRCS) $(OUTS)"`,
 					"outs": `["out"]`,
 					"srcs": `["in1"]`,
@@ -644,7 +621,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd": `"cmd1 $(SRCS) $(OUTS)"`,
 					"outs": `[
         "out-from-3",
diff --git a/bp2build/gensrcs_conversion_test.go b/bp2build/gensrcs_conversion_test.go
new file mode 100644
index 0000000..7682663
--- /dev/null
+++ b/bp2build/gensrcs_conversion_test.go
@@ -0,0 +1,80 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/genrule"
+	"testing"
+)
+
+func TestGensrcs(t *testing.T) {
+	testcases := []struct {
+		name               string
+		bp                 string
+		expectedBazelAttrs attrNameToString
+	}{
+		{
+			name: "gensrcs with common usage of properties",
+			bp: `
+			gensrcs {
+                name: "foo",
+                srcs: ["test/input.txt", ":external_files"],
+                tool_files: ["program.py"],
+                cmd: "$(location program.py) $(in) $(out)",
+                output_extension: "out",
+                bazel_module: { bp2build_available: true },
+			}`,
+			expectedBazelAttrs: attrNameToString{
+				"srcs": `[
+        "test/input.txt",
+        ":external_files__BP2BUILD__MISSING__DEP",
+    ]`,
+				"tools":            `["program.py"]`,
+				"output_extension": `"out"`,
+				"cmd":              `"$(location program.py) $(SRC) $(OUT)"`,
+			},
+		},
+		{
+			name: "gensrcs with out_extension unset",
+			bp: `
+			gensrcs {
+                name: "foo",
+                srcs: ["input.txt"],
+                cmd: "cat $(in) > $(out)",
+                bazel_module: { bp2build_available: true },
+			}`,
+			expectedBazelAttrs: attrNameToString{
+				"srcs": `["input.txt"]`,
+				"cmd":  `"cat $(SRC) > $(OUT)"`,
+			},
+		},
+	}
+
+	for _, test := range testcases {
+		expectedBazelTargets := []string{
+			makeBazelTargetNoRestrictions("gensrcs", "foo", test.expectedBazelAttrs),
+		}
+		t.Run(test.name, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        "gensrcs",
+					moduleTypeUnderTestFactory: genrule.GenSrcsFactory,
+					blueprint:                  test.bp,
+					expectedBazelTargets:       expectedBazelTargets,
+				})
+		})
+	}
+}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index dfa11d1..22bd028 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -43,9 +43,10 @@
     }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("py_binary", "foo", attrNameToString{
-				"data": `["files/data.txt"]`,
-				"deps": `[":bar"]`,
-				"main": `"a.py"`,
+				"data":    `["files/data.txt"]`,
+				"deps":    `[":bar"]`,
+				"main":    `"a.py"`,
+				"imports": `["."]`,
 				"srcs": `[
         "a.py",
         "b/c.py",
@@ -83,6 +84,7 @@
 		expectedBazelTargets: []string{
 			makeBazelTarget("py_binary", "foo", attrNameToString{
 				"python_version": `"PY2"`,
+				"imports":        `["."]`,
 				"srcs":           `["a.py"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
@@ -116,7 +118,8 @@
 		expectedBazelTargets: []string{
 			// python_version is PY3 by default.
 			makeBazelTarget("py_binary", "foo", attrNameToString{
-				"srcs": `["a.py"]`,
+				"imports": `["."]`,
+				"srcs":    `["a.py"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
@@ -148,6 +151,7 @@
 				 }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("py_binary", "foo-arm", attrNameToString{
+				"imports": `["."]`,
 				"srcs": `select({
         "//build/bazel/platforms/arch:arm": ["arm.py"],
         "//build/bazel/platforms/arch:x86": ["x86.py"],
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
index 66c2290..f51f106 100644
--- a/bp2build/python_library_conversion_test.go
+++ b/bp2build/python_library_conversion_test.go
@@ -305,3 +305,46 @@
 		},
 	})
 }
+
+func TestPythonLibraryWithProtobufs(t *testing.T) {
+	runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
+		description: "test %s protobuf",
+		filesystem: map[string]string{
+			"dir/mylib.py":      "",
+			"dir/myproto.proto": "",
+		},
+		blueprint: `%s {
+					 name: "foo",
+					 srcs: [
+						"dir/mylib.py",
+						"dir/myproto.proto",
+					 ],
+				 }`,
+		expectedBazelTargets: []testBazelTarget{
+			{
+				typ:  "proto_library",
+				name: "foo_proto",
+				attrs: attrNameToString{
+					"srcs": `["dir/myproto.proto"]`,
+				},
+			},
+			{
+				typ:  "py_proto_library",
+				name: "foo_py_proto",
+				attrs: attrNameToString{
+					"deps": `[":foo_proto"]`,
+				},
+			},
+			{
+				typ:  "py_library",
+				name: "foo",
+				attrs: attrNameToString{
+					"srcs":         `["dir/mylib.py"]`,
+					"srcs_version": `"PY3"`,
+					"imports":      `["."]`,
+					"deps":         `[":foo_py_proto"]`,
+				},
+			},
+		},
+	})
+}
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index b1e1fb2..8460cae 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -49,6 +49,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		feature1: {
 			conditions_default: {
@@ -94,6 +95,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		feature1: {
 			conditions_default: {
@@ -141,6 +143,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		board: {
 			soc_a: {
@@ -200,6 +203,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		feature1: {
 			conditions_default: {
@@ -268,6 +272,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		board: {
 			soc_a: {
@@ -356,6 +361,7 @@
 	name: "lib",
 	defaults: ["foo_defaults_2"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 `
 
@@ -429,12 +435,14 @@
 	name: "lib",
 	defaults: ["foo_defaults", "bar_defaults"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 
 cc_library_static {
 	name: "lib2",
 	defaults: ["bar_defaults", "foo_defaults"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 `
 
@@ -550,6 +558,7 @@
 	name: "lib",
 	defaults: ["foo_defaults", "qux_defaults"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 `
 
@@ -615,6 +624,7 @@
 library_linking_strategy_cc_defaults {
     name: "library_linking_strategy_merged_defaults",
     defaults: ["library_linking_strategy_lib_a_defaults"],
+    host_supported: true,
     soong_config_variables: {
         library_linking_strategy: {
             prefer_static: {
@@ -714,6 +724,7 @@
 
 cc_binary {
     name: "library_linking_strategy_sample_binary",
+    host_supported: true,
     srcs: ["library_linking_strategy.cc"],
     defaults: ["library_linking_strategy_sample_defaults"],
 }`
@@ -800,6 +811,7 @@
 
 cc_binary {
     name: "alphabet_binary",
+    host_supported: true,
     srcs: ["main.cc"],
     defaults: ["alphabet_sample_cc_defaults"],
 }`
@@ -861,6 +873,7 @@
 cc_binary {
     name: "alphabet_binary",
     srcs: ["main.cc"],
+    host_supported: true,
     defaults: ["alphabet_sample_cc_defaults"],
     enabled: false,
     arch: {
@@ -958,6 +971,7 @@
 
 alphabet_cc_defaults {
     name: "alphabet_sample_cc_defaults",
+    host_supported: true,
     soong_config_variables: {
         special_build: {
             enabled: true,
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 9341495..580bac4 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -213,12 +213,36 @@
 	return module
 }
 
-func customModuleFactory() android.Module {
+func customModuleFactoryHostAndDevice() android.Module {
 	m := customModuleFactoryBase()
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibBoth)
 	return m
 }
 
+func customModuleFactoryDeviceSupported() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth)
+	return m
+}
+
+func customModuleFactoryHostSupported() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.HostSupported, android.MultilibBoth)
+	return m
+}
+
+func customModuleFactoryHostAndDeviceDefault() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibBoth)
+	return m
+}
+
+func customModuleFactoryNeitherHostNorDeviceSupported() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.NeitherHostNorDeviceSupported, android.MultilibBoth)
+	return m
+}
+
 type testProps struct {
 	Test_prop struct {
 		Test_string_prop string
@@ -355,7 +379,7 @@
 }
 
 func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
-	ctx.RegisterModuleType("custom", customModuleFactory)
+	ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 	ctx.RegisterForBazelConversion()
 }
 
@@ -369,7 +393,29 @@
 
 type attrNameToString map[string]string
 
-func makeBazelTarget(typ, name string, attrs attrNameToString) string {
+func (a attrNameToString) clone() attrNameToString {
+	newAttrs := make(attrNameToString, len(a))
+	for k, v := range a {
+		newAttrs[k] = v
+	}
+	return newAttrs
+}
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that can be host or
+// device specific, or independent of host/device.
+func makeBazelTargetHostOrDevice(typ, name string, attrs attrNameToString, hod android.HostOrDeviceSupported) string {
+	if _, ok := attrs["target_compatible_with"]; !ok {
+		switch hod {
+		case android.HostSupported:
+			attrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		case android.DeviceSupported:
+			attrs["target_compatible_with"] = `["//build/bazel/platforms/os:android"]`
+		}
+	}
+
 	attrStrings := make([]string, 0, len(attrs)+1)
 	attrStrings = append(attrStrings, fmt.Sprintf(`    name = "%s",`, name))
 	for _, k := range android.SortedStringKeys(attrs) {
@@ -379,3 +425,16 @@
 %s
 )`, typ, strings.Join(attrStrings, "\n"))
 }
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that does not add a
+// target_compatible_with.  This is useful for module types like filegroup and genrule that arch not
+// arch variant
+func makeBazelTargetNoRestrictions(typ, name string, attrs attrNameToString) string {
+	return makeBazelTargetHostOrDevice(typ, name, attrs, android.HostAndDeviceDefault)
+}
+
+// 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 {
+	return makeBazelTargetHostOrDevice(typ, name, attrs, android.DeviceSupported)
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 4155aa3..fa30d09 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -29,6 +29,8 @@
 const (
 	cSrcPartition     = "c"
 	asSrcPartition    = "as"
+	lSrcPartition     = "l"
+	llSrcPartition    = "ll"
 	cppSrcPartition   = "cpp"
 	protoSrcPartition = "proto"
 )
@@ -53,6 +55,8 @@
 
 	Enabled bazel.BoolAttribute
 
+	Native_coverage bazel.BoolAttribute
+
 	sdkAttributes
 }
 
@@ -76,6 +80,12 @@
 		protoSrcPartition: android.ProtoSrcLabelPartition,
 		cSrcPartition:     bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
 		asSrcPartition:    bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+		// 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
+		//		converts them appropriately
+		lSrcPartition:  bazel.LabelPartition{Extensions: []string{".l"}},
+		llSrcPartition: bazel.LabelPartition{Extensions: []string{".ll"}},
 		// C++ is the "catch-all" group, and comprises generated sources because we don't
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
@@ -285,6 +295,11 @@
 	cppFlags bazel.StringListAttribute
 	srcs     bazel.LabelListAttribute
 
+	// Lex sources and options
+	lSrcs   bazel.LabelListAttribute
+	llSrcs  bazel.LabelListAttribute
+	lexopts bazel.StringListAttribute
+
 	hdrs bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
@@ -361,7 +376,8 @@
 				return
 			}
 			if ca.stl == nil {
-				ca.stl = stlProps.Stl
+				stl := deduplicateStlInput(*stlProps.Stl)
+				ca.stl = &stl
 			} else if ca.stl != stlProps.Stl {
 				ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
 			}
@@ -407,6 +423,8 @@
 	ca.srcs = partitionedSrcs[cppSrcPartition]
 	ca.cSrcs = partitionedSrcs[cSrcPartition]
 	ca.asSrcs = partitionedSrcs[asSrcPartition]
+	ca.lSrcs = partitionedSrcs[lSrcPartition]
+	ca.llSrcs = partitionedSrcs[llSrcPartition]
 
 	ca.absoluteIncludes.DeduplicateAxesFromBase()
 	ca.localIncludes.DeduplicateAxesFromBase()
@@ -429,32 +447,33 @@
 	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
 }
 
-func bp2buildResolveCppStdValue(c_std *string, cpp_std *string, gnu_extensions *bool) (*string, *string) {
-	var cStdVal, cppStdVal string
+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.
 	// Defaults are handled by the toolchain definition.
 	// However, if gnu_extensions is false, then the default gnu-to-c version must be specified.
-	if cpp_std != nil {
-		cppStdVal = parseCppStd(cpp_std)
-	} else if gnu_extensions != nil && !*gnu_extensions {
-		cppStdVal = "c++17"
-	}
-	if c_std != nil {
-		cStdVal = parseCStd(c_std)
-	} else if gnu_extensions != nil && !*gnu_extensions {
-		cStdVal = "c99"
+	stdVal := proptools.StringDefault(std, defaultVal)
+	if stdVal == "experimental" || stdVal == defaultVal {
+		if stdVal == "experimental" {
+			stdVal = prefix + "_std_experimental"
+		}
+		if !useGnu {
+			stdVal += "_no_gnu"
+		}
+	} else if !useGnu {
+		stdVal = gnuToCReplacer.Replace(stdVal)
 	}
 
-	cStdVal, cppStdVal = maybeReplaceGnuToC(gnu_extensions, cStdVal, cppStdVal)
-	var c_std_prop, cpp_std_prop *string
-	if cStdVal != "" {
-		c_std_prop = &cStdVal
+	if stdVal == defaultVal {
+		return nil
 	}
-	if cppStdVal != "" {
-		cpp_std_prop = &cppStdVal
-	}
+	return &stdVal
+}
 
-	return c_std_prop, cpp_std_prop
+func bp2buildResolveCppStdValue(c_std *string, cpp_std *string, gnu_extensions *bool) (*string, *string) {
+	useGnu := useGnuExtensions(gnu_extensions)
+
+	return bp2buildStdVal(c_std, "c", useGnu), bp2buildStdVal(cpp_std, "cpp", useGnu)
 }
 
 // packageFromLabel extracts package from a fully-qualified or relative Label and whether the label
@@ -515,7 +534,9 @@
 			var allHdrs []string
 			if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
 				allHdrs = baseCompilerProps.Generated_headers
-
+				if baseCompilerProps.Lex != nil {
+					compilerAttrs.lexopts.SetSelectValue(axis, config, baseCompilerProps.Lex.Flags)
+				}
 				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
 			}
 
@@ -550,10 +571,15 @@
 			}
 		}
 	}
-
 	compilerAttrs.convertStlProps(ctx, module)
 	(&linkerAttrs).convertStripProps(ctx, module)
 
+	if module.coverage != nil && module.coverage.Properties.Native_coverage != nil &&
+		!Bool(module.coverage.Properties.Native_coverage) {
+		// Native_coverage is arch neutral
+		(&linkerAttrs).features.Append(bazel.MakeStringListAttribute([]string{"-coverage"}))
+	}
+
 	productVariableProps := android.ProductVariableProperties(ctx)
 
 	(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
@@ -570,6 +596,10 @@
 	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
 	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
 
+	convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
+	(&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
+	(&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName)
+
 	return baseAttributes{
 		compilerAttrs,
 		linkerAttrs,
diff --git a/cc/builder.go b/cc/builder.go
index 107cd58..70bbd6a 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -202,36 +202,22 @@
 		},
 		"clangBin", "format")
 
-	// Rule for invoking clang-tidy (a clang-based linter).
-	clangTidyDep, clangTidyDepRE = pctx.RemoteStaticRules("clangTidyDep",
-		blueprint.RuleParams{
-			Depfile: "$out",
-			Deps:    blueprint.DepsGCC,
-			Command: "${config.CcWrapper}$ccCmd $cFlags -E -o /dev/null $in " +
-				"-MQ $tidyFile -MD -MF $out",
-			CommandDeps: []string{"$ccCmd"},
-		},
-		&remoteexec.REParams{
-			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
-			ExecStrategy: "${config.REClangTidyExecStrategy}",
-			Inputs:       []string{"$in"},
-			Platform:     map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
-		}, []string{"ccCmd", "cFlags", "tidyFile"}, []string{})
-
+	// Rules for invoking clang-tidy (a clang-based linter).
 	clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
 		blueprint.RuleParams{
 			Depfile: "${out}.d",
 			Deps:    blueprint.DepsGCC,
-			Command: "cp ${out}.dep ${out}.d && " +
-				"$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
-				"touch $out",
-			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
+			Command: "CLANG_CMD=$clangCmd TIDY_FILE=$out " +
+				"$tidyVars$reTemplate${config.ClangBin}/clang-tidy.sh $in $tidyFlags -- $cFlags",
+			CommandDeps: []string{"${config.ClangBin}/clang-tidy.sh", "$ccCmd", "$tidyCmd"},
 		},
 		&remoteexec.REParams{
 			Labels:               map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
 			ExecStrategy:         "${config.REClangTidyExecStrategy}",
-			Inputs:               []string{"$in", "${out}.dep"},
-			EnvironmentVariables: []string{"TIDY_TIMEOUT"},
+			Inputs:               []string{"$in"},
+			OutputFiles:          []string{"${out}", "${out}.d"},
+			ToolchainInputs:      []string{"$ccCmd", "$tidyCmd"},
+			EnvironmentVariables: []string{"CLANG_CMD", "TIDY_FILE", "TIDY_TIMEOUT"},
 			// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
 			// under parallel compilation and RBE. So we assume no OutputFiles here.
 			// The clang-tidy fix option is best run locally in single thread.
@@ -239,7 +225,7 @@
 			// (1) New timestamps trigger clang and clang-tidy compilations again.
 			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
 			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
-		}, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
+		}, []string{"cFlags", "ccCmd", "clangCmd", "tidyCmd", "tidyFlags", "tidyVars"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -636,6 +622,7 @@
 			continue
 		}
 
+		// ccCmd is "clang" or "clang++"
 		ccDesc := ccCmd
 
 		ccCmd = "${config.ClangBin}/" + ccCmd
@@ -681,43 +668,30 @@
 		//  Even with tidy, some src file could be skipped by noTidySrcsMap.
 		if tidy && !noTidySrcsMap[srcFile.String()] {
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
-			tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
 			tidyFiles = append(tidyFiles, tidyFile)
+			tidyCmd := "${config.ClangBin}/clang-tidy"
 
-			ruleDep := clangTidyDep
 			rule := clangTidy
 			if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
-				ruleDep = clangTidyDepRE
 				rule = clangTidyRE
 			}
 
 			sharedCFlags := shareFlags("cFlags", moduleFlags)
 			srcRelPath := srcFile.Rel()
 
-			// Add the .tidy.d rule
-			ctx.Build(pctx, android.BuildParams{
-				Rule:        ruleDep,
-				Description: "clang-tidy-dep " + srcRelPath,
-				Output:      tidyDepFile,
-				Input:       srcFile,
-				Implicits:   cFlagsDeps,
-				OrderOnly:   pathDeps,
-				Args: map[string]string{
-					"ccCmd":    ccCmd,
-					"cFlags":   sharedCFlags,
-					"tidyFile": tidyFile.String(),
-				},
-			})
-			// Add the .tidy rule with order only dependency on the .tidy.d file
+			// Add the .tidy rule
 			ctx.Build(pctx, android.BuildParams{
 				Rule:        rule,
 				Description: "clang-tidy " + srcRelPath,
 				Output:      tidyFile,
 				Input:       srcFile,
 				Implicits:   cFlagsDeps,
-				OrderOnly:   append(android.Paths{}, tidyDepFile),
+				OrderOnly:   pathDeps,
 				Args: map[string]string{
 					"cFlags":    sharedCFlags,
+					"ccCmd":     ccCmd,
+					"clangCmd":  ccDesc,
+					"tidyCmd":   tidyCmd,
 					"tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
 					"tidyVars":  tidyVars, // short and not shared
 				},
@@ -855,24 +829,13 @@
 	deps = append(deps, crtBegin...)
 	deps = append(deps, crtEnd...)
 
-	var depFile android.WritablePath
-	var depFileLdFlags string
-	depsType := blueprint.DepsNone
-	if !ctx.Windows() && !ctx.Darwin() {
-		// lld only supports --dependency-file for elf files
-		depFile = outputFile.ReplaceExtension(ctx, "d")
-		depFileLdFlags = " -Wl,--dependency-file=" + depFile.String()
-		depsType = blueprint.DepsGCC
-		implicitOutputs = append(implicitOutputs, depFile)
-	}
-
 	rule := ld
 	args := map[string]string{
 		"ldCmd":         ldCmd,
 		"crtBegin":      strings.Join(crtBegin.Strings(), " "),
 		"libFlags":      strings.Join(libFlagsList, " "),
 		"extraLibFlags": flags.extraLibFlags,
-		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags + depFileLdFlags,
+		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
 		"crtEnd":        strings.Join(crtEnd.Strings(), " "),
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
@@ -883,8 +846,6 @@
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rule,
-		Deps:            depsType,
-		Depfile:         depFile,
 		Description:     "link " + outputFile.Base(),
 		Output:          outputFile,
 		ImplicitOutputs: implicitOutputs,
@@ -957,13 +918,13 @@
 	return outputFile
 }
 
-// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.ldump).
+// 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, checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
+	baseName, exportedHeaderFlags string, diffFlags []string,
+	checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
-	createReferenceDumpFlags := ""
 
 	var extraFlags []string
 	if checkAllApis {
@@ -974,22 +935,14 @@
 			"-allow-unreferenced-elf-symbol-changes")
 	}
 
-	if exportedHeaderFlags == "" {
-		extraFlags = append(extraFlags, "-advice-only")
-	}
-
 	if isLlndk || isNdk {
-		createReferenceDumpFlags = "--llndk"
-		if isLlndk {
-			// TODO(b/130324828): "-consider-opaque-types-different" should apply to
-			// both LLNDK and NDK shared libs. However, a known issue in header-abi-diff
-			// breaks libaaudio. Remove the if-guard after the issue is fixed.
-			extraFlags = append(extraFlags, "-consider-opaque-types-different")
-		}
+		extraFlags = append(extraFlags, "-consider-opaque-types-different")
 	}
 	if isVndkExt {
 		extraFlags = append(extraFlags, "-allow-extensions")
 	}
+	// TODO(b/232891473): Simplify the above logic with diffFlags.
+	extraFlags = append(extraFlags, diffFlags...)
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        sAbiDiff,
@@ -1002,7 +955,7 @@
 			"libName":                  libName,
 			"arch":                     ctx.Arch().ArchType.Name,
 			"extraFlags":               strings.Join(extraFlags, " "),
-			"createReferenceDumpFlags": createReferenceDumpFlags,
+			"createReferenceDumpFlags": "",
 		},
 	})
 	return android.OptionalPathForPath(outputFile)
@@ -1038,33 +991,18 @@
 
 	ldCmd := "${config.ClangBin}/clang++"
 
-	var implicitOutputs android.WritablePaths
-	var depFile android.WritablePath
-	var depFileLdFlags string
-	depsType := blueprint.DepsNone
-	if !ctx.Windows() && !ctx.Darwin() {
-		// lld only supports --dependency-file for elf files
-		depFile = outputFile.ReplaceExtension(ctx, "d")
-		depFileLdFlags = " -Wl,--dependency-file=" + depFile.String()
-		depsType = blueprint.DepsGCC
-		implicitOutputs = append(implicitOutputs, depFile)
-	}
-
 	rule := partialLd
 	args := map[string]string{
 		"ldCmd":   ldCmd,
-		"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags + depFileLdFlags,
+		"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags,
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = partialLdRE
 		args["inCommaList"] = strings.Join(objFiles.Strings(), ",")
 		args["implicitInputs"] = strings.Join(deps.Strings(), ",")
-		args["implicitOutputs"] = strings.Join(implicitOutputs.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
-		Deps:        depsType,
-		Depfile:     depFile,
 		Description: "link " + outputFile.Base(),
 		Output:      outputFile,
 		Inputs:      objFiles,
diff --git a/cc/cc.go b/cc/cc.go
index da8a807..f04b6f0 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -95,6 +95,10 @@
 	HeaderLibs                                  []string
 	RuntimeLibs                                 []string
 
+	// UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to
+	// prevent automatically exporting symbols.
+	UnexportedStaticLibs []string
+
 	// Used for data dependencies adjacent to tests
 	DataLibs []string
 	DataBins []string
@@ -156,6 +160,7 @@
 	GeneratedDeps    android.Paths
 
 	Flags                      []string
+	LdFlags                    []string
 	IncludeDirs                android.Paths
 	SystemIncludeDirs          android.Paths
 	ReexportedDirs             android.Paths
@@ -678,6 +683,9 @@
 
 	// Whether or not this dependency has to be followed for the apex variants
 	excludeInApex bool
+
+	// If true, don't automatically export symbols from the static library into a shared library.
+	unexportedSymbols bool
 }
 
 // header returns true if the libraryDependencyTag is tagging a header lib dependency.
@@ -982,6 +990,7 @@
 			return library.shared()
 		}
 	}
+
 	panic(fmt.Errorf("Shared() called on non-library module: %q", c.BaseModuleName()))
 }
 
@@ -1789,13 +1798,20 @@
 var _ android.MixedBuildBuildable = (*Module)(nil)
 
 func (c *Module) getBazelModuleLabel(ctx android.BaseModuleContext) string {
+	var bazelModuleLabel string
 	if c.typ() == fullLibrary && c.static() {
 		// cc_library is a special case in bp2build; two targets are generated -- one for each
 		// of the shared and static variants. The shared variant keeps the module name, but the
 		// static variant uses a different suffixed name.
-		return bazelLabelForStaticModule(ctx, c)
+		bazelModuleLabel = bazelLabelForStaticModule(ctx, c)
+	} else {
+		bazelModuleLabel = c.GetBazelLabel(ctx, c)
 	}
-	return c.GetBazelLabel(ctx, c)
+	labelNoPrebuilt := bazelModuleLabel
+	if c.IsPrebuilt() {
+		labelNoPrebuilt = android.RemoveOptionalPrebuiltPrefixFromBazelLabel(bazelModuleLabel)
+	}
+	return labelNoPrebuilt
 }
 
 func (c *Module) QueueBazelCall(ctx android.BaseModuleContext) {
@@ -1914,6 +1930,8 @@
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-isystem "+dir.String())
 	}
 
+	flags.Local.LdFlags = append(flags.Local.LdFlags, deps.LdFlags...)
+
 	c.flags = flags
 	// We need access to all the flags seen by a source file.
 	if c.sabi != nil {
@@ -2361,6 +2379,13 @@
 		}, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
+	for _, lib := range deps.UnexportedStaticLibs {
+		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency, unexportedSymbols: true}
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+	}
+
 	for _, lib := range deps.LateSharedLibs {
 		if inList(lib, sharedLibNames) {
 			// This is to handle the case that some of the late shared libs (libc, libdl, libm, ...)
@@ -2859,6 +2884,10 @@
 						panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 					}
 				}
+				if libDepTag.unexportedSymbols {
+					depPaths.LdFlags = append(depPaths.LdFlags,
+						"-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
+				}
 			}
 
 			if libDepTag.static() && !libDepTag.wholeStatic {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index fb24624..b6d196c 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3722,6 +3722,25 @@
 	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
 }
 
+func TestNonDigitMinSdkVersionInClangTriple(t *testing.T) {
+	bp := `
+		cc_library_shared {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			min_sdk_version: "S",
+		}
+	`
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"UpsideDownCake", "Tiramisu"}
+		}),
+	).RunTestWithBp(t, bp)
+	ctx := result.TestContext
+	cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android31")
+}
+
 func TestIncludeDirsExporting(t *testing.T) {
 
 	// Trim spaces from the beginning, end and immediately after any newline characters. Leaves
@@ -4076,7 +4095,7 @@
 		{
 			name:     "assemble",
 			src:      "foo.s",
-			expected: combineSlices(baseExpectedFlags, []string{"-D__ASSEMBLY__"}, expectedIncludes, lastIncludes),
+			expected: combineSlices(baseExpectedFlags, []string{"${config.CommonGlobalAsflags}"}, expectedIncludes, lastIncludes),
 		},
 	}
 
diff --git a/cc/compiler.go b/cc/compiler.go
index 773a642..3c904b8 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -295,8 +295,12 @@
 	getNamedMapForConfig(ctx.Config(), key).Store(module, true)
 }
 
+func useGnuExtensions(gnuExtensions *bool) bool {
+	return proptools.BoolDefault(gnuExtensions, true)
+}
+
 func maybeReplaceGnuToC(gnuExtensions *bool, cStd string, cppStd string) (string, string) {
-	if gnuExtensions != nil && *gnuExtensions == false {
+	if !useGnuExtensions(gnuExtensions) {
 		cStd = gnuToCReplacer.Replace(cStd)
 		cppStd = gnuToCReplacer.Replace(cppStd)
 	}
@@ -453,7 +457,8 @@
 		if version == "" || version == "current" {
 			target += strconv.Itoa(android.FutureApiLevelInt)
 		} else {
-			target += version
+			apiLevel := nativeApiLevelOrPanic(ctx, version)
+			target += apiLevel.String()
 		}
 	}
 
@@ -489,7 +494,7 @@
 		}
 	}
 
-	flags.Global.AsFlags = append(flags.Global.AsFlags, "-D__ASSEMBLY__")
+	flags.Global.AsFlags = append(flags.Global.AsFlags, "${config.CommonGlobalAsflags}")
 
 	flags.Global.CppFlags = append(flags.Global.CppFlags, tc.Cppflags())
 
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 1a21c13..64a121e 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -24,6 +24,7 @@
         "x86_device.go",
         "x86_64_device.go",
 
+        "arm_linux_host.go",
         "darwin_host.go",
         "x86_linux_host.go",
         "x86_linux_bionic_host.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index dfe143f..66087e6 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -33,9 +33,7 @@
 		},
 		"armv8-a-branchprot": []string{
 			"-march=armv8-a",
-			// Disable BTI until drm vendors stop using OS libraries as sources
-			// of gadgets (https://issuetracker.google.com/216395195).
-			"-mbranch-protection=pac-ret",
+			"-mbranch-protection=standard",
 		},
 		"armv8-2a": []string{
 			"-march=armv8.2-a",
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 5c7f926..2d316e6 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -64,25 +64,25 @@
 
 // toolchain config for ARM64 Linux CrossHost. Almost everything is the same as the ARM64 Android
 // target. The overridden methods below show the differences.
-type toolchainLinuxArm64 struct {
+type toolchainLinuxBionicArm64 struct {
 	toolchainArm64
 }
 
-func (toolchainLinuxArm64) ClangTriple() string {
+func (toolchainLinuxBionicArm64) ClangTriple() string {
 	// Note the absence of "-android" suffix. The compiler won't define __ANDROID__
 	return "aarch64-linux"
 }
 
-func (toolchainLinuxArm64) Cflags() string {
+func (toolchainLinuxBionicArm64) Cflags() string {
 	// The inherited flags + extra flags
 	return "${config.Arm64Cflags} ${config.LinuxBionicArm64Cflags}"
 }
 
-func (toolchainLinuxArm64) CrtBeginSharedBinary() []string {
+func (toolchainLinuxBionicArm64) CrtBeginSharedBinary() []string {
 	return linuxArm64CrtBeginSharedBinary
 }
 
-func linuxArm64ToolchainFactory(arch android.Arch) Toolchain {
+func linuxBionicArm64ToolchainFactory(arch android.Arch) Toolchain {
 	archVariant := "armv8-a" // for host, default to armv8-a
 	toolchainCflags := []string{arm64ArchVariantCflagsVar[archVariant]}
 
@@ -90,7 +90,7 @@
 	// the host CPU needs the fix
 	extraLdflags := "-Wl,--fix-cortex-a53-843419"
 
-	ret := toolchainLinuxArm64{}
+	ret := toolchainLinuxBionicArm64{}
 
 	// add the extra ld and lld flags
 	ret.toolchainArm64.ldflags = strings.Join([]string{
@@ -108,5 +108,5 @@
 }
 
 func init() {
-	registerToolchainFactory(android.LinuxBionic, android.Arm64, linuxArm64ToolchainFactory)
+	registerToolchainFactory(android.LinuxBionic, android.Arm64, linuxBionicArm64ToolchainFactory)
 }
diff --git a/cc/config/arm_linux_host.go b/cc/config/arm_linux_host.go
new file mode 100644
index 0000000..525fb5d
--- /dev/null
+++ b/cc/config/arm_linux_host.go
@@ -0,0 +1,174 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import "android/soong/android"
+
+var (
+	linuxArmCflags = []string{
+		"-march=armv7a",
+	}
+
+	linuxArm64Cflags = []string{}
+
+	linuxArmLdflags = []string{
+		"-march=armv7a",
+	}
+
+	linuxArm64Ldflags = []string{}
+)
+
+func init() {
+	exportedVars.ExportStringListStaticVariable("LinuxArmCflags", linuxArmCflags)
+	exportedVars.ExportStringListStaticVariable("LinuxArm64Cflags", linuxArm64Cflags)
+	exportedVars.ExportStringListStaticVariable("LinuxArmLdflags", linuxArmLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxArmLldflags", linuxArmLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxArm64Ldflags", linuxArm64Ldflags)
+	exportedVars.ExportStringListStaticVariable("LinuxArm64Lldflags", linuxArm64Ldflags)
+
+	exportedVars.ExportStringListStaticVariable("LinuxArmYasmFlags", []string{"-f elf32 -m arm"})
+	exportedVars.ExportStringListStaticVariable("LinuxArm64YasmFlags", []string{"-f elf64 -m aarch64"})
+
+}
+
+// Musl arm+arm64
+type toolchainLinuxArm struct {
+	toolchain32Bit
+	toolchainLinux
+}
+
+type toolchainLinuxArm64 struct {
+	toolchain64Bit
+	toolchainLinux
+}
+
+func (t *toolchainLinuxArm) Name() string {
+	return "arm"
+}
+
+func (t *toolchainLinuxArm64) Name() string {
+	return "arm64"
+}
+
+func (t *toolchainLinuxArm) Cflags() string {
+	return "${config.LinuxCflags} ${config.LinuxArmCflags}"
+}
+
+func (t *toolchainLinuxArm) Cppflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxArm64) Cflags() string {
+	return "${config.LinuxCflags} ${config.LinuxArm64Cflags}"
+}
+
+func (t *toolchainLinuxArm64) Cppflags() string {
+	return ""
+}
+
+func (t *toolchainLinuxArm) Ldflags() string {
+	return "${config.LinuxLdflags} ${config.LinuxArmLdflags}"
+}
+
+func (t *toolchainLinuxArm) Lldflags() string {
+	return "${config.LinuxLldflags} ${config.LinuxArmLldflags}"
+}
+
+func (t *toolchainLinuxArm64) Ldflags() string {
+	return "${config.LinuxLdflags} ${config.LinuxArm64Ldflags}"
+}
+
+func (t *toolchainLinuxArm64) Lldflags() string {
+	return "${config.LinuxLldflags} ${config.LinuxArm64Lldflags}"
+}
+
+func (t *toolchainLinuxArm) YasmFlags() string {
+	return "${config.LinuxArmYasmFlags}"
+}
+
+func (t *toolchainLinuxArm64) YasmFlags() string {
+	return "${config.LinuxArm64YasmFlags}"
+}
+
+func (toolchainLinuxArm) LibclangRuntimeLibraryArch() string {
+	return "arm"
+}
+
+func (toolchainLinuxArm64) LibclangRuntimeLibraryArch() string {
+	return "arm64"
+}
+
+func (t *toolchainLinuxArm) InstructionSetFlags(isa string) (string, error) {
+	// TODO: Is no thumb OK?
+	return t.toolchainBase.InstructionSetFlags("")
+}
+
+type toolchainLinuxMuslArm struct {
+	toolchainLinuxArm
+	toolchainMusl
+}
+
+type toolchainLinuxMuslArm64 struct {
+	toolchainLinuxArm64
+	toolchainMusl
+}
+
+func (t *toolchainLinuxMuslArm) ClangTriple() string {
+	return "arm-linux-musleabihf"
+}
+
+func (t *toolchainLinuxMuslArm) Cflags() string {
+	return t.toolchainLinuxArm.Cflags() + " " + t.toolchainMusl.Cflags()
+}
+
+func (t *toolchainLinuxMuslArm) Ldflags() string {
+	return t.toolchainLinuxArm.Ldflags() + " " + t.toolchainMusl.Ldflags()
+}
+
+func (t *toolchainLinuxMuslArm) Lldflags() string {
+	return t.toolchainLinuxArm.Lldflags() + " " + t.toolchainMusl.Lldflags()
+}
+
+func (t *toolchainLinuxMuslArm64) ClangTriple() string {
+	return "aarch64-linux-musl"
+}
+
+func (t *toolchainLinuxMuslArm64) Cflags() string {
+	return t.toolchainLinuxArm64.Cflags() + " " + t.toolchainMusl.Cflags()
+}
+
+func (t *toolchainLinuxMuslArm64) Ldflags() string {
+	return t.toolchainLinuxArm64.Ldflags() + " " + t.toolchainMusl.Ldflags()
+}
+
+func (t *toolchainLinuxMuslArm64) Lldflags() string {
+	return t.toolchainLinuxArm64.Lldflags() + " " + t.toolchainMusl.Lldflags()
+}
+
+var toolchainLinuxMuslArmSingleton Toolchain = &toolchainLinuxMuslArm{}
+var toolchainLinuxMuslArm64Singleton Toolchain = &toolchainLinuxMuslArm64{}
+
+func linuxMuslArmToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslArmSingleton
+}
+
+func linuxMuslArm64ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslArm64Singleton
+}
+
+func init() {
+	registerToolchainFactory(android.LinuxMusl, android.Arm, linuxMuslArmToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.Arm64, linuxMuslArm64ToolchainFactory)
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 1c4ad7f..c5fde55 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -117,6 +117,13 @@
 
 	commonGlobalConlyflags = []string{}
 
+	commonGlobalAsflags = []string{
+		"-D__ASSEMBLY__",
+		// TODO(b/235105792): override global -fdebug-default-version=5, it is causing $TMPDIR to
+		// end up in the dwarf data for crtend_so.S.
+		"-fdebug-default-version=4",
+	}
+
 	deviceGlobalCflags = []string{
 		"-ffunction-sections",
 		"-fdata-sections",
@@ -191,7 +198,6 @@
 		"-Werror=int-in-bool-context",
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
-		"-Werror=string-compare",
 		"-Werror=xor-used-as-pow",
 		// http://b/161386391 for -Wno-void-pointer-to-enum-cast
 		"-Wno-void-pointer-to-enum-cast",
@@ -225,7 +231,6 @@
 		"-Wno-misleading-indentation",               // http://b/153746954
 		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
 		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
-		"-Wno-string-compare",                       // http://b/153764102
 		"-Wno-pessimizing-move",                     // http://b/154270751
 		// New warnings to be fixed after clang-r399163
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
@@ -315,6 +320,7 @@
 	}
 
 	exportedVars.ExportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
+	exportedVars.ExportStringListStaticVariable("CommonGlobalAsflags", commonGlobalAsflags)
 	exportedVars.ExportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
 	exportedVars.ExportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
 	exportedVars.ExportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
@@ -372,6 +378,11 @@
 	exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
 	exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
 
+	exportedVars.ExportString("CStdVersion", CStdVersion)
+	exportedVars.ExportString("CppStdVersion", CppStdVersion)
+	exportedVars.ExportString("ExperimentalCStdVersion", ExperimentalCStdVersion)
+	exportedVars.ExportString("ExperimentalCppStdVersion", ExperimentalCppStdVersion)
+
 	// Everything in these lists is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
 	commonGlobalIncludes := []string{
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 826197a..674edad 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -19,6 +19,37 @@
 	"strings"
 )
 
+var (
+	// Some clang-tidy checks have bugs or don't work for Android.
+	// They are disabled here, overriding any locally selected checks.
+	globalNoCheckList = []string{
+		// https://b.corp.google.com/issues/153464409
+		// many local projects enable cert-* checks, which
+		// trigger bugprone-reserved-identifier.
+		"-bugprone-reserved-identifier*,-cert-dcl51-cpp,-cert-dcl37-c",
+		// http://b/153757728
+		"-readability-qualified-auto",
+		// http://b/193716442
+		"-bugprone-implicit-widening-of-multiplication-result",
+		// Too many existing functions trigger this rule, and fixing it requires large code
+		// refactoring. The cost of maintaining this tidy rule outweighs the benefit it brings.
+		"-bugprone-easily-swappable-parameters",
+		// http://b/216364337 - TODO: Follow-up after compiler update to
+		// disable or fix individual instances.
+		"-cert-err33-c",
+	}
+
+	// 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/155034972
+		"-bugprone-branch-clone",
+	}
+)
+
 func init() {
 	// Many clang-tidy checks like altera-*, llvm-*, modernize-*
 	// are not designed for Android source code or creating too
@@ -94,6 +125,14 @@
 		}, ",")
 	})
 
+	pctx.VariableFunc("TidyGlobalNoChecks", func(ctx android.PackageVarContext) string {
+		return strings.Join(globalNoCheckList, ",")
+	})
+
+	pctx.VariableFunc("TidyGlobalNoErrorChecks", func(ctx android.PackageVarContext) string {
+		return strings.Join(globalNoErrorCheckList, ",")
+	})
+
 	// To reduce duplicate warnings from the same header files,
 	// header-filter will contain only the module directory and
 	// those specified by DEFAULT_TIDY_HEADER_DIRS.
@@ -152,6 +191,22 @@
 	return tidyDefault
 }
 
+// Returns a globally disabled tidy checks, overriding locally selected checks.
+func TidyGlobalNoChecks() string {
+	if len(globalNoCheckList) > 0 {
+		return ",${config.TidyGlobalNoChecks}"
+	}
+	return ""
+}
+
+// Returns a globally allowed/no-error tidy checks, appended to -warnings-as-errors.
+func TidyGlobalNoErrorChecks() string {
+	if len(globalNoErrorCheckList) > 0 {
+		return ",${config.TidyGlobalNoErrorChecks}"
+	}
+	return ""
+}
+
 func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
 	// Disable clang-analyzer-* checks globally for generated source files
 	// because some of them are too huge. Local .bp files can add wanted
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 492cd98..dd612ce 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -17,100 +17,9 @@
 // List of VNDK libraries that have different core variant and vendor variant.
 // For these libraries, the vendor variants must be installed even if the device
 // has VndkUseCoreVariant set.
-// TODO(b/150578172): clean up unstable and non-versioned aidl module
+// Note that AIDL-generated modules must use vendor variants by default.
 var VndkMustUseVendorVariantList = []string{
-	"android.hardware.authsecret-V1-ndk",
-	"android.hardware.authsecret-V1-ndk_platform",
-	"android.hardware.authsecret-ndk_platform",
-	"android.hardware.authsecret-unstable-ndk_platform",
-	"android.hardware.automotive.occupant_awareness-V1-ndk",
-	"android.hardware.automotive.occupant_awareness-V1-ndk_platform",
-	"android.hardware.automotive.occupant_awareness-ndk_platform",
-	"android.hardware.gnss-V1-ndk",
-	"android.hardware.gnss-V1-ndk_platform",
-	"android.hardware.gnss-ndk_platform",
-	"android.hardware.gnss-unstable-ndk_platform",
-	"android.hardware.health-V1-ndk",
-	"android.hardware.health-ndk",
-	"android.hardware.health.storage-V1-ndk",
-	"android.hardware.health.storage-V1-ndk_platform",
-	"android.hardware.health.storage-ndk_platform",
-	"android.hardware.health.storage-unstable-ndk_platform",
-	"android.hardware.identity-V2-ndk_platform",
-	"android.hardware.identity-V3-ndk",
-	"android.hardware.identity-V3-ndk_platform",
-	"android.hardware.identity-ndk_platform",
-	"android.hardware.light-V1-ndk",
-	"android.hardware.light-V1-ndk_platform",
-	"android.hardware.light-ndk_platform",
-	"android.hardware.memtrack-V1-ndk",
-	"android.hardware.memtrack-V1-ndk_platform",
-	"android.hardware.memtrack-ndk_platform",
-	"android.hardware.memtrack-unstable-ndk_platform",
 	"android.hardware.nfc@1.2",
-	"android.hardware.oemlock-V1-ndk",
-	"android.hardware.oemlock-V1-ndk_platform",
-	"android.hardware.oemlock-ndk_platform",
-	"android.hardware.oemlock-unstable-ndk_platform",
-	"android.hardware.power-V1-ndk_platform",
-	"android.hardware.power-V2-ndk",
-	"android.hardware.power-V2-ndk_platform",
-	"android.hardware.power-ndk_platform",
-	"android.hardware.power.stats-V1-ndk",
-	"android.hardware.power.stats-V1-ndk_platform",
-	"android.hardware.power.stats-ndk_platform",
-	"android.hardware.power.stats-unstable-ndk_platform",
-	"android.hardware.rebootescrow-V1-ndk",
-	"android.hardware.rebootescrow-V1-ndk_platform",
-	"android.hardware.rebootescrow-ndk_platform",
-	"android.hardware.radio-V1-ndk",
-	"android.hardware.radio-V1-ndk_platform",
-	"android.hardware.radio.config-V1-ndk",
-	"android.hardware.radio.config-V1-ndk_platform",
-	"android.hardware.radio.data-V1-ndk",
-	"android.hardware.radio.data-V1-ndk_platform",
-	"android.hardware.radio.messaging-V1-ndk",
-	"android.hardware.radio.messaging-V1-ndk_platform",
-	"android.hardware.radio.modem-V1-ndk",
-	"android.hardware.radio.modem-V1-ndk_platform",
-	"android.hardware.radio.network-V1-ndk",
-	"android.hardware.radio.network-V1-ndk_platform",
-	"android.hardware.radio.sim-V1-ndk",
-	"android.hardware.radio.sim-V1-ndk_platform",
-	"android.hardware.radio.voice-V1-ndk",
-	"android.hardware.radio.voice-V1-ndk_platform",
-	"android.hardware.security.keymint-V1-ndk",
-	"android.hardware.security.keymint-V1-ndk_platform",
-	"android.hardware.security.keymint-ndk_platform",
-	"android.hardware.security.keymint-unstable-ndk_platform",
-	"android.hardware.security.secureclock-V1-ndk",
-	"android.hardware.security.secureclock-V1-ndk_platform",
-	"android.hardware.security.secureclock-ndk_platform",
-	"android.hardware.security.secureclock-unstable-ndk_platform",
-	"android.hardware.security.sharedsecret-V1-ndk",
-	"android.hardware.security.sharedsecret-V1-ndk_platform",
-	"android.hardware.security.sharedsecret-ndk_platform",
-	"android.hardware.security.sharedsecret-unstable-ndk_platform",
-	"android.hardware.vibrator-V1-ndk_platform",
-	"android.hardware.vibrator-V2-ndk",
-	"android.hardware.vibrator-V2-ndk_platform",
-	"android.hardware.vibrator-ndk_platform",
-	"android.hardware.weaver-V1-ndk",
-	"android.hardware.weaver-V1-ndk_platform",
-	"android.hardware.weaver-ndk_platform",
-	"android.hardware.weaver-unstable-ndk_platform",
-	"android.system.suspend-V1-ndk",
-	"android.system.keystore2-V1-ndk",
-	"android.se.omapi-V1-ndk_platform",
-	"android.se.omapi-ndk_platform",
-	"android.se.omapi-unstable-ndk_platform",
-	"android.hardware.wifi.hostapd-V1-ndk",
-	"android.hardware.wifi.hostapd-V1-ndk_platform",
-	"android.hardware.wifi.supplicant-V1-ndk",
-	"android.system.keystore2-V1-ndk_platform",
-	"android.system.keystore2-ndk_platform",
-	"android.system.keystore2-unstable-ndk_platform",
-	"android.system.suspend-V1-ndk_platform",
 	"libbinder",
 	"libcrypto",
 	"libexpat",
diff --git a/cc/gen.go b/cc/gen.go
index 8f62363..08b49c9 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/bazel"
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -169,6 +170,41 @@
 	})
 }
 
+type LexAttrs struct {
+	Srcs    bazel.LabelListAttribute
+	Lexopts bazel.StringListAttribute
+}
+
+type LexNames struct {
+	cSrcName bazel.LabelAttribute
+	srcName  bazel.LabelAttribute
+}
+
+func bp2BuildLex(ctx android.Bp2buildMutatorContext, moduleName string, ca compilerAttributes) LexNames {
+	names := LexNames{}
+	if !ca.lSrcs.IsEmpty() {
+		names.cSrcName = createLexTargetModule(ctx, moduleName+"_genlex_l", ca.lSrcs, ca.lexopts)
+	}
+	if !ca.llSrcs.IsEmpty() {
+		names.srcName = createLexTargetModule(ctx, moduleName+"_genlex_ll", ca.llSrcs, ca.lexopts)
+	}
+	return names
+}
+
+func createLexTargetModule(ctx android.Bp2buildMutatorContext, name string, srcs bazel.LabelListAttribute, opts bazel.StringListAttribute) bazel.LabelAttribute {
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "genlex",
+			Bzl_load_location: "//build/bazel/rules/cc:flex.bzl",
+		},
+		android.CommonAttributes{Name: name},
+		&LexAttrs{
+			Srcs:    srcs,
+			Lexopts: opts,
+		})
+	return bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + name}}
+}
+
 func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Paths) {
 	headerFile := android.PathForModuleGen(ctx, "sysprop", "include", syspropFile.Rel()+".h")
 	publicHeaderFile := android.PathForModuleGen(ctx, "sysprop/public", "include", syspropFile.Rel()+".h")
diff --git a/cc/library.go b/cc/library.go
index d819c62..c445a42 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -110,6 +110,9 @@
 		// Run checks on all APIs (in addition to the ones referred by
 		// one of exported ELF symbols.)
 		Check_all_apis *bool
+
+		// Extra flags passed to header-abi-diff
+		Diff_flags []string
 	}
 
 	// Inject boringssl hash into the shared library.  This is only intended for use by external/boringssl.
@@ -922,7 +925,6 @@
 		if ctx.Darwin() {
 			f = append(f,
 				"-dynamiclib",
-				"-single_module",
 				"-install_name @rpath/"+libName+flags.Toolchain.ShlibSuffix(),
 			)
 			if ctx.Arch().ArchType == android.X86 {
@@ -1634,6 +1636,7 @@
 		if refAbiDumpFile != nil {
 			library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
 				refAbiDumpFile, fileName, exportedHeaderFlags,
+				library.Properties.Header_abi_checker.Diff_flags,
 				Bool(library.Properties.Header_abi_checker.Check_all_apis),
 				ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
 		}
@@ -2432,7 +2435,6 @@
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Command().
 			BuiltTool("bssl_inject_hash").
-			Flag("-sha256").
 			FlagWithInput("-in-object ", outputFile).
 			FlagWithOutput("-o ", hashedOutputfile)
 		rule.Build("injectCryptoHash", "inject crypto hash")
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 7232290..970d8d1 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -125,12 +125,12 @@
 	baseAttributes := bp2BuildParseBaseProps(ctx, module)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &baseAttributes.includes)
 	linkerAttrs := baseAttributes.linkerAttributes
+	(&linkerAttrs.deps).Append(linkerAttrs.dynamicDeps)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
 		Export_includes:          exportedIncludes.Includes,
 		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
 		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Implementation_deps:      linkerAttrs.implementationDeps,
 		Deps:                     linkerAttrs.deps,
 		System_dynamic_deps:      linkerAttrs.systemDynamicDeps,
 		Hdrs:                     baseAttributes.hdrs,
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 8988de2..1bcbdc5 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -27,32 +27,33 @@
 
 var sharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName:    "native_shared_libs",
-		SupportsSdk:     true,
-		HostOsDependent: true,
+		PropertyName:          "native_shared_libs",
+		SupportsSdk:           true,
+		HostOsDependent:       true,
+		SupportedLinkageNames: []string{"shared"},
 	},
 	prebuiltModuleType: "cc_prebuilt_library_shared",
-	linkTypes:          []string{"shared"},
 }
 
 var staticLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName:    "native_static_libs",
-		SupportsSdk:     true,
-		HostOsDependent: true,
+		PropertyName:          "native_static_libs",
+		SupportsSdk:           true,
+		HostOsDependent:       true,
+		SupportedLinkageNames: []string{"static"},
 	},
 	prebuiltModuleType: "cc_prebuilt_library_static",
-	linkTypes:          []string{"static"},
 }
 
 var staticAndSharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
-		PropertyName:    "native_libs",
-		SupportsSdk:     true,
-		HostOsDependent: true,
+		PropertyName:           "native_libs",
+		OverridesPropertyNames: map[string]bool{"native_shared_libs": true, "native_static_libs": true},
+		SupportsSdk:            true,
+		HostOsDependent:        true,
+		SupportedLinkageNames:  []string{"static", "shared"},
 	},
 	prebuiltModuleType: "cc_prebuilt_library",
-	linkTypes:          []string{"static", "shared"},
 }
 
 func init() {
@@ -69,9 +70,6 @@
 
 	noOutputFiles bool // True if there are no srcs files.
 
-	// The set of link types supported. A set of "static", "shared", or nil to
-	// skip link type variations.
-	linkTypes []string
 }
 
 func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
@@ -165,12 +163,12 @@
 				// Add any additional dependencies needed.
 				variations = append(variations, dependency.imageVariations...)
 
-				if mt.linkTypes == nil {
+				if mt.SupportedLinkageNames == nil {
 					// No link types are supported so add a dependency directly.
 					ctx.AddFarVariationDependencies(variations, dependencyTag, name)
 				} else {
 					// Otherwise, add a dependency on each supported link type in turn.
-					for _, linkType := range mt.linkTypes {
+					for _, linkType := range mt.SupportedLinkageNames {
 						libVariations := append(variations,
 							blueprint.Variation{Mutator: "link", Variation: linkType})
 						// If this is for the device and a shared link type then add a dependency onto the
diff --git a/cc/linkable.go b/cc/linkable.go
index 04eab39..2316d86 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -22,13 +22,6 @@
 	// than left undefined.
 	IsSanitizerExplicitlyDisabled(t SanitizerType) bool
 
-	// SanitizeDep returns true if the module is statically linked into another that is sanitized
-	// with the given sanitizer.
-	SanitizeDep(t SanitizerType) bool
-
-	// SetSanitizeDep marks a module as a static dependency of another module to be sanitized.
-	SetSanitizeDep(t SanitizerType)
-
 	// SetSanitizer enables or disables the specified sanitizer type if it's supported, otherwise this should panic.
 	SetSanitizer(t SanitizerType, b bool)
 
@@ -113,6 +106,9 @@
 	UnstrippedOutputFile() android.Path
 	CoverageFiles() android.Paths
 
+	// CoverageOutputFile returns the output archive of gcno coverage information files.
+	CoverageOutputFile() android.OptionalPath
+
 	NonCcVariants() bool
 
 	SelectedStl() string
@@ -140,6 +136,12 @@
 
 	UseSdk() bool
 
+	// IsNdk returns true if the library is in the configs known NDK list.
+	IsNdk(config android.Config) bool
+
+	// IsStubs returns true if the this is a stubs library.
+	IsStubs() bool
+
 	// IsLlndk returns true for both LLNDK (public) and LLNDK-private libs.
 	IsLlndk() bool
 
diff --git a/cc/linker.go b/cc/linker.go
index 4e9404c..76a60ca 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -399,7 +399,7 @@
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins has to be last on the command line
 		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
-			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 
 		if inList("libdl", deps.SharedLibs) {
@@ -422,7 +422,7 @@
 		}
 	} else if ctx.toolchain().Musl() {
 		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
-			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 	}
 
@@ -436,11 +436,6 @@
 }
 
 func (linker *baseLinker) useClangLld(ctx ModuleContext) bool {
-	// Clang lld is not ready for for Darwin host executables yet.
-	// See https://lld.llvm.org/AtomLLD.html for status of lld for Mach-O.
-	if ctx.Darwin() {
-		return false
-	}
 	if linker.Properties.Use_clang_lld != nil {
 		return Bool(linker.Properties.Use_clang_lld)
 	}
@@ -530,10 +525,6 @@
 		}
 	}
 
-	if ctx.toolchain().LibclangRuntimeLibraryArch() != "" {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
-	}
-
 	CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
 
 	flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
diff --git a/cc/object.go b/cc/object.go
index 0b9cf6f..65a11e0 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -38,7 +38,6 @@
 		SupportsSdk:  true,
 	},
 	prebuiltModuleType: "cc_prebuilt_object",
-	linkTypes:          nil,
 }
 
 type objectLinker struct {
diff --git a/cc/pgo.go b/cc/pgo.go
index 0632c15..463e2e6 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -41,7 +41,6 @@
 
 const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
 const profileUseInstrumentFormat = "-fprofile-use=%s"
-const profileUseSamplingFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
 
 func getPgoProfileProjects(config android.DeviceConfig) []string {
 	return config.OnceStringSlice(pgoProfileProjectsConfigKey, func() []string {
@@ -56,12 +55,11 @@
 type PgoProperties struct {
 	Pgo struct {
 		Instrumentation    *bool
-		Sampling           *bool   `android:"arch_variant"`
 		Profile_file       *string `android:"arch_variant"`
 		Benchmarks         []string
 		Enable_profile_use *bool `android:"arch_variant"`
 		// Additional compiler flags to use when building this module
-		// for profiling (either instrumentation or sampling).
+		// for profiling.
 		Cflags []string `android:"arch_variant"`
 	} `android:"arch_variant"`
 
@@ -79,10 +77,6 @@
 	return props.Pgo.Instrumentation != nil && *props.Pgo.Instrumentation == true
 }
 
-func (props *PgoProperties) isSampling() bool {
-	return props.Pgo.Sampling != nil && *props.Pgo.Sampling == true
-}
-
 func (pgo *pgo) props() []interface{} {
 	return []interface{}{&pgo.Properties}
 }
@@ -135,18 +129,8 @@
 	return android.OptionalPathForPath(nil)
 }
 
-func (props *PgoProperties) profileUseFlag(ctx ModuleContext, file string) string {
-	if props.isInstrumentation() {
-		return fmt.Sprintf(profileUseInstrumentFormat, file)
-	}
-	if props.isSampling() {
-		return fmt.Sprintf(profileUseSamplingFormat, file)
-	}
-	return ""
-}
-
 func (props *PgoProperties) profileUseFlags(ctx ModuleContext, file string) []string {
-	flags := []string{props.profileUseFlag(ctx, file)}
+	flags := []string{fmt.Sprintf(profileUseInstrumentFormat, file)}
 	flags = append(flags, profileUseOtherFlags...)
 	return flags
 }
@@ -169,19 +153,14 @@
 		// if profileFile gets updated
 		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
-
-		if props.isSampling() {
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
-		}
 	}
 	return flags
 }
 
 func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
 	isInstrumentation := props.isInstrumentation()
-	isSampling := props.isSampling()
 
-	profileKindPresent := isInstrumentation || isSampling
+	profileKindPresent := isInstrumentation
 	filePresent := props.Pgo.Profile_file != nil
 	benchmarksPresent := len(props.Pgo.Benchmarks) > 0
 
@@ -194,7 +173,7 @@
 	if !profileKindPresent || !filePresent {
 		var missing []string
 		if !profileKindPresent {
-			missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
+			missing = append(missing, "profile kind")
 		}
 		if !filePresent {
 			missing = append(missing, "profile_file property")
@@ -208,14 +187,6 @@
 		ctx.ModuleErrorf("Instrumentation PGO specification is missing benchmark property")
 	}
 
-	if isSampling {
-		ctx.ModuleErrorf("Sampling PGO is deprecated, use AFDO instead")
-	}
-
-	if isSampling && isInstrumentation {
-		ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
-	}
-
 	return true
 }
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index a29e618..8c404d3 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -55,6 +55,13 @@
 	// This is needed only if this library is linked by other modules in build time.
 	// Only makes sense for the Windows target.
 	Windows_import_lib *string `android:"path,arch_variant"`
+
+	// MixedBuildsDisabled is true if and only if building this prebuilt is explicitly disabled in mixed builds for either
+	// its static or shared version on the current build variant. This is to prevent Bazel targets for build variants with
+	// which either the static or shared version is incompatible from participating in mixed buiods. Please note that this
+	// is an override and does not fully determine whether Bazel or Soong will be used. For the full determination, see
+	// cc.ProcessBazelQueryResponse, cc.QueueBazelCall, and cc.MixedBuildsDisabled.
+	MixedBuildsDisabled bool `blueprint:"mutated"`
 }
 
 type prebuiltLinker struct {
@@ -244,6 +251,7 @@
 
 func (p *prebuiltLibraryLinker) disablePrebuilt() {
 	p.properties.Srcs = nil
+	p.properties.MixedBuildsDisabled = true
 }
 
 // Implements versionedInterface
@@ -255,6 +263,7 @@
 	module, library := NewLibrary(hod)
 	module.compiler = nil
 	module.bazelable = true
+	module.bazelHandler = &prebuiltLibraryBazelHandler{module: module, library: library}
 
 	prebuilt := &prebuiltLibraryLinker{
 		libraryDecorator: library,
@@ -310,8 +319,6 @@
 func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewPrebuiltLibrary(hod, "srcs")
 	library.BuildOnlyShared()
-	module.bazelable = true
-	module.bazelHandler = &prebuiltSharedLibraryBazelHandler{module: module, library: library}
 
 	// Prebuilt shared libraries can be included in APEXes
 	android.InitApexModule(module)
@@ -329,8 +336,7 @@
 func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewPrebuiltLibrary(hod, "srcs")
 	library.BuildOnlyStatic()
-	module.bazelable = true
-	module.bazelHandler = &prebuiltStaticLibraryBazelHandler{module: module, library: library}
+
 	return module, library
 }
 
@@ -406,29 +412,52 @@
 	properties prebuiltObjectProperties
 }
 
-type prebuiltStaticLibraryBazelHandler struct {
+type prebuiltLibraryBazelHandler struct {
 	module  *Module
 	library *libraryDecorator
 }
 
-var _ BazelHandler = (*prebuiltStaticLibraryBazelHandler)(nil)
+var _ BazelHandler = (*prebuiltLibraryBazelHandler)(nil)
 
-func (h *prebuiltStaticLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+func (h *prebuiltLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
+	if h.module.linker.(*prebuiltLibraryLinker).properties.MixedBuildsDisabled {
+		return
+	}
 	bazelCtx := ctx.Config().BazelContext
 	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
 }
 
-func (h *prebuiltStaticLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+func (h *prebuiltLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+	if h.module.linker.(*prebuiltLibraryLinker).properties.MixedBuildsDisabled {
+		return
+	}
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
 	}
+
+	if h.module.static() {
+		if ok := h.processStaticBazelQueryResponse(ctx, label, ccInfo); !ok {
+			return
+		}
+	} else if h.module.Shared() {
+		if ok := h.processSharedBazelQueryResponse(ctx, label, ccInfo); !ok {
+			return
+		}
+	} else {
+		return
+	}
+
+	h.module.maybeUnhideFromMake()
+}
+
+func (h *prebuiltLibraryBazelHandler) processStaticBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
 	staticLibs := ccInfo.CcStaticLibraryFiles
 	if len(staticLibs) > 1 {
 		ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
-		return
+		return false
 	}
 
 	// TODO(b/184543518): cc_prebuilt_library_static may have properties for re-exporting flags
@@ -443,7 +472,7 @@
 
 	if len(staticLibs) == 0 {
 		h.module.outputFile = android.OptionalPath{}
-		return
+		return true
 	}
 
 	out := android.PathForBazelOut(ctx, staticLibs[0])
@@ -455,31 +484,15 @@
 
 		TransitiveStaticLibrariesForOrdering: depSet,
 	})
+
+	return true
 }
 
-type prebuiltSharedLibraryBazelHandler struct {
-	module  *Module
-	library *libraryDecorator
-}
-
-var _ BazelHandler = (*prebuiltSharedLibraryBazelHandler)(nil)
-
-func (h *prebuiltSharedLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
-	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
-}
-
-func (h *prebuiltSharedLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
-	bazelCtx := ctx.Config().BazelContext
-	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
-	if err != nil {
-		ctx.ModuleErrorf(err.Error())
-		return
-	}
+func (h *prebuiltLibraryBazelHandler) processSharedBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
 	sharedLibs := ccInfo.CcSharedLibraryFiles
-	if len(sharedLibs) != 1 {
+	if len(sharedLibs) > 1 {
 		ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs)
-		return
+		return false
 	}
 
 	// TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags
@@ -487,14 +500,9 @@
 	// TODO(eakammer):Add stub-related flags if this library is a stub library.
 	// h.library.exportVersioningMacroIfNeeded(ctx)
 
-	// Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
-	// validation will fail. For now, set this to an empty list.
-	// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
-	h.library.collectedSnapshotHeaders = android.Paths{}
-
 	if len(sharedLibs) == 0 {
 		h.module.outputFile = android.OptionalPath{}
-		return
+		return true
 	}
 
 	out := android.PathForBazelOut(ctx, sharedLibs[0])
@@ -519,6 +527,7 @@
 
 	h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
 	h.module.maybeUnhideFromMake()
+	return true
 }
 
 func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt {
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 901f458..e959157 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -381,6 +381,149 @@
 	assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a")
 }
 
+func TestPrebuiltLibraryWithBazel(t *testing.T) {
+	const bp = `
+cc_prebuilt_library {
+	name: "foo",
+	shared: {
+		srcs: ["foo.so"],
+	},
+	static: {
+		srcs: ["foo.a"],
+	},
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	outBaseDir := "outputbase"
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltTest,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outBaseDir,
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					"//foo/bar:bar": cquery.CcInfo{
+						CcSharedLibraryFiles: []string{"foo.so"},
+					},
+					"//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+						CcStaticLibraryFiles: []string{"foo.a"},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	pathPrefix := outBaseDir + "/execroot/__main__/"
+
+	sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t,
+		"prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+		pathPrefix+"foo.so", sharedInfo.SharedLibrary)
+
+	outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{pathPrefix + "foo.so"}
+	android.AssertDeepEquals(t,
+		"prebuilt library shared target output files did not match expected.",
+		expectedOutputFiles, outputFiles.Strings())
+
+	staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+	staticInfo := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t,
+		"prebuilt library static target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+		pathPrefix+"foo.a", staticInfo.StaticLibrary)
+
+	staticOutputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object staticOutputFiles %s", err)
+	}
+	expectedStaticOutputFiles := []string{pathPrefix + "foo.a"}
+	android.AssertDeepEquals(t,
+		"prebuilt library static target output files did not match expected.",
+		expectedStaticOutputFiles, staticOutputFiles.Strings())
+}
+
+func TestPrebuiltLibraryWithBazelStaticDisabled(t *testing.T) {
+	const bp = `
+cc_prebuilt_library {
+	name: "foo",
+	shared: {
+		srcs: ["foo.so"],
+	},
+	static: {
+		enabled: false
+	},
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	outBaseDir := "outputbase"
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltTest,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outBaseDir,
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					"//foo/bar:bar": cquery.CcInfo{
+						CcSharedLibraryFiles: []string{"foo.so"},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	pathPrefix := outBaseDir + "/execroot/__main__/"
+
+	sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t,
+		"prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+		pathPrefix+"foo.so", sharedInfo.SharedLibrary)
+
+	outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{pathPrefix + "foo.so"}
+	android.AssertDeepEquals(t,
+		"prebuilt library shared target output files did not match expected.",
+		expectedOutputFiles, outputFiles.Strings())
+}
+
+func TestPrebuiltLibraryStaticWithBazel(t *testing.T) {
+	const bp = `
+cc_prebuilt_library_static {
+	name: "foo",
+	srcs: ["foo.so"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	outBaseDir := "outputbase"
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltTest,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outBaseDir,
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					"//foo/bar:bar": cquery.CcInfo{
+						CcStaticLibraryFiles: []string{"foo.so"},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+	pathPrefix := outBaseDir + "/execroot/__main__/"
+
+	info := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t,
+		"prebuilt library static path did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+		pathPrefix+"foo.so", info.StaticLibrary)
+
+	outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{pathPrefix + "foo.so"}
+	android.AssertDeepEquals(t, "prebuilt library static output files did not match expected.", expectedOutputFiles, outputFiles.Strings())
+}
+
 func TestPrebuiltLibrarySharedWithBazelWithoutToc(t *testing.T) {
 	const bp = `
 cc_prebuilt_library_shared {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 87cee9f..86472a2 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -153,9 +153,10 @@
 
 func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) {
 	switch t {
-	case Asan, Hwasan, Fuzzer, scs, tsan, cfi:
-		ctx.TopDown(t.variationName()+"_deps", sanitizerDepsMutator(t))
-		ctx.BottomUp(t.variationName(), sanitizerMutator(t))
+	case cfi, Hwasan, Asan, tsan, Fuzzer, scs:
+		sanitizer := &sanitizerSplitMutator{t}
+		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
+		ctx.Transition(t.variationName(), sanitizer)
 	case Memtag_heap, intOverflow:
 		// do nothing
 	default:
@@ -276,7 +277,6 @@
 type SanitizeProperties struct {
 	Sanitize          SanitizeUserProps `android:"arch_variant"`
 	SanitizerEnabled  bool              `blueprint:"mutated"`
-	SanitizeDepTypes  []SanitizerType   `blueprint:"mutated"`
 	MinimalRuntimeDep bool              `blueprint:"mutated"`
 	BuiltinsDep       bool              `blueprint:"mutated"`
 	UbsanRuntimeDep   bool              `blueprint:"mutated"`
@@ -430,7 +430,7 @@
 	}
 
 	// Enable Memtag for all components in the include paths (for Aarch64 only)
-	if ctx.Arch().ArchType == android.Arm64 {
+	if ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
 				s.Memtag_heap = proptools.BoolPtr(true)
@@ -460,17 +460,17 @@
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
 		s.Hwaddress = nil
 	}
 
 	// SCS is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
 		s.Scs = nil
 	}
 
 	// Memtag_heap is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
 		s.Memtag_heap = nil
 	}
 
@@ -588,13 +588,6 @@
 }
 
 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
-	minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
-
-	if sanitize.Properties.MinimalRuntimeDep {
-		flags.Local.LdFlags = append(flags.Local.LdFlags,
-			"-Wl,--exclude-libs,"+minimalRuntimeLib)
-	}
-
 	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
 		return flags
 	}
@@ -717,14 +710,14 @@
 			// Host sanitizers only link symbols in the final executable, so
 			// there will always be undefined symbols in intermediate libraries.
 			_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
-
-			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
-			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
-		if enableMinimalRuntime(sanitize) {
-			flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
+		if !ctx.toolchain().Bionic() {
+			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function san.
+			// Musl toolchain prebuilts have vptr and function sanitizers, but enabling them
+			// implicitly enables RTTI which causes RTTI mismatch issues with dependencies.
+
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
 		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -735,6 +728,11 @@
 		} else {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
 		}
+
+		if enableMinimalRuntime(sanitize) {
+			flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
+		}
+
 		// http://b/119329758, Android core does not boot up with this sanitizer yet.
 		if toDisableImplicitIntegerChange(flags.Local.CFlags) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
@@ -906,7 +904,7 @@
 // Determines if the current module is a static library going to be captured
 // as vendor snapshot. Such modules must create both cfi and non-cfi variants,
 // except for ones which explicitly disable cfi.
-func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
+func needsCfiForVendorSnapshot(mctx android.BaseModuleContext) bool {
 	if snapshot.IsVendorProprietaryModule(mctx) {
 		return false
 	}
@@ -934,62 +932,232 @@
 		!c.IsSanitizerExplicitlyDisabled(cfi)
 }
 
-// Propagate sanitizer requirements down from binaries
-func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) {
-	return func(mctx android.TopDownMutatorContext) {
-		if c, ok := mctx.Module().(PlatformSanitizeable); ok {
-			enabled := c.IsSanitizerEnabled(t)
-			if t == cfi && needsCfiForVendorSnapshot(mctx) {
-				// We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
-				// determine defaultVariation in sanitizerMutator below.
-				// Instead, just mark SanitizeDep to forcefully create cfi variant.
+type sanitizerSplitMutator struct {
+	sanitizer SanitizerType
+}
+
+// If an APEX is sanitized or not depends on whether it contains at least one
+// sanitized module. Transition mutators cannot propagate information up the
+// dependency graph this way, so we need an auxiliary mutator to do so.
+func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.TopDownMutatorContext) {
+	if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
+		enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			if c, ok := dep.(*Module); ok && c.sanitize.isSanitizerEnabled(s.sanitizer) {
 				enabled = true
-				c.SetSanitizeDep(t)
 			}
-			if enabled {
-				isSanitizableDependencyTag := c.SanitizableDepTagChecker()
-				mctx.WalkDeps(func(child, parent android.Module) bool {
-					if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
-						return false
-					}
-					if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
-						!d.SanitizeNever() &&
-						!d.IsSanitizerExplicitlyDisabled(t) {
-						if t == cfi || t == Hwasan || t == scs || t == Asan {
-							if d.StaticallyLinked() && d.SanitizerSupported(t) {
-								// Rust does not support some of these sanitizers, so we need to check if it's
-								// supported before setting this true.
-								d.SetSanitizeDep(t)
-							}
-						} else {
-							d.SetSanitizeDep(t)
-						}
-					}
-					return true
-				})
+		})
+
+		if enabled {
+			sanitizeable.EnableSanitizer(s.sanitizer.name())
+		}
+	}
+}
+
+func (s *sanitizerSplitMutator) Split(ctx android.BaseModuleContext) []string {
+	if c, ok := ctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+		if s.sanitizer == cfi && needsCfiForVendorSnapshot(ctx) {
+			return []string{"", s.sanitizer.variationName()}
+		}
+
+		// If the given sanitizer is not requested in the .bp file for a module, it
+		// won't automatically build the sanitized variation.
+		if !c.IsSanitizerEnabled(s.sanitizer) {
+			return []string{""}
+		}
+
+		if c.Binary() {
+			// If a sanitizer is enabled for a binary, we do not build the version
+			// without the sanitizer
+			return []string{s.sanitizer.variationName()}
+		} else if c.StaticallyLinked() || c.Header() {
+			// For static libraries, we build both versions. Some Make modules
+			// apparently depend on this behavior.
+			return []string{"", s.sanitizer.variationName()}
+		} else {
+			// We only build the requested variation of dynamic libraries
+			return []string{s.sanitizer.variationName()}
+		}
+	}
+
+	if _, ok := ctx.Module().(JniSanitizeable); ok {
+		// TODO: this should call into JniSanitizable.IsSanitizerEnabledForJni but
+		// that is short-circuited for now
+		return []string{""}
+	}
+
+	// If an APEX has a sanitized dependency, we build the APEX in the sanitized
+	// variation. This is useful because such APEXes require extra dependencies.
+	if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
+		enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
+		if enabled {
+			return []string{s.sanitizer.variationName()}
+		} else {
+			return []string{""}
+		}
+	}
+
+	if c, ok := ctx.Module().(*Module); ok {
+		//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
+
+		// Check if it's a snapshot module supporting sanitizer
+		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+			return []string{"", s.sanitizer.variationName()}
+		} else {
+			return []string{""}
+		}
+	}
+
+	return []string{""}
+}
+
+func (s *sanitizerSplitMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if c, ok := ctx.Module().(PlatformSanitizeable); ok {
+		if !c.SanitizableDepTagChecker()(ctx.DepTag()) {
+			// If the dependency is through a non-sanitizable tag, use the
+			// non-sanitized variation
+			return ""
+		}
+
+		return sourceVariation
+	} else if _, ok := ctx.Module().(JniSanitizeable); ok {
+		// TODO: this should call into JniSanitizable.IsSanitizerEnabledForJni but
+		// that is short-circuited for now
+		return ""
+	} else {
+		// Otherwise, do not rock the boat.
+		return sourceVariation
+	}
+}
+
+func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if d, ok := ctx.Module().(PlatformSanitizeable); ok {
+		if dm, ok := ctx.Module().(*Module); ok {
+			if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+				return incomingVariation
 			}
-		} else if jniSanitizeable, ok := mctx.Module().(JniSanitizeable); ok {
-			// If it's a Java module with native dependencies through jni,
-			// set the sanitizer for them
-			if jniSanitizeable.IsSanitizerEnabledForJni(mctx, t.name()) {
-				mctx.VisitDirectDeps(func(child android.Module) {
-					if c, ok := child.(PlatformSanitizeable); ok &&
-						mctx.OtherModuleDependencyTag(child) == JniFuzzLibTag &&
-						c.SanitizePropDefined() &&
-						!c.SanitizeNever() &&
-						!c.IsSanitizerExplicitlyDisabled(t) {
-						c.SetSanitizeDep(t)
-					}
-				})
+		}
+
+		if !d.SanitizePropDefined() ||
+			d.SanitizeNever() ||
+			d.IsSanitizerExplicitlyDisabled(s.sanitizer) ||
+			!d.SanitizerSupported(s.sanitizer) {
+			// If a module opts out of a sanitizer, use its non-sanitized variation
+			return ""
+		}
+
+		// Binaries are always built in the variation they requested.
+		if d.Binary() {
+			if d.IsSanitizerEnabled(s.sanitizer) {
+				return s.sanitizer.variationName()
+			} else {
+				return ""
 			}
-		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
-			// If an APEX module includes a lib which is enabled for a sanitizer T, then
-			// the APEX module is also enabled for the same sanitizer type.
-			mctx.VisitDirectDeps(func(child android.Module) {
-				if c, ok := child.(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
-					sanitizeable.EnableSanitizer(t.name())
+		}
+
+		// If a shared library requests to be sanitized, it will be built for that
+		// sanitizer. Otherwise, some sanitizers propagate through shared library
+		// dependency edges, some do not.
+		if !d.StaticallyLinked() && !d.Header() {
+			if d.IsSanitizerEnabled(s.sanitizer) {
+				return s.sanitizer.variationName()
+			}
+
+			if s.sanitizer == cfi || s.sanitizer == Hwasan || s.sanitizer == scs || s.sanitizer == Asan {
+				return ""
+			}
+		}
+
+		// Static and header libraries inherit whether they are sanitized from the
+		// module they are linked into
+		return incomingVariation
+	} else if d, ok := ctx.Module().(Sanitizeable); ok {
+		// If an APEX contains a sanitized module, it will be built in the variation
+		// corresponding to that sanitizer.
+		enabled := d.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
+		if enabled {
+			return s.sanitizer.variationName()
+		}
+
+		return incomingVariation
+	}
+
+	return ""
+}
+
+func (s *sanitizerSplitMutator) Mutate(mctx android.BottomUpMutatorContext, variationName string) {
+	sanitizerVariation := variationName == s.sanitizer.variationName()
+
+	if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+		sanitizerEnabled := c.IsSanitizerEnabled(s.sanitizer)
+
+		oneMakeVariation := false
+		if c.StaticallyLinked() || c.Header() {
+			if s.sanitizer != cfi && s.sanitizer != scs && s.sanitizer != Hwasan {
+				// These sanitizers export only one variation to Make. For the rest,
+				// Make targets can depend on both the sanitized and non-sanitized
+				// versions.
+				oneMakeVariation = true
+			}
+		} else if !c.Binary() {
+			// Shared library. These are the sanitizers that do propagate through shared
+			// library dependencies and therefore can cause multiple variations of a
+			// shared library to be built.
+			if s.sanitizer != cfi && s.sanitizer != Hwasan && s.sanitizer != scs && s.sanitizer != Asan {
+				oneMakeVariation = true
+			}
+		}
+
+		if oneMakeVariation {
+			if sanitizerEnabled != sanitizerVariation {
+				c.SetPreventInstall()
+				c.SetHideFromMake()
+			}
+		}
+
+		if sanitizerVariation {
+			c.SetSanitizer(s.sanitizer, true)
+
+			// CFI is incompatible with ASAN so disable it in ASAN variations
+			if s.sanitizer.incompatibleWithCfi() {
+				cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
+				if mctx.Device() && cfiSupported {
+					c.SetSanitizer(cfi, false)
 				}
-			})
+			}
+
+			// locate the asan libraries under /data/asan
+			if !c.Binary() && !c.StaticallyLinked() && !c.Header() && mctx.Device() && s.sanitizer == Asan && sanitizerEnabled {
+				c.SetInSanitizerDir()
+			}
+
+			if c.StaticallyLinked() && c.ExportedToMake() {
+				if s.sanitizer == Hwasan {
+					hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
+				} else if s.sanitizer == cfi {
+					cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
+				}
+			}
+		} else if c.IsSanitizerEnabled(s.sanitizer) {
+			// Disable the sanitizer for the non-sanitized variation
+			c.SetSanitizer(s.sanitizer, false)
+		}
+	} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
+		// If an APEX has sanitized dependencies, it gets a few more dependencies
+		if sanitizerVariation {
+			sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
+		}
+	} else if c, ok := mctx.Module().(*Module); ok {
+		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+			c.linker.(snapshotSanitizer).setSanitizerVariation(s.sanitizer, sanitizerVariation)
+
+			// Export the static lib name to make
+			if c.static() && c.ExportedToMake() {
+				if s.sanitizer == cfi {
+					// use BaseModuleName which is the name for Make.
+					cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+				}
+			}
 		}
 	}
 }
@@ -1217,7 +1385,7 @@
 			}
 
 			// static executable gets static runtime libs
-			depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+			depTag := libraryDependencyTag{Kind: staticLibraryDependency, unexportedSymbols: true}
 			variations := append(mctx.Target().Variations(),
 				blueprint.Variation{Mutator: "link", Variation: "static"})
 			if c.Device() {
@@ -1289,7 +1457,7 @@
 
 type Sanitizeable interface {
 	android.Module
-	IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool
+	IsSanitizerEnabled(config android.Config, sanitizerName string) bool
 	EnableSanitizer(sanitizerName string)
 	AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
 }
@@ -1315,16 +1483,6 @@
 	return c.sanitize.isSanitizerEnabled(t)
 }
 
-func (c *Module) SanitizeDep(t SanitizerType) bool {
-	for _, e := range c.sanitize.Properties.SanitizeDepTypes {
-		if t == e {
-			return true
-		}
-	}
-
-	return false
-}
-
 func (c *Module) StaticallyLinked() bool {
 	return c.static()
 }
@@ -1341,120 +1499,8 @@
 	}
 }
 
-func (c *Module) SetSanitizeDep(t SanitizerType) {
-	if !c.SanitizeDep(t) {
-		c.sanitize.Properties.SanitizeDepTypes = append(c.sanitize.Properties.SanitizeDepTypes, t)
-	}
-}
-
 var _ PlatformSanitizeable = (*Module)(nil)
 
-// Create sanitized variants for modules that need them
-func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
-	return func(mctx android.BottomUpMutatorContext) {
-		if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
-
-			// Make sure we're not setting CFI to any value if it's not supported.
-			cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
-
-			if c.Binary() && c.IsSanitizerEnabled(t) {
-				modules := mctx.CreateVariations(t.variationName())
-				modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
-			} else if c.IsSanitizerEnabled(t) || c.SanitizeDep(t) {
-				isSanitizerEnabled := c.IsSanitizerEnabled(t)
-				if c.StaticallyLinked() || c.Header() || t == Fuzzer {
-					// Static and header libs are split into non-sanitized and sanitized variants.
-					// Shared libs are not split. However, for asan and fuzzer, we split even for shared
-					// libs because a library sanitized for asan/fuzzer can't be linked from a library
-					// that isn't sanitized for asan/fuzzer.
-					//
-					// Note for defaultVariation: since we don't split for shared libs but for static/header
-					// libs, it is possible for the sanitized variant of a static/header lib to depend
-					// on non-sanitized variant of a shared lib. Such unfulfilled variation causes an
-					// error when the module is split. defaultVariation is the name of the variation that
-					// will be used when such a dangling dependency occurs during the split of the current
-					// module. By setting it to the name of the sanitized variation, the dangling dependency
-					// is redirected to the sanitized variant of the dependent module.
-					defaultVariation := t.variationName()
-					// Not all PlatformSanitizeable modules support the CFI sanitizer
-					mctx.SetDefaultDependencyVariation(&defaultVariation)
-
-					modules := mctx.CreateVariations("", t.variationName())
-					modules[0].(PlatformSanitizeable).SetSanitizer(t, false)
-					modules[1].(PlatformSanitizeable).SetSanitizer(t, true)
-
-					if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
-						// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
-						// are incompatible with cfi
-						modules[1].(PlatformSanitizeable).SetSanitizer(cfi, false)
-					}
-
-					// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
-					// to Make, because the sanitized version has a different suffix in name.
-					// For other types of sanitizers, suppress the variation that is disabled.
-					if t != cfi && t != scs && t != Hwasan {
-						if isSanitizerEnabled {
-							modules[0].(PlatformSanitizeable).SetPreventInstall()
-							modules[0].(PlatformSanitizeable).SetHideFromMake()
-						} else {
-							modules[1].(PlatformSanitizeable).SetPreventInstall()
-							modules[1].(PlatformSanitizeable).SetHideFromMake()
-						}
-					}
-
-					// Export the static lib name to make
-					if c.StaticallyLinked() && c.ExportedToMake() {
-						if t == cfi {
-							cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
-						} else if t == Hwasan {
-							hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
-						}
-					}
-				} else {
-					// Shared libs are not split. Only the sanitized variant is created.
-					modules := mctx.CreateVariations(t.variationName())
-					modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
-
-					// locate the asan libraries under /data/asan
-					if mctx.Device() && t == Asan && isSanitizerEnabled {
-						modules[0].(PlatformSanitizeable).SetInSanitizerDir()
-					}
-
-					if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
-						// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
-						// are incompatible with cfi
-						modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
-					}
-				}
-			}
-		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
-			// APEX and Java fuzz modules fall here
-			sanitizeable.AddSanitizerDependencies(mctx, t.name())
-			mctx.CreateVariations(t.variationName())
-		} else if c, ok := mctx.Module().(*Module); ok {
-			//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
-
-			// Check if it's a snapshot module supporting sanitizer
-			if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
-				// Set default variation as above.
-				defaultVariation := t.variationName()
-				mctx.SetDefaultDependencyVariation(&defaultVariation)
-				modules := mctx.CreateVariations("", t.variationName())
-				modules[0].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, false)
-				modules[1].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, true)
-
-				// Export the static lib name to make
-				if c.static() && c.ExportedToMake() {
-					if t == cfi {
-						// use BaseModuleName which is the name for Make.
-						cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
-					}
-				}
-			}
-		}
-	}
-}
-
 type sanitizerStaticLibsMap struct {
 	// libsMap contains one list of modules per each image and each arch.
 	// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index c1ca034..5d7e7d8 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"runtime"
 	"strings"
 	"testing"
 
@@ -201,6 +202,125 @@
 	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
 }
 
+func TestUbsan(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skip("requires linux")
+	}
+
+	bp := `
+		cc_binary {
+			name: "bin_with_ubsan",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+			],
+			static_libs: [
+				"libstatic",
+				"libnoubsan",
+			],
+			sanitize: {
+				undefined: true,
+			}
+		}
+
+		cc_binary {
+			name: "bin_depends_ubsan",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+			],
+			static_libs: [
+				"libstatic",
+				"libubsan",
+				"libnoubsan",
+			],
+		}
+
+		cc_binary {
+			name: "bin_no_ubsan",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+			],
+			static_libs: [
+				"libstatic",
+				"libnoubsan",
+			],
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+		}
+
+		cc_library_shared {
+			name: "libtransitive",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libubsan",
+			host_supported: true,
+			sanitize: {
+				undefined: true,
+			}
+		}
+
+		cc_library_static {
+			name: "libstatic",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libnoubsan",
+			host_supported: true,
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		staticVariant := variant + "_static"
+
+		minimalRuntime := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
+
+		// The binaries, one with ubsan and one without
+		binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
+		binDependsUbsan := result.ModuleForTests("bin_depends_ubsan", variant)
+		binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
+
+		android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_with_ubsan static libs",
+			strings.Split(binWithUbsan.Rule("ld").Args["libFlags"], " "),
+			minimalRuntime.OutputFiles(t, "")[0].String())
+
+		android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_depends_ubsan static libs",
+			strings.Split(binDependsUbsan.Rule("ld").Args["libFlags"], " "),
+			minimalRuntime.OutputFiles(t, "")[0].String())
+
+		android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_no_ubsan static libs",
+			strings.Split(binNoUbsan.Rule("ld").Args["libFlags"], " "),
+			minimalRuntime.OutputFiles(t, "")[0].String())
+
+		android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_with_ubsan",
+			strings.Split(binWithUbsan.Rule("ld").Args["ldFlags"], " "),
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+
+		android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan static libs",
+			strings.Split(binDependsUbsan.Rule("ld").Args["ldFlags"], " "),
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+
+		android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_no_ubsan static libs",
+			strings.Split(binNoUbsan.Rule("ld").Args["ldFlags"], " "),
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+	}
+
+	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
 type MemtagNoteType int
 
 const (
diff --git a/cc/stl.go b/cc/stl.go
index 0f2a878..85a06da 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -25,6 +25,16 @@
 	return family
 }
 
+func deduplicateStlInput(stl string) string {
+	switch stl {
+	case "c++_shared":
+		return "libc++"
+	case "c++_static":
+		return "libc++_static"
+	}
+	return stl
+}
+
 func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
 	stl := m.SelectedStl()
 	switch stl {
@@ -66,18 +76,18 @@
 		} else if ctx.header() {
 			s = "none"
 		}
+		if s == "none" {
+			return ""
+		}
+		s = deduplicateStlInput(s)
 		if ctx.useSdk() && ctx.Device() {
 			switch s {
 			case "", "system":
 				return "ndk_system"
-			case "c++_shared", "c++_static":
-				return "ndk_lib" + s
 			case "libc++":
 				return "ndk_libc++_shared"
 			case "libc++_static":
 				return "ndk_libc++_static"
-			case "none":
-				return ""
 			default:
 				ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", s)
 				return ""
@@ -87,8 +97,6 @@
 			case "libc++", "libc++_static", "":
 				// Only use static libc++ for Windows.
 				return "libc++_static"
-			case "none":
-				return ""
 			default:
 				ctx.ModuleErrorf("stl: %q is not a supported STL for windows", s)
 				return ""
@@ -97,12 +105,6 @@
 			switch s {
 			case "libc++", "libc++_static":
 				return s
-			case "c++_shared":
-				return "libc++"
-			case "c++_static":
-				return "libc++_static"
-			case "none":
-				return ""
 			case "", "system":
 				if ctx.static() {
 					return "libc++_static"
diff --git a/cc/testing.go b/cc/testing.go
index ecdae8b..077fcda 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -73,7 +73,6 @@
 			nocrt: true,
 			system_shared_libs: [],
 			stl: "none",
-			srcs: [""],
 			check_elf_files: false,
 			sanitize: {
 				never: true,
@@ -84,6 +83,7 @@
 			name: "libcompiler_rt-extras",
 			defaults: ["toolchain_libs_defaults"],
 			vendor_ramdisk_available: true,
+			srcs: [""],
 		}
 
 		cc_prebuilt_library_static {
@@ -93,11 +93,13 @@
 	        vendor_available: true,
 			vendor_ramdisk_available: true,
 			native_bridge_supported: true,
+			srcs: [""],
 		}
 
 		cc_prebuilt_library_shared {
 			name: "libclang_rt.hwasan",
 			defaults: ["toolchain_libs_defaults"],
+			srcs: [""],
 		}
 
 		cc_prebuilt_library_static {
@@ -108,6 +110,7 @@
 			],
 			vendor_ramdisk_available: true,
 			native_bridge_supported: true,
+			srcs: [""],
 		}
 
 		cc_prebuilt_library_static {
@@ -116,17 +119,34 @@
 				"linux_bionic_supported",
 				"toolchain_libs_defaults",
 			],
+			srcs: [""],
 		}
 
 		// Needed for sanitizer
 		cc_prebuilt_library_shared {
 			name: "libclang_rt.ubsan_standalone",
 			defaults: ["toolchain_libs_defaults"],
+			srcs: [""],
 		}
 
 		cc_prebuilt_library_static {
 			name: "libclang_rt.ubsan_minimal",
 			defaults: ["toolchain_libs_defaults"],
+			host_supported: true,
+			target: {
+				android_arm64: {
+					srcs: ["libclang_rt.ubsan_minimal.android_arm64.a"],
+				},
+				android_arm: {
+					srcs: ["libclang_rt.ubsan_minimal.android_arm.a"],
+				},
+				linux_glibc_x86_64: {
+					srcs: ["libclang_rt.ubsan_minimal.x86_64.a"],
+				},
+				linux_glibc_x86: {
+					srcs: ["libclang_rt.ubsan_minimal.x86.a"],
+				},
+			},
 		}
 
 		cc_library {
@@ -546,6 +566,11 @@
 		"defaults/cc/common/crtend_so.c":            nil,
 		"defaults/cc/common/crtend.c":               nil,
 		"defaults/cc/common/crtbrand.c":             nil,
+
+		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil,
+		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a":   nil,
+		"defaults/cc/common/libclang_rt.ubsan_minimal.x86_64.a":        nil,
+		"defaults/cc/common/libclang_rt.ubsan_minimal.x86.a":           nil,
 	}.AddToFixture(),
 
 	// Place the default cc test modules that are common to all platforms in a location that will not
diff --git a/cc/tidy.go b/cc/tidy.go
index ac1521b..6b5d572 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"fmt"
 	"path/filepath"
 	"regexp"
 	"strings"
@@ -62,6 +63,11 @@
 	return []interface{}{&tidy.Properties}
 }
 
+// Set this const to true when all -warnings-as-errors in tidy_flags
+// are replaced with tidy_checks_as_errors.
+// Then, that old style usage will be obsolete and an error.
+const NoWarningsAsErrorsInTidyFlags = true
+
 func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags {
 	CheckBadTidyFlags(ctx, "tidy_flags", tidy.Properties.Tidy_flags)
 	CheckBadTidyChecks(ctx, "tidy_checks", tidy.Properties.Tidy_checks)
@@ -144,61 +150,54 @@
 		tidyChecks += config.TidyChecksForDir(ctx.ModuleDir())
 	}
 	if len(tidy.Properties.Tidy_checks) > 0 {
-		tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
-			config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",")
+		// If Tidy_checks contains "-*", ignore all checks before "-*".
+		localChecks := tidy.Properties.Tidy_checks
+		ignoreGlobalChecks := false
+		for n, check := range tidy.Properties.Tidy_checks {
+			if check == "-*" {
+				ignoreGlobalChecks = true
+				localChecks = tidy.Properties.Tidy_checks[n:]
+			}
+		}
+		if ignoreGlobalChecks {
+			tidyChecks = "-checks=" + strings.Join(esc(ctx, "tidy_checks",
+				config.ClangRewriteTidyChecks(localChecks)), ",")
+		} else {
+			tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
+				config.ClangRewriteTidyChecks(localChecks)), ",")
+		}
 	}
+	tidyChecks = tidyChecks + config.TidyGlobalNoChecks()
 	if ctx.Windows() {
 		// https://b.corp.google.com/issues/120614316
 		// mingw32 has cert-dcl16-c warning in NO_ERROR,
 		// which is used in many Android files.
-		tidyChecks = tidyChecks + ",-cert-dcl16-c"
+		tidyChecks += ",-cert-dcl16-c"
 	}
-	// https://b.corp.google.com/issues/153464409
-	// many local projects enable cert-* checks, which
-	// trigger bugprone-reserved-identifier.
-	tidyChecks = tidyChecks + ",-bugprone-reserved-identifier*,-cert-dcl51-cpp,-cert-dcl37-c"
-	// http://b/153757728
-	tidyChecks = tidyChecks + ",-readability-qualified-auto"
-	// http://b/155034563
-	tidyChecks = tidyChecks + ",-bugprone-signed-char-misuse"
-	// http://b/155034972
-	tidyChecks = tidyChecks + ",-bugprone-branch-clone"
-	// http://b/193716442
-	tidyChecks = tidyChecks + ",-bugprone-implicit-widening-of-multiplication-result"
-	// Too many existing functions trigger this rule, and fixing it requires large code
-	// refactoring. The cost of maintaining this tidy rule outweighs the benefit it brings.
-	tidyChecks = tidyChecks + ",-bugprone-easily-swappable-parameters"
-	// http://b/216364337 - TODO: Follow-up after compiler update to
-	// disable or fix individual instances.
-	tidyChecks = tidyChecks + ",-cert-err33-c"
+
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
-	if ctx.Config().IsEnvTrue("WITH_TIDY") {
-		// WITH_TIDY=1 enables clang-tidy globally. There could be many unexpected
-		// warnings from new checks and many local tidy_checks_as_errors and
-		// -warnings-as-errors can break a global build.
-		// So allow all clang-tidy warnings.
-		inserted := false
-		for i, s := range flags.TidyFlags {
-			if strings.Contains(s, "-warnings-as-errors=") {
-				// clang-tidy accepts only one -warnings-as-errors
-				// replace the old one
-				re := regexp.MustCompile(`'?-?-warnings-as-errors=[^ ]* *`)
-				newFlag := re.ReplaceAllString(s, "")
-				if newFlag == "" {
-					flags.TidyFlags[i] = "-warnings-as-errors=-*"
-				} else {
-					flags.TidyFlags[i] = newFlag + " -warnings-as-errors=-*"
-				}
-				inserted = true
-				break
+	// Embedding -warnings-as-errors in tidy_flags is error-prone.
+	// It should be replaced with the tidy_checks_as_errors list.
+	for i, s := range flags.TidyFlags {
+		if strings.Contains(s, "-warnings-as-errors=") {
+			if NoWarningsAsErrorsInTidyFlags {
+				ctx.PropertyErrorf("tidy_flags", "should not contain "+s+"; use tidy_checks_as_errors instead.")
+			} else {
+				fmt.Printf("%s: warning: module %s's tidy_flags should not contain %s, which is replaced with -warnings-as-errors=-*; use tidy_checks_as_errors for your own as-error warnings instead.\n",
+					ctx.BlueprintsFile(), ctx.ModuleName(), s)
+				flags.TidyFlags[i] = "-warnings-as-errors=-*"
 			}
+			break // there is at most one -warnings-as-errors
 		}
-		if !inserted {
-			flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
-		}
-	} else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
-		tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",")
+	}
+	// Default clang-tidy flags does not contain -warning-as-errors.
+	// If a module has tidy_checks_as_errors, add the list to -warnings-as-errors
+	// and then append the TidyGlobalNoErrorChecks.
+	if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+		tidyChecksAsErrors := "-warnings-as-errors=" +
+			strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",") +
+			config.TidyGlobalNoErrorChecks()
 		flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
 	}
 	return flags
diff --git a/cc/tidy_test.go b/cc/tidy_test.go
index 339b302..7036ecb 100644
--- a/cc/tidy_test.go
+++ b/cc/tidy_test.go
@@ -16,11 +16,159 @@
 
 import (
 	"fmt"
+	"strings"
 	"testing"
 
 	"android/soong/android"
 )
 
+func TestTidyFlagsWarningsAsErrors(t *testing.T) {
+	// The "tidy_flags" property should not contain -warnings-as-errors.
+	type testCase struct {
+		libName, bp string
+		errorMsg    string   // a negative test; must have error message
+		flags       []string // must have substrings in tidyFlags
+		noFlags     []string // must not have substrings in tidyFlags
+	}
+
+	testCases := []testCase{
+		{
+			"libfoo1",
+			`cc_library_shared { // no warnings-as-errors, good tidy_flags
+			  name: "libfoo1",
+			  srcs: ["foo.c"],
+              tidy_flags: ["-header-filter=dir1/"],
+		    }`,
+			"",
+			[]string{"-header-filter=dir1/"},
+			[]string{"-warnings-as-errors"},
+		},
+		{
+			"libfoo2",
+			`cc_library_shared { // good use of tidy_checks_as_errors
+			  name: "libfoo2",
+			  srcs: ["foo.c"],
+			  tidy_checks_as_errors: ["xyz-*", "abc"],
+		    }`,
+			"",
+			[]string{
+				"-header-filter=^", // there is a default header filter
+				"-warnings-as-errors='xyz-*',abc,${config.TidyGlobalNoErrorChecks}",
+			},
+			[]string{},
+		},
+	}
+	if NoWarningsAsErrorsInTidyFlags {
+		testCases = append(testCases, testCase{
+			"libfoo3",
+			`cc_library_shared { // bad use of -warnings-as-errors in tidy_flags
+					  name: "libfoo3",
+					  srcs: ["foo.c"],
+		              tidy_flags: [
+		                "-header-filters=.*",
+					    "-warnings-as-errors=xyz-*",
+		              ],
+				    }`,
+			`module "libfoo3" .*: tidy_flags: should not contain .*;` +
+				` use tidy_checks_as_errors instead`,
+			[]string{},
+			[]string{},
+		})
+	}
+	for _, test := range testCases {
+		if test.errorMsg != "" {
+			testCcError(t, test.errorMsg, test.bp)
+			continue
+		}
+		variant := "android_arm64_armv8-a_shared"
+		ctx := testCc(t, test.bp)
+		t.Run("caseTidyFlags", func(t *testing.T) {
+			flags := ctx.ModuleForTests(test.libName, variant).Rule("clangTidy").Args["tidyFlags"]
+			for _, flag := range test.flags {
+				if !strings.Contains(flags, flag) {
+					t.Errorf("tidyFlags %v for %s does not contain %s.", flags, test.libName, flag)
+				}
+			}
+			for _, flag := range test.noFlags {
+				if strings.Contains(flags, flag) {
+					t.Errorf("tidyFlags %v for %s should not contain %s.", flags, test.libName, flag)
+				}
+			}
+		})
+	}
+}
+
+func TestTidyChecks(t *testing.T) {
+	// The "tidy_checks" property defines additional checks appended
+	// to global default. But there are some checks disabled after
+	// the local tidy_checks.
+	bp := `
+		cc_library_shared { // has global checks + extraGlobalChecks
+			name: "libfoo_1",
+			srcs: ["foo.c"],
+		}
+		cc_library_shared { // has only local checks + extraGlobalChecks
+			name: "libfoo_2",
+			srcs: ["foo.c"],
+			tidy_checks: ["-*", "xyz-*"],
+		}
+		cc_library_shared { // has global checks + local checks + extraGlobalChecks
+			name: "libfoo_3",
+			srcs: ["foo.c"],
+			tidy_checks: ["-abc*", "xyz-*", "mycheck"],
+		}
+		cc_library_shared { // has only local checks after "-*" + extraGlobalChecks
+			name: "libfoo_4",
+			srcs: ["foo.c"],
+			tidy_checks: ["-abc*", "xyz-*", "mycheck", "-*", "xyz-*"],
+		}`
+	ctx := testCc(t, bp)
+
+	globalChecks := "-checks=${config.TidyDefaultGlobalChecks},"
+	firstXyzChecks := "-checks='-*','xyz-*',"
+	localXyzChecks := "'-*','xyz-*'"
+	localAbcChecks := "'-abc*','xyz-*',mycheck"
+	extraGlobalChecks := ",${config.TidyGlobalNoChecks}"
+	testCases := []struct {
+		libNumber int      // 1,2,3,...
+		checks    []string // must have substrings in -checks
+		noChecks  []string // must not have substrings in -checks
+	}{
+		{1, []string{globalChecks, extraGlobalChecks}, []string{localXyzChecks, localAbcChecks}},
+		{2, []string{firstXyzChecks, extraGlobalChecks}, []string{globalChecks, localAbcChecks}},
+		{3, []string{globalChecks, localAbcChecks, extraGlobalChecks}, []string{localXyzChecks}},
+		{4, []string{firstXyzChecks, extraGlobalChecks}, []string{globalChecks, localAbcChecks}},
+	}
+	t.Run("caseTidyChecks", func(t *testing.T) {
+		variant := "android_arm64_armv8-a_shared"
+		for _, test := range testCases {
+			libName := fmt.Sprintf("libfoo_%d", test.libNumber)
+			flags := ctx.ModuleForTests(libName, variant).Rule("clangTidy").Args["tidyFlags"]
+			splitFlags := strings.Split(flags, " ")
+			foundCheckFlag := false
+			for _, flag := range splitFlags {
+				if strings.HasPrefix(flag, "-checks=") {
+					foundCheckFlag = true
+					for _, check := range test.checks {
+						if !strings.Contains(flag, check) {
+							t.Errorf("tidyFlags for %s does not contain %s.", libName, check)
+						}
+					}
+					for _, check := range test.noChecks {
+						if strings.Contains(flag, check) {
+							t.Errorf("tidyFlags for %s should not contain %s.", libName, check)
+						}
+					}
+					break
+				}
+			}
+			if !foundCheckFlag {
+				t.Errorf("tidyFlags for %s does not contain -checks=.", libName)
+			}
+		}
+	})
+}
+
 func TestWithTidy(t *testing.T) {
 	// When WITH_TIDY=1 or (ALLOW_LOCAL_TIDY_TRUE=1 and local tidy:true)
 	// a C++ library should depend on .tidy files.
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index ad379d5..8a3d6e0 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -139,11 +139,12 @@
 	bazelHook := func() error {
 		ctx.EventHandler.Begin("bazel")
 		defer ctx.EventHandler.End("bazel")
-		return configuration.BazelContext.InvokeBazel()
+		return configuration.BazelContext.InvokeBazel(configuration)
 	}
 	ctx.SetBeforePrepareBuildActionsHook(bazelHook)
 
 	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, ctx.Context, configuration)
+	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 	globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
 	ninjaDeps = append(ninjaDeps, globListFiles...)
@@ -165,8 +166,7 @@
 	touch(shared.JoinPath(topDir, queryviewMarker))
 }
 
-func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler) {
-	metricsDir := configuration.Getenv("LOG_DIR")
+func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
 	if len(metricsDir) < 1 {
 		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
 		os.Exit(1)
@@ -220,7 +220,7 @@
 // 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(configuration android.Config, extraNinjaDeps []string) string {
+func doChosenActivity(configuration android.Config, extraNinjaDeps []string, logDir string) string {
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
 	generateBazelWorkspace := bp2buildMarker != ""
 	generateQueryView := bazelQueryViewDir != ""
@@ -284,7 +284,7 @@
 		}
 	}
 
-	writeMetrics(configuration, *ctx.EventHandler)
+	writeMetrics(configuration, *ctx.EventHandler, logDir)
 	return cmdlineArgs.OutFile
 }
 
@@ -340,7 +340,11 @@
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
 	}
 
-	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+	// Bypass configuration.Getenv, as LOG_DIR does not need to be dependency tracked. By definition, it will
+	// change between every CI build, so tracking it would require re-running Soong for every build.
+	logDir := availableEnv["LOG_DIR"]
+
+	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps, logDir)
 
 	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
@@ -442,6 +446,8 @@
 
 	// FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite symlink expansion error for Bazel
 	excludes = append(excludes, "external/autotest/venv/autotest_lib")
+	excludes = append(excludes, "external/autotest/autotest_lib")
+	excludes = append(excludes, "external/autotest/client/autotest_lib/client")
 
 	// FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison
 	// It contains several symlinks back to real source dirs, and those source dirs contain BUILD files we want to ignore
@@ -474,88 +480,88 @@
 // Bazel BUILD files instead of Ninja files.
 func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
 	eventHandler := metrics.EventHandler{}
-	eventHandler.Begin("bp2build")
+	var metrics bp2build.CodegenMetrics
+	eventHandler.Do("bp2build", func() {
 
-	// Register an alternate set of singletons and mutators for bazel
-	// conversion for Bazel conversion.
-	bp2buildCtx := android.NewContext(configuration)
+		// Register an alternate set of singletons and mutators for bazel
+		// 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()
+		// 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())
-	bp2buildCtx.SetNameInterface(newNameResolver(configuration))
-	bp2buildCtx.RegisterForBazelConversion()
+		// Propagate "allow misssing dependencies" bit. This is normally set in
+		// newContext(), but we create bp2buildCtx without calling that method.
+		bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
+		bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+		bp2buildCtx.RegisterForBazelConversion()
 
-	// The bp2build process is a purely functional process that only depends on
-	// Android.bp files. It must not depend on the values of per-build product
-	// configurations or variables, since those will generate different BUILD
-	// files based on how the user has configured their tree.
-	bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
-	modulePaths, err := bp2buildCtx.ListModulePaths(".")
-	if err != nil {
-		panic(err)
-	}
+		// The bp2build process is a purely functional process that only depends on
+		// Android.bp files. It must not depend on the values of per-build product
+		// configurations or variables, since those will generate different BUILD
+		// files based on how the user has configured their tree.
+		bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+		modulePaths, err := bp2buildCtx.ListModulePaths(".")
+		if err != nil {
+			panic(err)
+		}
 
-	extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+		extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
 
-	// Run the loading and analysis pipeline to prepare the graph of regular
-	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
-	// from the regular Modules.
-	blueprintArgs := cmdlineArgs
-	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
-	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+		// Run the loading and analysis pipeline to prepare the graph of regular
+		// Modules parsed from Android.bp files, and the BazelTargetModules mapped
+		// from the regular Modules.
+		blueprintArgs := cmdlineArgs
+		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
+		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
-	ninjaDeps = append(ninjaDeps, globListFiles...)
+		globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
+		ninjaDeps = append(ninjaDeps, globListFiles...)
 
-	// Run the code-generation phase to convert BazelTargetModules to BUILD files
-	// and print conversion metrics to the user.
-	codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
-	metrics := bp2build.Codegen(codegenContext)
+		// Run the code-generation phase to convert BazelTargetModules to BUILD files
+		// and print conversion metrics to the user.
+		codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
+		metrics = bp2build.Codegen(codegenContext)
 
-	generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
-	workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
+		generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
+		workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
 
-	excludes := []string{
-		"bazel-bin",
-		"bazel-genfiles",
-		"bazel-out",
-		"bazel-testlogs",
-		"bazel-" + filepath.Base(topDir),
-	}
+		excludes := []string{
+			"bazel-bin",
+			"bazel-genfiles",
+			"bazel-out",
+			"bazel-testlogs",
+			"bazel-" + filepath.Base(topDir),
+		}
 
-	if outDir[0] != '/' {
-		excludes = append(excludes, outDir)
-	}
+		if outDir[0] != '/' {
+			excludes = append(excludes, outDir)
+		}
 
-	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
-		os.Exit(1)
-	}
+		existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+			os.Exit(1)
+		}
 
-	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
-	excludes = append(excludes, pathsToIgnoredBuildFiles...)
+		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
+		excludes = append(excludes, pathsToIgnoredBuildFiles...)
 
-	excludes = append(excludes, getTemporaryExcludes()...)
+		excludes = append(excludes, getTemporaryExcludes()...)
 
-	symlinkForestDeps := bp2build.PlantSymlinkForest(
-		topDir, workspaceRoot, generatedRoot, ".", excludes)
+		symlinkForestDeps := bp2build.PlantSymlinkForest(
+			topDir, workspaceRoot, generatedRoot, ".", excludes)
 
-	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
-	ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+		ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 
-	writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
+		writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
 
-	// Create an empty bp2build marker file.
-	touch(shared.JoinPath(topDir, bp2buildMarker))
-
-	eventHandler.End("bp2build")
+		// Create an empty bp2build marker file.
+		touch(shared.JoinPath(topDir, bp2buildMarker))
+	})
 
 	// Only report metrics when in bp2build mode. The metrics aren't relevant
 	// for queryview, since that's a total repo-wide conversion and there's a
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index a03a86a..16f994d 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -221,6 +221,7 @@
 		}
 		defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
 		defer met.Dump(soongMetricsFile)
+		defer build.CheckProdCreds(buildCtx, config)
 	}
 
 	// Read the time at the starting point.
diff --git a/compliance/copy_license_metadata/Android.bp b/compliance/copy_license_metadata/Android.bp
new file mode 100644
index 0000000..83019eb
--- /dev/null
+++ b/compliance/copy_license_metadata/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "copy_license_metadata",
+    srcs: [
+        "copy_license_metadata.go",
+    ],
+    deps: [
+        "license_metadata_proto",
+        "golang-protobuf-proto",
+        "golang-protobuf-encoding-prototext",
+        "soong-response",
+    ],
+}
diff --git a/compliance/copy_license_metadata/copy_license_metadata.go b/compliance/copy_license_metadata/copy_license_metadata.go
new file mode 100644
index 0000000..36b9489
--- /dev/null
+++ b/compliance/copy_license_metadata/copy_license_metadata.go
@@ -0,0 +1,144 @@
+// 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 main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
+
+	"android/soong/compliance/license_metadata_proto"
+	"android/soong/response"
+)
+
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
+	var f multiString
+	flags.Var(&f, name, usage)
+	return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+	var expandedArgs []string
+	for _, arg := range os.Args[1:] {
+		if strings.HasPrefix(arg, "@") {
+			f, err := os.Open(strings.TrimPrefix(arg, "@"))
+			if err != nil {
+				fmt.Fprintln(os.Stderr, err.Error())
+				os.Exit(1)
+			}
+
+			respArgs, err := response.ReadRspFile(f)
+			f.Close()
+			if err != nil {
+				fmt.Fprintln(os.Stderr, err.Error())
+				os.Exit(1)
+			}
+			expandedArgs = append(expandedArgs, respArgs...)
+		} else {
+			expandedArgs = append(expandedArgs, arg)
+		}
+	}
+
+	flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+	installed := flags.String("i", "", "installed target")
+	sources := newMultiString(flags, "s", "source (input) file")
+	dep := flags.String("d", "", "license metadata file dependency")
+	outFile := flags.String("o", "", "output file")
+
+	flags.Parse(expandedArgs)
+
+	if len(*dep) == 0 || len(*installed) == 0 || len(*sources) == 0 {
+		flags.Usage()
+		if len(*dep) == 0 {
+			fmt.Fprintf(os.Stderr, "source license metadata (-d flag) required\n")
+		}
+		if len(*sources) == 0 {
+			fmt.Fprintf(os.Stderr, "source copy (-s flag required\n")
+		}
+		if len(*installed) == 0 {
+			fmt.Fprintf(os.Stderr, "installed copy (-i flag) required\n")
+		}
+		os.Exit(1)
+	}
+
+	src_metadata := license_metadata_proto.LicenseMetadata{}
+	err := readMetadata(*dep, &src_metadata)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+		os.Exit(2)
+	}
+
+	metadata := src_metadata
+	metadata.Built = nil
+	metadata.InstallMap = nil
+	metadata.Installed = []string{*installed}
+	metadata.Sources = *sources
+	metadata.Deps = []*license_metadata_proto.AnnotatedDependency{&license_metadata_proto.AnnotatedDependency{
+		File:        proto.String(*dep),
+		Annotations: []string{"static"},
+	}}
+
+	err = writeMetadata(*outFile, &metadata)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+		os.Exit(2)
+	}
+}
+
+func readMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
+	if file == "" {
+		return fmt.Errorf("source metadata file (-d) required")
+	}
+	buf, err := ioutil.ReadFile(file)
+	if err != nil {
+		return fmt.Errorf("error reading textproto %q: %w", file, err)
+	}
+
+	err = prototext.Unmarshal(buf, metadata)
+	if err != nil {
+		return fmt.Errorf("error unmarshalling textproto: %w", err)
+	}
+
+	return nil
+}
+
+func writeMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
+	buf, err := prototext.MarshalOptions{Multiline: true}.Marshal(metadata)
+	if err != nil {
+		return fmt.Errorf("error marshalling textproto: %w", err)
+	}
+
+	if file != "" {
+		err = ioutil.WriteFile(file, buf, 0666)
+		if err != nil {
+			return fmt.Errorf("error writing textproto %q: %w", file, err)
+		}
+	} else {
+		_, _ = os.Stdout.Write(buf)
+	}
+
+	return nil
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index ab9e418..64cd46a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -250,8 +250,9 @@
 }
 
 type globalConfigAndRaw struct {
-	global *GlobalConfig
-	data   []byte
+	global     *GlobalConfig
+	data       []byte
+	pathErrors []error
 }
 
 // GetGlobalConfig returns the global dexpreopt.config that's created in the
@@ -272,16 +273,26 @@
 var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
 var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
 
+type pathContextErrorCollector struct {
+	android.PathContext
+	errors []error
+}
+
+func (p *pathContextErrorCollector) Errorf(format string, args ...interface{}) {
+	p.errors = append(p.errors, fmt.Errorf(format, args...))
+}
+
 func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
-	return ctx.Config().Once(globalConfigOnceKey, func() interface{} {
+	config := ctx.Config().Once(globalConfigOnceKey, func() interface{} {
 		if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
 			panic(err)
 		} else if data != nil {
-			globalConfig, err := ParseGlobalConfig(ctx, data)
+			pathErrorCollectorCtx := &pathContextErrorCollector{PathContext: ctx}
+			globalConfig, err := ParseGlobalConfig(pathErrorCollectorCtx, data)
 			if err != nil {
 				panic(err)
 			}
-			return globalConfigAndRaw{globalConfig, data}
+			return globalConfigAndRaw{globalConfig, data, pathErrorCollectorCtx.errors}
 		}
 
 		// No global config filename set, see if there is a test config set
@@ -291,16 +302,35 @@
 				DisablePreopt:           true,
 				DisablePreoptBootImages: true,
 				DisableGenerateProfile:  true,
-			}, nil}
+			}, nil, nil}
 		})
 	}).(globalConfigAndRaw)
+
+	// Avoid non-deterministic errors by reporting cached path errors on all callers.
+	for _, err := range config.pathErrors {
+		if ctx.Config().AllowMissingDependencies() {
+			// When AllowMissingDependencies it set, report errors through AddMissingDependencies.
+			// If AddMissingDependencies doesn't exist on the current context (for example when
+			// called with a SingletonContext), just swallow the errors since there is no way to
+			// report them.
+			if missingDepsCtx, ok := ctx.(interface {
+				AddMissingDependencies(missingDeps []string)
+			}); ok {
+				missingDepsCtx.AddMissingDependencies([]string{err.Error()})
+			}
+		} else {
+			android.ReportPathErrorf(ctx, "%w", err)
+		}
+	}
+
+	return config
 }
 
 // SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
 // will return. It must be called before the first call to GetGlobalConfig for
 // the config.
 func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
-	config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
+	config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil, nil} })
 }
 
 // This struct is required to convert ModuleConfig from/to JSON.
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 857dfa7..dfcd405 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -12,6 +12,7 @@
         "soong-linkerconfig",
     ],
     srcs: [
+        "avb_add_hash_footer.go",
         "bootimg.go",
         "filesystem.go",
         "logical_partition.go",
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
new file mode 100644
index 0000000..af3bdbe
--- /dev/null
+++ b/filesystem/avb_add_hash_footer.go
@@ -0,0 +1,149 @@
+// 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.
+
+package filesystem
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
+}
+
+type avbAddHashFooter struct {
+	android.ModuleBase
+
+	properties avbAddHashFooterProperties
+
+	output     android.OutputPath
+	installDir android.InstallPath
+}
+
+type avbAddHashFooterProperties struct {
+	// Source file of this image. Can reference a genrule type module with the ":module" syntax.
+	Src *string `android:"path,arch_variant"`
+
+	// Set the name of the output. Defaults to <module_name>.img.
+	Filename *string
+
+	// Name of the image partition. Defaults to the name of this module.
+	Partition_name *string
+
+	// Size of the partition. Defaults to dynamically calculating the size.
+	Partition_size *int64
+
+	// Path to the private key that avbtool will use to sign this image.
+	Private_key *string `android:"path"`
+
+	// Algorithm that avbtool will use to sign this image. Default is SHA256_RSA4096.
+	Algorithm *string
+
+	// The salt in hex. Required for reproducible builds.
+	Salt *string
+}
+
+// The AVB footer adds verification information to the image.
+func avbAddHashFooterFactory() android.Module {
+	module := &avbAddHashFooter{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+func (a *avbAddHashFooter) installFileName() string {
+	return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".img")
+}
+
+func (a *avbAddHashFooter) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	builder := android.NewRuleBuilder(pctx, ctx)
+
+	if a.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing source file")
+		return
+	}
+	input := android.PathForModuleSrc(ctx, proptools.String(a.properties.Src))
+	a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
+	builder.Command().Text("cp").Input(input).Output(a.output)
+
+	cmd := builder.Command().BuiltTool("avbtool").Text("add_hash_footer")
+
+	partition_name := proptools.StringDefault(a.properties.Partition_name, a.BaseModuleName())
+	cmd.FlagWithArg("--partition_name ", partition_name)
+
+	if a.properties.Partition_size == nil {
+		cmd.Flag("--dynamic_partition_size")
+	} else {
+		partition_size := proptools.Int(a.properties.Partition_size)
+		cmd.FlagWithArg("--partition_size ", strconv.Itoa(partition_size))
+	}
+
+	key := android.PathForModuleSrc(ctx, proptools.String(a.properties.Private_key))
+	cmd.FlagWithInput("--key ", key)
+
+	algorithm := proptools.StringDefault(a.properties.Algorithm, "SHA256_RSA4096")
+	cmd.FlagWithArg("--algorithm ", algorithm)
+
+	if a.properties.Salt == nil {
+		ctx.PropertyErrorf("salt", "missing salt value")
+		return
+	}
+	cmd.FlagWithArg("--salt ", proptools.String(a.properties.Salt))
+
+	cmd.FlagWithOutput("--image ", a.output)
+
+	builder.Build("avbAddHashFooter", fmt.Sprintf("avbAddHashFooter %s", ctx.ModuleName()))
+
+	a.installDir = android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(a.installDir, a.installFileName(), a.output)
+}
+
+var _ android.AndroidMkEntriesProvider = (*avbAddHashFooter)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (a *avbAddHashFooter) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(a.output),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", a.installDir.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", a.installFileName())
+			},
+		},
+	}}
+}
+
+var _ Filesystem = (*avbAddHashFooter)(nil)
+
+func (a *avbAddHashFooter) OutputPath() android.Path {
+	return a.output
+}
+
+func (a *avbAddHashFooter) SignedOutputPath() android.Path {
+	return a.OutputPath() // always signed
+}
+
+// TODO(b/185115783): remove when not needed as input to a prebuilt_etc rule
+var _ android.SourceFileProducer = (*avbAddHashFooter)(nil)
+
+// Implements android.SourceFileProducer
+func (a *avbAddHashFooter) Srcs() android.Paths {
+	return append(android.Paths{}, a.output)
+}
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index f726124..1544ea7 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -26,7 +26,8 @@
 var (
 	toRawBinary = pctx.AndroidStaticRule("toRawBinary",
 		blueprint.RuleParams{
-			Command:     "${objcopy} --output-target=binary ${in} ${out}",
+			Command: "${objcopy} --output-target=binary ${in} ${out} &&" +
+				"chmod -x ${out}",
 			CommandDeps: []string{"$objcopy"},
 		},
 		"objcopy")
@@ -76,7 +77,7 @@
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        toRawBinary,
-		Description: "prefix symbols " + outputFile.Base(),
+		Description: "raw binary " + outputFile.Base(),
 		Output:      outputFile,
 		Input:       inputFile,
 		Args: map[string]string{
@@ -93,7 +94,7 @@
 
 // Implements android.AndroidMkEntriesProvider
 func (r *rawBinary) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
+	return []android.AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(r.output),
 	}}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8649b15..b796877 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -266,7 +266,7 @@
 	var bazelOutputFiles android.Paths
 	exportIncludeDirs := map[string]bool{}
 	for _, bazelOutputFile := range filePaths {
-		bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
+		bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), bazelOutputFile))
 		exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true
 	}
 	g.outputFiles = bazelOutputFiles
@@ -590,6 +590,18 @@
 }
 
 func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Allowlist genrule to use depfile until we have a solution to remove it.
+	// TODO(b/235582219): Remove allowlist for genrule
+	if ctx.ModuleType() == "gensrcs" &&
+		!ctx.DeviceConfig().BuildBrokenDepfile() &&
+		Bool(g.properties.Depfile) {
+		ctx.PropertyErrorf(
+			"depfile",
+			"Deprecated to ensure the module type is convertible to Bazel. "+
+				"Try specifying the dependencies explicitly so that there is no need to use depfile. "+
+				"If not possible, the escape hatch is to use BUILD_BROKEN_DEPFILE to bypass the error.")
+	}
+
 	g.generateCommonBuildActions(ctx)
 
 	// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
@@ -805,6 +817,7 @@
 func GenSrcsFactory() android.Module {
 	m := NewGenSrcs()
 	android.InitAndroidModule(m)
+	android.InitBazelModule(m)
 	return m
 }
 
@@ -816,6 +829,13 @@
 	Shard_size *int64
 }
 
+type bazelGensrcsAttributes struct {
+	Srcs             bazel.LabelListAttribute
+	Output_extension *string
+	Tools            bazel.LabelListAttribute
+	Cmd              string
+}
+
 const defaultShardSize = 50
 
 func NewGenRule() *Module {
@@ -880,8 +900,14 @@
 	// Replace in and out variables with $< and $@
 	var cmd string
 	if m.properties.Cmd != nil {
-		cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
-		cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+		if ctx.ModuleType() == "gensrcs" {
+			cmd = strings.ReplaceAll(*m.properties.Cmd, "$(in)", "$(SRC)")
+			cmd = strings.ReplaceAll(cmd, "$(out)", "$(OUT)")
+		} else {
+			cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
+			cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+		}
+
 		genDir := "$(GENDIR)"
 		if t := ctx.ModuleType(); t == "cc_genrule" || t == "java_genrule" || t == "java_genrule_host" {
 			genDir = "$(RULEDIR)"
@@ -901,30 +927,50 @@
 		}
 	}
 
-	// The Out prop is not in an immediately accessible field
-	// in the Module struct, so use GetProperties and cast it
-	// to the known struct prop.
-	var outs []string
-	for _, propIntf := range m.GetProperties() {
-		if props, ok := propIntf.(*genRuleProperties); ok {
-			outs = props.Out
-			break
+	if ctx.ModuleType() == "gensrcs" {
+		// The Output_extension prop is not in an immediately accessible field
+		// in the Module struct, so use GetProperties and cast it
+		// to the known struct prop.
+		var outputExtension *string
+		for _, propIntf := range m.GetProperties() {
+			if props, ok := propIntf.(*genSrcsProperties); ok {
+				outputExtension = props.Output_extension
+				break
+			}
 		}
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "gensrcs",
+			Bzl_load_location: "//build/bazel/rules:gensrcs.bzl",
+		}
+		attrs := &bazelGensrcsAttributes{
+			Srcs:             srcs,
+			Output_extension: outputExtension,
+			Cmd:              cmd,
+			Tools:            tools,
+		}
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+	} else {
+		// The Out prop is not in an immediately accessible field
+		// in the Module struct, so use GetProperties and cast it
+		// to the known struct prop.
+		var outs []string
+		for _, propIntf := range m.GetProperties() {
+			if props, ok := propIntf.(*genRuleProperties); ok {
+				outs = props.Out
+				break
+			}
+		}
+		attrs := &bazelGenruleAttributes{
+			Srcs:  srcs,
+			Outs:  outs,
+			Cmd:   cmd,
+			Tools: tools,
+		}
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class: "genrule",
+		}
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 	}
-
-	attrs := &bazelGenruleAttributes{
-		Srcs:  srcs,
-		Outs:  outs,
-		Cmd:   cmd,
-		Tools: tools,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class: "genrule",
-	}
-
-	// Create the BazelTargetModule.
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 }
 
 var Bool = proptools.Bool
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 1b5cef2..b9be1f7 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -15,6 +15,7 @@
 package genrule
 
 import (
+	"fmt"
 	"os"
 	"regexp"
 	"testing"
@@ -626,6 +627,73 @@
 	}
 }
 
+func TestGensrcsBuildBrokenDepfile(t *testing.T) {
+	tests := []struct {
+		name               string
+		prop               string
+		BuildBrokenDepfile *bool
+		err                string
+	}{
+		{
+			name: `error when BuildBrokenDepfile is set to false`,
+			prop: `
+				depfile: true,
+				cmd: "cat $(in) > $(out) && cat $(depfile)",
+			`,
+			BuildBrokenDepfile: proptools.BoolPtr(false),
+			err:                "depfile: Deprecated to ensure the module type is convertible to Bazel",
+		},
+		{
+			name: `error when BuildBrokenDepfile is not set`,
+			prop: `
+				depfile: true,
+				cmd: "cat $(in) > $(out) && cat $(depfile)",
+			`,
+			err: "depfile: Deprecated to ensure the module type is convertible to Bazel.",
+		},
+		{
+			name: `no error when BuildBrokenDepfile is explicitly set to true`,
+			prop: `
+				depfile: true,
+				cmd: "cat $(in) > $(out) && cat $(depfile)",
+			`,
+			BuildBrokenDepfile: proptools.BoolPtr(true),
+		},
+		{
+			name: `no error if depfile is not set`,
+			prop: `
+				cmd: "cat $(in) > $(out)",
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			bp := fmt.Sprintf(`
+			gensrcs {
+			   name: "foo",
+			   srcs: ["data.txt"],
+			   %s
+			}`, test.prop)
+
+			var expectedErrors []string
+			if test.err != "" {
+				expectedErrors = append(expectedErrors, test.err)
+			}
+			android.GroupFixturePreparers(
+				prepareForGenRuleTest,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					if test.BuildBrokenDepfile != nil {
+						variables.BuildBrokenDepfile = test.BuildBrokenDepfile
+					}
+				}),
+			).
+				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+				RunTestWithBp(t, bp)
+		})
+
+	}
+}
+
 func TestGenruleDefaults(t *testing.T) {
 	bp := `
 				genrule_defaults {
diff --git a/go.mod b/go.mod
index 14444b3..8c1a9f0 100644
--- a/go.mod
+++ b/go.mod
@@ -16,4 +16,4 @@
 // Indirect dep from go-cmp
 exclude golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
 
-go 1.15
+go 1.18
diff --git a/java/Android.bp b/java/Android.bp
index df0d1eb..e25accf 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -87,6 +87,7 @@
         "dexpreopt_bootjars_test.go",
         "droiddoc_test.go",
         "droidstubs_test.go",
+        "genrule_test.go",
         "hiddenapi_singleton_test.go",
         "jacoco_test.go",
         "java_test.go",
@@ -97,6 +98,7 @@
         "platform_compat_config_test.go",
         "plugin_test.go",
         "prebuilt_apis_test.go",
+        "proto_test.go",
         "rro_test.go",
         "sdk_test.go",
         "sdk_library_test.go",
diff --git a/java/androidmk.go b/java/androidmk.go
index 330e594..82ef413 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -623,6 +623,7 @@
 					if dstubs.apiLintReport != nil {
 						fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", dstubs.Name()+"-api-lint",
 							dstubs.apiLintReport.String(), "apilint/"+dstubs.Name()+"-lint-report.txt")
+						fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", dstubs.apiLintReport.String())
 					}
 				}
 				if dstubs.checkNullabilityWarningsTimestamp != nil {
diff --git a/java/app.go b/java/app.go
index 86238d5..23a9816 100755
--- a/java/app.go
+++ b/java/app.go
@@ -583,6 +583,16 @@
 
 	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 
+	var noticeAssetPath android.WritablePath
+	if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+		// The rule to create the notice file can't be generated yet, as the final output path
+		// for the apk isn't known yet.  Add the path where the notice file will be generated to the
+		// aapt rules now before calling aaptBuildActions, the rule to create the notice file will
+		// be generated later.
+		noticeAssetPath = android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+		a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath)
+	}
+
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 
@@ -654,16 +664,21 @@
 		a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
 	}
 
-	if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+	if a.aapt.noticeFile.Valid() {
+		// Generating the notice file rule has to be here after a.outputFile is known.
 		noticeFile := android.PathForModuleOut(ctx, "NOTICE.html.gz")
-		android.BuildNoticeHtmlOutputFromLicenseMetadata(ctx, noticeFile, "", "", a.outputFile.String())
-		noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+		android.BuildNoticeHtmlOutputFromLicenseMetadata(
+			ctx, noticeFile, "", "",
+			[]string{
+				a.installDir.String() + "/",
+				android.PathForModuleInstall(ctx).String() + "/",
+				a.outputFile.String(),
+			})
 		builder := android.NewRuleBuilder(pctx, ctx)
 		builder.Command().Text("cp").
 			Input(noticeFile).
 			Output(noticeAssetPath)
 		builder.Build("notice_dir", "Building notice dir")
-		a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath)
 	}
 
 	for _, split := range a.aapt.splits {
@@ -725,7 +740,7 @@
 		tag := ctx.OtherModuleDependencyTag(module)
 
 		if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
-			if dep, ok := module.(*cc.Module); ok {
+			if dep, ok := module.(cc.LinkableInterface); ok {
 				if dep.IsNdk(ctx.Config()) || dep.IsStubs() {
 					return false
 				}
@@ -1012,7 +1027,7 @@
 func AndroidTestFactory() android.Module {
 	module := &AndroidTest{}
 
-	module.Module.dexProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.EnabledByDefault = false
 
 	module.Module.properties.Instrument = true
 	module.Module.properties.Supports_static_instrumentation = true
@@ -1066,6 +1081,7 @@
 func AndroidTestHelperAppFactory() android.Module {
 	module := &AndroidTestHelperApp{}
 
+	// TODO(b/192032291): Disable by default after auditing downstream usage.
 	module.Module.dexProperties.Optimize.EnabledByDefault = true
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
diff --git a/java/base.go b/java/base.go
index 0900daa..c399c40 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1418,17 +1418,18 @@
 	j.implementationAndResourcesJar = implementationAndResourcesJar
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
+	compileDex := j.dexProperties.Compile_dex
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
-		if j.dexProperties.Compile_dex == nil {
-			j.dexProperties.Compile_dex = proptools.BoolPtr(true)
+		if compileDex == nil {
+			compileDex = proptools.BoolPtr(true)
 		}
 		if j.deviceProperties.Hostdex == nil {
 			j.deviceProperties.Hostdex = proptools.BoolPtr(true)
 		}
 	}
 
-	if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+	if ctx.Device() && (Bool(j.properties.Installable) || Bool(compileDex)) {
 		if j.hasCode(ctx) {
 			if j.shouldInstrumentStatic(ctx) {
 				j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
@@ -1484,11 +1485,30 @@
 	}
 
 	if ctx.Device() {
-		lintSDKVersion := func(sdkSpec android.SdkSpec) android.ApiLevel {
+		lintSDKVersion := func(sdkSpec android.SdkSpec) int {
 			if v := sdkSpec.ApiLevel; !v.IsPreview() {
-				return v
+				return v.FinalInt()
 			} else {
-				return ctx.Config().DefaultAppTargetSdk(ctx)
+				// When running metalava, we pass --version-codename. When that value
+				// is not REL, metalava will add 1 to the --current-version argument.
+				// On old branches, PLATFORM_SDK_VERSION is the latest version (for that
+				// branch) and the codename is REL, except potentially on the most
+				// recent non-master branch. On that branch, it goes through two other
+				// phases before it gets to the phase previously described:
+				//  - PLATFORM_SDK_VERSION has not been updated yet, and the codename
+				//    is not rel. This happens for most of the internal branch's life
+				//    while the branch has been cut but is still under active development.
+				//  - PLATFORM_SDK_VERSION has been set, but the codename is still not
+				//    REL. This happens briefly during the release process. During this
+				//    state the code to add --current-version is commented out, and then
+				//    that commenting out is reverted after the codename is set to REL.
+				// On the master branch, the PLATFORM_SDK_VERSION always represents a
+				// prior version and the codename is always non-REL.
+				//
+				// We need to add one here to match metalava adding 1. Technically
+				// this means that in the state described in the second bullet point
+				// above, this number is 1 higher than it should be.
+				return ctx.Config().PlatformSdkVersion().FinalInt() + 1
 			}
 		}
 
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 0345aad..f08b64b 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -32,19 +32,24 @@
 func init() {
 	registerBootclasspathFragmentBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&bootclasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "bootclasspath_fragments",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(BootclasspathFragmentSdkMemberType)
 }
 
 func registerBootclasspathFragmentBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("bootclasspath_fragment", bootclasspathFragmentFactory)
+	ctx.RegisterModuleType("bootclasspath_fragment_test", testBootclasspathFragmentFactory)
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
 }
 
+// BootclasspathFragmentSdkMemberType is the member type used to add bootclasspath_fragments to
+// the SDK snapshot. It is exported for use by apex.
+var BootclasspathFragmentSdkMemberType = &bootclasspathFragmentMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "bootclasspath_fragments",
+		SupportsSdk:  true,
+	},
+}
+
 type bootclasspathFragmentContentDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
@@ -227,6 +232,9 @@
 	android.SdkBase
 	ClasspathFragmentBase
 
+	// True if this fragment is for testing purposes.
+	testFragment bool
+
 	properties bootclasspathFragmentProperties
 
 	sourceOnlyProperties SourceOnlyBootclasspathProperties
@@ -298,6 +306,12 @@
 	return m
 }
 
+func testBootclasspathFragmentFactory() android.Module {
+	m := bootclasspathFragmentFactory().(*BootclasspathFragmentModule)
+	m.testFragment = true
+	return m
+}
+
 // bootclasspathFragmentInitContentsFromImage will initialize the contents property from the image_name if
 // necessary.
 func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, m *BootclasspathFragmentModule) {
@@ -815,6 +829,26 @@
 	return input
 }
 
+// isTestFragment returns true if the current module is a test bootclasspath_fragment.
+func (b *BootclasspathFragmentModule) isTestFragment() bool {
+	if b.testFragment {
+		return true
+	}
+
+	// TODO(b/194063708): Once test fragments all use bootclasspath_fragment_test
+	// Some temporary exceptions until all test fragments use the
+	// bootclasspath_fragment_test module type.
+	name := b.BaseModuleName()
+	if strings.HasPrefix(name, "test_") {
+		return true
+	}
+	if name == "apex.apexd_test_bootclasspath-fragment" {
+		return true
+	}
+
+	return false
+}
+
 // produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files)
 // for the fragment as well as encoding the flags in the boot dex jars.
 func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
@@ -828,11 +862,18 @@
 	packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes
 	singlePackages := b.sourceOnlyProperties.Hidden_api.Single_packages
 	if splitPackages != nil || packagePrefixes != nil || singlePackages != nil {
-		if splitPackages == nil {
-			splitPackages = []string{"*"}
-		}
 		output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
 			ctx, output.AllFlagsPath, splitPackages, packagePrefixes, singlePackages)
+	} else if !b.isTestFragment() {
+		ctx.ModuleErrorf(`Must specify at least one of the split_packages, package_prefixes and single_packages properties
+  If this is a new bootclasspath_fragment or you are unsure what to do add the
+  the following to the bootclasspath_fragment:
+      hidden_api: {split_packages: ["*"]},
+  and then run the following:
+      m analyze_bcpf && analyze_bcpf --bcpf %q
+  it will analyze the bootclasspath_fragment and provide hints as to what you
+  should specify here. If you are happy with its suggestions then you can add
+  the --fix option and it will fix them for you.`, b.BaseModuleName())
 	}
 
 	return output
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index d3de675..83beb6d 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -121,6 +121,9 @@
 					],
 				},
 			},
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -201,6 +204,9 @@
 			core_platform_api: {
 				stub_libs: ["mycoreplatform.stubs"],
 			},
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -278,3 +284,64 @@
 
 	android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
 }
+
+func TestBootclasspathFragment_Test(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary"),
+	).RunTestWithBp(t, `
+		bootclasspath_fragment {
+			name: "myfragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+		bootclasspath_fragment {
+			name: "test_fragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+		bootclasspath_fragment {
+			name: "apex.apexd_test_bootclasspath-fragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+		bootclasspath_fragment_test {
+			name: "a_test_fragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: [],
+			},
+		}
+
+
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["a.java"],
+			shared_library: false,
+			public: {enabled: true},
+			system: {enabled: true},
+		}
+	`)
+
+	fragment := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "not a test fragment", false, fragment.isTestFragment())
+
+	fragment = result.Module("test_fragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "is a test fragment by prefix", true, fragment.isTestFragment())
+
+	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
+
+	fragment = result.Module("apex.apexd_test_bootclasspath-fragment", "android_common").(*BootclasspathFragmentModule)
+	android.AssertBoolEquals(t, "is a test fragment by name", true, fragment.isTestFragment())
+}
diff --git a/java/config/config.go b/java/config/config.go
index d744002..1d4b242 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -95,6 +95,8 @@
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
 		"-JXmx2048M",
+		// Disable this optimization as it can impact weak reference semantics. See b/233432839.
+		"-JDcom.android.tools.r8.disableEnqueuerDeferredTracing=true",
 	}, dexerJavaVmFlagsList...))
 
 	exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
@@ -157,7 +159,7 @@
 	pctx.HostBinToolVariable("ZipSyncCmd", "zipsync")
 	pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
 	pctx.HostBinToolVariable("D8Cmd", "d8")
-	pctx.HostBinToolVariable("R8Cmd", "r8-compat-proguard")
+	pctx.HostBinToolVariable("R8Cmd", "r8")
 	pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
@@ -175,7 +177,7 @@
 	pctx.HostJavaToolVariable("MetalavaJar", "metalava.jar")
 	pctx.HostJavaToolVariable("DokkaJar", "dokka.jar")
 	pctx.HostJavaToolVariable("JetifierJar", "jetifier.jar")
-	pctx.HostJavaToolVariable("R8Jar", "r8-compat-proguard.jar")
+	pctx.HostJavaToolVariable("R8Jar", "r8.jar")
 	pctx.HostJavaToolVariable("D8Jar", "d8.jar")
 
 	pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
diff --git a/java/dex.go b/java/dex.go
index 13d6e4a..c943938 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -36,8 +36,8 @@
 	Main_dex_rules []string `android:"path"`
 
 	Optimize struct {
-		// If false, disable all optimization.  Defaults to true for android_app and android_test
-		// modules, false for java_library and java_test modules.
+		// If false, disable all optimization.  Defaults to true for android_app and
+		// android_test_helper_app modules, false for android_test, java_library, and java_test modules.
 		Enabled *bool
 		// True if the module containing this has it set by default.
 		EnabledByDefault bool `blueprint:"mutated"`
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 115388b..932fb19 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -697,87 +697,16 @@
 
 	zipSyncCleanupCmd(rule, srcJarDir)
 
-	rule.Build("metalava", "metalava merged")
-
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
+		d.generateCheckCurrentCheckedInApiIsUpToDateBuildRules(ctx)
 
-		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")
+		// 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 String(d.properties.Check_nullability_warnings) != "" {
 		if d.nullabilityWarningsFile == nil {
 			ctx.PropertyErrorf("check_nullability_warnings",
@@ -814,6 +743,84 @@
 	}
 }
 
+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/fuzz.go b/java/fuzz.go
index cf2c981..d0f369f 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -54,11 +54,12 @@
 // cc.JniSanitizeable. It returns a bool for whether a cc dependency should be
 // sanitized for the given sanitizer or not.
 func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool {
-	for _, s := range j.jniProperties.Sanitizers {
-		if sanitizerName == s {
-			return true
-		}
-	}
+	// TODO: once b/231370928 is resolved, please uncomment the loop
+	// 	for _, s := range j.jniProperties.Sanitizers {
+	// 		if sanitizerName == s {
+	// 			return true
+	// 		}
+	// 	}
 	return false
 }
 
@@ -170,6 +171,10 @@
 			return
 		}
 
+		if javaFuzzModule.Target().HostCross {
+			return
+		}
+
 		fuzzModuleValidator := fuzz.FuzzModule{
 			javaFuzzModule.ModuleBase,
 			javaFuzzModule.DefaultableModuleBase,
diff --git a/java/java.go b/java/java.go
index 511e885..6e05159 100644
--- a/java/java.go
+++ b/java/java.go
@@ -164,6 +164,9 @@
 		android.SdkMemberTypeBase{
 			PropertyName: "java_systemserver_libs",
 			SupportsSdk:  true,
+
+			// This was only added in Tiramisu.
+			SupportedBuildReleaseSpecification: "Tiramisu+",
 		},
 		func(ctx android.SdkMemberContext, j *Library) android.Path {
 			// Java systemserver libs are only provided in the SDK to provide access to their dex
@@ -468,6 +471,12 @@
 		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() {
 		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
+	} else if ctx.Config().TargetsJava17() {
+		// Temporary experimental flag to be able to try and build with
+		// java version 17 options.  The flag, if used, just sets Java
+		// 17 as the default version, leaving any components that
+		// target an older version intact.
+		return JAVA_VERSION_17
 	} else {
 		return JAVA_VERSION_11
 	}
@@ -482,6 +491,7 @@
 	JAVA_VERSION_8           = 8
 	JAVA_VERSION_9           = 9
 	JAVA_VERSION_11          = 11
+	JAVA_VERSION_17          = 17
 )
 
 func (v javaVersion) String() string {
@@ -496,11 +506,27 @@
 		return "1.9"
 	case JAVA_VERSION_11:
 		return "11"
+	case JAVA_VERSION_17:
+		return "17"
 	default:
 		return "unsupported"
 	}
 }
 
+func (v javaVersion) StringForKotlinc() string {
+	// $ ./external/kotlinc/bin/kotlinc -jvm-target foo
+	// error: unknown JVM target version: foo
+	// Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17
+	switch v {
+	case JAVA_VERSION_7:
+		return "1.6"
+	case JAVA_VERSION_9:
+		return "9"
+	default:
+		return v.String()
+	}
+}
+
 // Returns true if javac targeting this version uses system modules instead of a bootclasspath.
 func (v javaVersion) usesJavaModules() bool {
 	return v >= 9
@@ -518,8 +544,10 @@
 		return JAVA_VERSION_9
 	case "11":
 		return JAVA_VERSION_11
-	case "10":
-		ctx.PropertyErrorf("java_version", "Java language levels 10 is not supported")
+	case "17":
+		return JAVA_VERSION_17
+	case "10", "12", "13", "14", "15", "16":
+		ctx.PropertyErrorf("java_version", "Java language level %s is not supported", javaVersion)
 		return JAVA_VERSION_UNSUPPORTED
 	default:
 		ctx.PropertyErrorf("java_version", "Unrecognized Java language level")
@@ -853,7 +881,25 @@
 	Data_native_bins []string `android:"arch_variant"`
 
 	// list of device binary modules that should be installed alongside the test
-	Data_device_bins []string `android:"arch_variant"`
+	// This property only adds the first variant of the dependency
+	Data_device_bins_first []string `android:"arch_variant"`
+
+	// list of device binary modules that should be installed alongside the test
+	// This property adds 64bit AND 32bit variants of the dependency
+	Data_device_bins_both []string `android:"arch_variant"`
+
+	// list of device binary modules that should be installed alongside the test
+	// This property only adds 64bit variants of the dependency
+	Data_device_bins_64 []string `android:"arch_variant"`
+
+	// list of device binary modules that should be installed alongside the test
+	// This property adds 32bit variants of the dependency if available, or else
+	// defaults to the 64bit variant
+	Data_device_bins_prefer32 []string `android:"arch_variant"`
+
+	// list of device binary modules that should be installed alongside the test
+	// This property only adds 32bit variants of the dependency
+	Data_device_bins_32 []string `android:"arch_variant"`
 }
 
 type testHelperLibraryProperties struct {
@@ -920,6 +966,83 @@
 	return true
 }
 
+func (j *TestHost) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext) {
+	if len(j.testHostProperties.Data_device_bins_first) > 0 {
+		deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
+		ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins_first...)
+	}
+
+	var maybeAndroid32Target *android.Target
+	var maybeAndroid64Target *android.Target
+	android32TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib32")
+	android64TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib64")
+	if len(android32TargetList) > 0 {
+		maybeAndroid32Target = &android32TargetList[0]
+	}
+	if len(android64TargetList) > 0 {
+		maybeAndroid64Target = &android64TargetList[0]
+	}
+
+	if len(j.testHostProperties.Data_device_bins_both) > 0 {
+		if maybeAndroid32Target == nil && maybeAndroid64Target == nil {
+			ctx.PropertyErrorf("data_device_bins_both", "no device targets available. Targets: %q", ctx.Config().Targets)
+			return
+		}
+		if maybeAndroid32Target != nil {
+			ctx.AddFarVariationDependencies(
+				maybeAndroid32Target.Variations(),
+				dataDeviceBinsTag,
+				j.testHostProperties.Data_device_bins_both...,
+			)
+		}
+		if maybeAndroid64Target != nil {
+			ctx.AddFarVariationDependencies(
+				maybeAndroid64Target.Variations(),
+				dataDeviceBinsTag,
+				j.testHostProperties.Data_device_bins_both...,
+			)
+		}
+	}
+
+	if len(j.testHostProperties.Data_device_bins_prefer32) > 0 {
+		if maybeAndroid32Target != nil {
+			ctx.AddFarVariationDependencies(
+				maybeAndroid32Target.Variations(),
+				dataDeviceBinsTag,
+				j.testHostProperties.Data_device_bins_prefer32...,
+			)
+		} else {
+			if maybeAndroid64Target == nil {
+				ctx.PropertyErrorf("data_device_bins_prefer32", "no device targets available. Targets: %q", ctx.Config().Targets)
+				return
+			}
+			ctx.AddFarVariationDependencies(
+				maybeAndroid64Target.Variations(),
+				dataDeviceBinsTag,
+				j.testHostProperties.Data_device_bins_prefer32...,
+			)
+		}
+	}
+
+	if len(j.testHostProperties.Data_device_bins_32) > 0 {
+		if maybeAndroid32Target == nil {
+			ctx.PropertyErrorf("data_device_bins_32", "cannot find 32bit device target. Targets: %q", ctx.Config().Targets)
+			return
+		}
+		deviceVariations := maybeAndroid32Target.Variations()
+		ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins_32...)
+	}
+
+	if len(j.testHostProperties.Data_device_bins_64) > 0 {
+		if maybeAndroid64Target == nil {
+			ctx.PropertyErrorf("data_device_bins_64", "cannot find 64bit device target. Targets: %q", ctx.Config().Targets)
+			return
+		}
+		deviceVariations := maybeAndroid64Target.Variations()
+		ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins_64...)
+	}
+}
+
 func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if len(j.testHostProperties.Data_native_bins) > 0 {
 		for _, target := range ctx.MultiTargets() {
@@ -927,11 +1050,6 @@
 		}
 	}
 
-	if len(j.testHostProperties.Data_device_bins) > 0 {
-		deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
-		ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins...)
-	}
-
 	if len(j.testProperties.Jni_libs) > 0 {
 		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
@@ -939,6 +1057,8 @@
 		}
 	}
 
+	j.addDataDeviceBinsDeps(ctx)
+
 	j.deps(ctx)
 }
 
@@ -946,17 +1066,40 @@
 	j.extraResources = append(j.extraResources, p)
 }
 
+func (j *TestHost) dataDeviceBins() []string {
+	ret := make([]string, 0,
+		len(j.testHostProperties.Data_device_bins_first)+
+			len(j.testHostProperties.Data_device_bins_both)+
+			len(j.testHostProperties.Data_device_bins_prefer32)+
+			len(j.testHostProperties.Data_device_bins_32)+
+			len(j.testHostProperties.Data_device_bins_64),
+	)
+
+	ret = append(ret, j.testHostProperties.Data_device_bins_first...)
+	ret = append(ret, j.testHostProperties.Data_device_bins_both...)
+	ret = append(ret, j.testHostProperties.Data_device_bins_prefer32...)
+	ret = append(ret, j.testHostProperties.Data_device_bins_32...)
+	ret = append(ret, j.testHostProperties.Data_device_bins_64...)
+
+	return ret
+}
+
 func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var configs []tradefed.Config
-	if len(j.testHostProperties.Data_device_bins) > 0 {
+	dataDeviceBins := j.dataDeviceBins()
+	if len(dataDeviceBins) > 0 {
 		// add Tradefed configuration to push device bins to device for testing
 		remoteDir := filepath.Join("/data/local/tests/unrestricted/", j.Name())
 		options := []tradefed.Option{{Name: "cleanup", Value: "true"}}
-		for _, bin := range j.testHostProperties.Data_device_bins {
+		for _, bin := range dataDeviceBins {
 			fullPath := filepath.Join(remoteDir, bin)
 			options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: fullPath})
 		}
-		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
+		configs = append(configs, tradefed.Object{
+			Type:    "target_preparer",
+			Class:   "com.android.tradefed.targetprep.PushFilePreparer",
+			Options: options,
+		})
 	}
 
 	j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
diff --git a/java/java_test.go b/java/java_test.go
index 4c93824..32b0b0f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -723,9 +723,9 @@
 		t.Errorf("atestNoOptimize should not optimize APK")
 	}
 
-	atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("r8")
+	atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("d8")
 	if atestDefault.Output == nil {
-		t.Errorf("atestDefault should optimize APK")
+		t.Errorf("atestDefault should not optimize APK")
 	}
 }
 
@@ -1498,62 +1498,172 @@
 }
 
 func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) {
-	bp := `
-		java_test_host {
-			name: "foo",
-			srcs: ["test.java"],
-			data_device_bins: ["bar"],
-		}
-
-		cc_binary {
-			name: "bar",
-		}
-	`
-
-	ctx := android.GroupFixturePreparers(
-		PrepareForIntegrationTestWithJava,
-	).RunTestWithBp(t, bp)
-
-	buildOS := ctx.Config.BuildOS.String()
-	fooVariant := ctx.ModuleForTests("foo", buildOS+"_common")
-	barVariant := ctx.ModuleForTests("bar", "android_arm64_armv8-a")
-	fooMod := fooVariant.Module().(*TestHost)
-
-	relocated := barVariant.Output("bar")
-	expectedInput := "out/soong/.intermediates/bar/android_arm64_armv8-a/unstripped/bar"
-	android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
-
-	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0]
-	expectedData := []string{
-		"out/soong/.intermediates/bar/android_arm64_armv8-a/bar:bar",
+	testCases := []struct {
+		dataDeviceBinType  string
+		depCompileMultilib string
+		variants           []string
+		expectedError      string
+	}{
+		{
+			dataDeviceBinType:  "first",
+			depCompileMultilib: "first",
+			variants:           []string{"android_arm64_armv8-a"},
+		},
+		{
+			dataDeviceBinType:  "first",
+			depCompileMultilib: "both",
+			variants:           []string{"android_arm64_armv8-a"},
+		},
+		{
+			// this is true because our testing framework is set up with
+			// Targets ~ [<64bit target>, <32bit target>], where 64bit is "first"
+			dataDeviceBinType:  "first",
+			depCompileMultilib: "32",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "first",
+			depCompileMultilib: "64",
+			variants:           []string{"android_arm64_armv8-a"},
+		},
+		{
+			dataDeviceBinType:  "both",
+			depCompileMultilib: "both",
+			variants: []string{
+				"android_arm_armv7-a-neon",
+				"android_arm64_armv8-a",
+			},
+		},
+		{
+			dataDeviceBinType:  "both",
+			depCompileMultilib: "32",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "both",
+			depCompileMultilib: "64",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "both",
+			depCompileMultilib: "first",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "32",
+			depCompileMultilib: "32",
+			variants:           []string{"android_arm_armv7-a-neon"},
+		},
+		{
+			dataDeviceBinType:  "32",
+			depCompileMultilib: "first",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "32",
+			depCompileMultilib: "both",
+			variants:           []string{"android_arm_armv7-a-neon"},
+		},
+		{
+			dataDeviceBinType:  "32",
+			depCompileMultilib: "64",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "64",
+			depCompileMultilib: "64",
+			variants:           []string{"android_arm64_armv8-a"},
+		},
+		{
+			dataDeviceBinType:  "64",
+			depCompileMultilib: "both",
+			variants:           []string{"android_arm64_armv8-a"},
+		},
+		{
+			dataDeviceBinType:  "64",
+			depCompileMultilib: "first",
+			variants:           []string{"android_arm64_armv8-a"},
+		},
+		{
+			dataDeviceBinType:  "64",
+			depCompileMultilib: "32",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "prefer32",
+			depCompileMultilib: "32",
+			variants:           []string{"android_arm_armv7-a-neon"},
+		},
+		{
+			dataDeviceBinType:  "prefer32",
+			depCompileMultilib: "both",
+			variants:           []string{"android_arm_armv7-a-neon"},
+		},
+		{
+			dataDeviceBinType:  "prefer32",
+			depCompileMultilib: "first",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
+		{
+			dataDeviceBinType:  "prefer32",
+			depCompileMultilib: "64",
+			expectedError:      `Android.bp:2:3: dependency "bar" of "foo" missing variant`,
+		},
 	}
-	actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
-	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData)
-}
 
-func TestDataDeviceBinsAutogenTradefedConfig(t *testing.T) {
-	bp := `
+	bpTemplate := `
 		java_test_host {
 			name: "foo",
 			srcs: ["test.java"],
-			data_device_bins: ["bar"],
+			data_device_bins_%s: ["bar"],
 		}
 
 		cc_binary {
 			name: "bar",
+			compile_multilib: "%s",
 		}
 	`
 
-	ctx := android.GroupFixturePreparers(
-		PrepareForIntegrationTestWithJava,
-	).RunTestWithBp(t, bp)
+	for _, tc := range testCases {
+		bp := fmt.Sprintf(bpTemplate, tc.dataDeviceBinType, tc.depCompileMultilib)
 
-	buildOS := ctx.Config.BuildOS.String()
-	fooModule := ctx.ModuleForTests("foo", buildOS+"_common")
-	expectedAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
+		errorHandler := android.FixtureExpectsNoErrors
+		if tc.expectedError != "" {
+			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError)
+		}
 
-	autogen := fooModule.Rule("autogen")
-	if !strings.Contains(autogen.Args["extraConfigs"], expectedAutogenConfig) {
-		t.Errorf("foo extraConfigs %v does not contain %q", autogen.Args["extraConfigs"], expectedAutogenConfig)
+		testName := fmt.Sprintf(`data_device_bins_%s with compile_multilib:"%s"`, tc.dataDeviceBinType, tc.depCompileMultilib)
+		t.Run(testName, func(t *testing.T) {
+			ctx := android.GroupFixturePreparers(PrepareForIntegrationTestWithJava).
+				ExtendWithErrorHandler(errorHandler).
+				RunTestWithBp(t, bp)
+			if tc.expectedError != "" {
+				return
+			}
+
+			buildOS := ctx.Config.BuildOS.String()
+			fooVariant := ctx.ModuleForTests("foo", buildOS+"_common")
+			fooMod := fooVariant.Module().(*TestHost)
+			entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0]
+
+			expectedAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
+			autogen := fooVariant.Rule("autogen")
+			if !strings.Contains(autogen.Args["extraConfigs"], expectedAutogenConfig) {
+				t.Errorf("foo extraConfigs %v does not contain %q", autogen.Args["extraConfigs"], expectedAutogenConfig)
+			}
+
+			expectedData := []string{}
+			for _, variant := range tc.variants {
+				barVariant := ctx.ModuleForTests("bar", variant)
+				relocated := barVariant.Output("bar")
+				expectedInput := fmt.Sprintf("out/soong/.intermediates/bar/%s/unstripped/bar", variant)
+				android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
+
+				expectedData = append(expectedData, fmt.Sprintf("out/soong/.intermediates/bar/%s/bar:bar", variant))
+			}
+
+			actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+			android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData)
+		})
 	}
 }
diff --git a/java/kotlin.go b/java/kotlin.go
index 903c624..9bff5ea 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -119,9 +119,8 @@
 			"srcJarDir":         android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
 			"kotlinBuildFile":   android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
 			"emptyDir":          android.PathForModuleOut(ctx, "kotlinc", "empty").String(),
-			// http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
-			"kotlinJvmTarget": "1.8",
-			"name":            kotlinName,
+			"kotlinJvmTarget":   flags.javaVersion.StringForKotlinc(),
+			"name":              kotlinName,
 		},
 	})
 }
diff --git a/java/lint.go b/java/lint.go
index 22c9ec4..e276345 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -75,9 +76,9 @@
 	extraLintCheckJars      android.Paths
 	test                    bool
 	library                 bool
-	minSdkVersion           android.ApiLevel
-	targetSdkVersion        android.ApiLevel
-	compileSdkVersion       android.ApiLevel
+	minSdkVersion           int
+	targetSdkVersion        int
+	compileSdkVersion       int
 	compileSdkKind          android.SdkKind
 	javaLanguageLevel       string
 	kotlinLanguageLevel     string
@@ -299,8 +300,8 @@
 		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
 		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
 		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
-		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
-			l.minSdkVersion.String(), l.targetSdkVersion.String()).
+		Textf(`echo "  <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`,
+			l.minSdkVersion, l.targetSdkVersion).
 		Text(`echo "</manifest>"`).
 		Text(") >").Output(manifestPath)
 
@@ -325,7 +326,7 @@
 		return
 	}
 
-	if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
+	if l.minSdkVersion != l.compileSdkVersion {
 		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
 		_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
 		if len(filtered) != 0 {
@@ -427,7 +428,7 @@
 		FlagWithOutput("--html ", html).
 		FlagWithOutput("--text ", text).
 		FlagWithOutput("--xml ", xml).
-		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
+		FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)).
 		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 6257e49..10c9187 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -272,7 +272,9 @@
 	entries := android.AndroidMkEntriesForTest(t, result.TestContext, platformBootclasspath)
 	goals := entries[0].GetDistForGoals(platformBootclasspath)
 	android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore\n", goals[0])
-	android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[1]))
+	android.AssertStringDoesContain(t, "platform dist goals meta check", goals[1], "$(if $(strip $(ALL_TARGETS.")
+	android.AssertStringDoesContain(t, "platform dist goals meta assign", goals[1], "),,$(eval ALL_TARGETS.")
+	android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[2]))
 }
 
 func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
diff --git a/java/sdk.go b/java/sdk.go
index 0dddd40..b0da5af 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -57,6 +57,12 @@
 		return JAVA_VERSION_8
 	} else if sdk.FinalOrFutureInt() <= 31 {
 		return JAVA_VERSION_9
+	} else if ctx.Config().TargetsJava17() {
+		// Temporary experimental flag to be able to try and build with
+		// java version 17 options.  The flag, if used, just sets Java
+		// 17 as the default version, leaving any components that
+		// target an older version intact.
+		return JAVA_VERSION_17
 	} else {
 		return JAVA_VERSION_11
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 8778937..f7e5d9d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -437,7 +437,7 @@
 	// Determines whether a runtime implementation library is built; defaults to false.
 	//
 	// If true then it also prevents the module from being used as a shared module, i.e.
-	// it is as is shared_library: false, was set.
+	// it is as if shared_library: false, was set.
 	Api_only *bool
 
 	// local files that are used within user customized droiddoc options.
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index fa61ea6..a2cd261 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -24,12 +24,7 @@
 func init() {
 	registerSystemserverClasspathBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&systemServerClasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "systemserverclasspath_fragments",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(SystemServerClasspathFragmentSdkMemberType)
 }
 
 func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) {
@@ -38,6 +33,17 @@
 	ctx.RegisterModuleType("prebuilt_systemserverclasspath_fragment", prebuiltSystemServerClasspathModuleFactory)
 }
 
+var SystemServerClasspathFragmentSdkMemberType = &systemServerClasspathFragmentMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "systemserverclasspath_fragments",
+		SupportsSdk:  true,
+
+		// Support for adding systemserverclasspath_fragments to the sdk snapshot was only added in
+		// Tiramisu.
+		SupportedBuildReleaseSpecification: "Tiramisu+",
+	},
+}
+
 type platformSystemServerClasspathModule struct {
 	android.ModuleBase
 
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index e59146b..2707f0c 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -830,21 +830,13 @@
 				pathPattern = append(pathPattern, chunk)
 			}
 		}
-		if pathPattern[0] == "" && len(ctx.includeTops) > 0 {
-			// If pattern starts from the top. restrict it to the directories where
-			// we know inherit-product uses dynamically calculated path.
-			for _, p := range ctx.includeTops {
-				pathPattern[0] = p
-				matchingPaths = append(matchingPaths, ctx.findMatchingPaths(pathPattern)...)
-			}
-		} else {
-			matchingPaths = ctx.findMatchingPaths(pathPattern)
+		if len(pathPattern) == 1 {
+			pathPattern = append(pathPattern, "")
 		}
+		matchingPaths = ctx.findMatchingPaths(pathPattern)
 		needsWarning = pathPattern[0] == "" && len(ctx.includeTops) == 0
 	} else if len(ctx.includeTops) > 0 {
-		for _, p := range ctx.includeTops {
-			matchingPaths = append(matchingPaths, ctx.findMatchingPaths([]string{p, ""})...)
-		}
+		matchingPaths = append(matchingPaths, ctx.findMatchingPaths([]string{"", ""})...)
 	} else {
 		return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
 	}
@@ -872,17 +864,31 @@
 	}
 
 	// Create regular expression from the pattern
-	s_regexp := "^" + regexp.QuoteMeta(pattern[0])
+	regexString := "^" + regexp.QuoteMeta(pattern[0])
 	for _, s := range pattern[1:] {
-		s_regexp += ".*" + regexp.QuoteMeta(s)
+		regexString += ".*" + regexp.QuoteMeta(s)
 	}
-	s_regexp += "$"
-	rex := regexp.MustCompile(s_regexp)
+	regexString += "$"
+	rex := regexp.MustCompile(regexString)
+
+	includeTopRegexString := ""
+	if len(ctx.includeTops) > 0 {
+		for i, top := range ctx.includeTops {
+			if i > 0 {
+				includeTopRegexString += "|"
+			}
+			includeTopRegexString += "^" + regexp.QuoteMeta(top)
+		}
+	} else {
+		includeTopRegexString = ".*"
+	}
+
+	includeTopRegex := regexp.MustCompile(includeTopRegexString)
 
 	// Now match
 	var res []string
 	for _, p := range files {
-		if rex.MatchString(p) {
+		if rex.MatchString(p) && includeTopRegex.MatchString(p) {
 			res = append(res, p)
 		}
 	}
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index a09764c..31555d3 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -1157,6 +1157,8 @@
 #RBC# include_top vendor/foo1
 $(call inherit-product,$(MY_OTHER_PATH))
 #RBC# include_top vendor/foo1
+$(call inherit-product,vendor/$(MY_OTHER_PATH))
+#RBC# include_top vendor/foo1
 $(foreach f,$(MY_MAKEFILES), \
 	$(call inherit-product,$(f)))
 `,
@@ -1180,6 +1182,13 @@
   if not _varmod_init:
     rblf.mkerror("product.mk", "Cannot find %s" % (g.get("MY_OTHER_PATH", "")))
   rblf.inherit(handle, _varmod, _varmod_init)
+  _entry = {
+    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+  }.get("vendor/%s" % g.get("MY_OTHER_PATH", ""))
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("vendor/%s" % g.get("MY_OTHER_PATH", "")))
+  rblf.inherit(handle, _varmod, _varmod_init)
   for f in rblf.words(g.get("MY_MAKEFILES", "")):
     _entry = {
       "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index d1cbd8f..fbb6212 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -35,10 +35,10 @@
 
 	mergeProvenanceMetaData = pctx.AndroidStaticRule("mergeProvenanceMetaData",
 		blueprint.RuleParams{
-			Command: `rm -rf $out $out.temp && ` +
+			Command: `rm -rf $out && ` +
 				`echo "# proto-file: build/soong/provenance/proto/provenance_metadata.proto" > $out && ` +
 				`echo "# proto-message: ProvenanceMetaDataList" >> $out && ` +
-				`touch $out.temp && cat $out.temp $in | grep -v "^#.*" >> $out && rm -rf $out.temp`,
+				`for file in $in; do echo '' >> $out; echo 'metadata {' | cat - $$file | grep -Ev "^#.*|^$$" >> $out; echo '}' >> $out; done`,
 		})
 )
 
diff --git a/python/binary.go b/python/binary.go
index 99c6259..af29bb6 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -38,6 +38,7 @@
 	Srcs           bazel.LabelListAttribute
 	Deps           bazel.LabelListAttribute
 	Python_version *string
+	Imports        bazel.StringListAttribute
 }
 
 func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
@@ -75,6 +76,7 @@
 		Srcs:           baseAttrs.Srcs,
 		Deps:           baseAttrs.Deps,
 		Python_version: python_version,
+		Imports:        baseAttrs.Imports,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/python/library.go b/python/library.go
index 3a27591..df92df4 100644
--- a/python/library.go
+++ b/python/library.go
@@ -17,9 +17,6 @@
 // This file contains the module types for building Python library.
 
 import (
-	"path/filepath"
-	"strings"
-
 	"android/soong/android"
 	"android/soong/bazel"
 
@@ -50,6 +47,10 @@
 	Srcs_version *string
 }
 
+type bazelPythonProtoLibraryAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
 func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
 	// TODO(b/182306917): this doesn't fully handle all nested props versioned
 	// by the python version, which would have been handled by the version split
@@ -68,39 +69,13 @@
 		// do nothing, since python_version defaults to PY2ANDPY3
 	}
 
-	// Bazel normally requires `import path.from.top.of.tree` statements in
-	// python code, but with soong you can directly import modules from libraries.
-	// Add "imports" attributes to the bazel library so it matches soong's behavior.
-	imports := "."
-	if m.properties.Pkg_path != nil {
-		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
-		// pkg_path properly right now. If the folder structure that contains this
-		// Android.bp file matches pkg_path, we can set imports to an appropriate
-		// number of ../..s to emulate moving the files under a pkg_path folder.
-		pkg_path := filepath.Clean(*m.properties.Pkg_path)
-		if strings.HasPrefix(pkg_path, "/") {
-			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
-			return
-		}
-
-		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
-			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
-			return
-		}
-		numFolders := strings.Count(pkg_path, "/") + 1
-		dots := make([]string, numFolders)
-		for i := 0; i < numFolders; i++ {
-			dots[i] = ".."
-		}
-		imports = strings.Join(dots, "/")
-	}
-
 	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+
 	attrs := &bazelPythonLibraryAttributes{
 		Srcs:         baseAttrs.Srcs,
 		Deps:         baseAttrs.Deps,
 		Srcs_version: python_version,
-		Imports:      bazel.MakeStringListAttribute([]string{imports}),
+		Imports:      baseAttrs.Imports,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/python/python.go b/python/python.go
index b100cc3..eb0d3ca 100644
--- a/python/python.go
+++ b/python/python.go
@@ -131,7 +131,8 @@
 	Srcs bazel.LabelListAttribute
 	Deps bazel.LabelListAttribute
 	// Combines Data and Java_data (invariant)
-	Data bazel.LabelListAttribute
+	Data    bazel.LabelListAttribute
+	Imports bazel.StringListAttribute
 }
 
 // Used to store files of current module after expanding dependencies
@@ -207,6 +208,56 @@
 			}
 		}
 	}
+
+	partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
+		"proto": android.ProtoSrcLabelPartition,
+		"py":    bazel.LabelPartition{Keep_remainder: true},
+	})
+	attrs.Srcs = partitionedSrcs["py"]
+
+	if !partitionedSrcs["proto"].IsEmpty() {
+		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
+		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+
+		pyProtoLibraryName := m.Name() + "_py_proto"
+		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+			Rule_class:        "py_proto_library",
+			Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
+		}, android.CommonAttributes{
+			Name: pyProtoLibraryName,
+		}, &bazelPythonProtoLibraryAttributes{
+			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+		})
+
+		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
+	}
+
+	// Bazel normally requires `import path.from.top.of.tree` statements in
+	// python code, but with soong you can directly import modules from libraries.
+	// Add "imports" attributes to the bazel library so it matches soong's behavior.
+	imports := "."
+	if m.properties.Pkg_path != nil {
+		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
+		// pkg_path properly right now. If the folder structure that contains this
+		// Android.bp file matches pkg_path, we can set imports to an appropriate
+		// number of ../..s to emulate moving the files under a pkg_path folder.
+		pkg_path := filepath.Clean(*m.properties.Pkg_path)
+		if strings.HasPrefix(pkg_path, "/") {
+			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
+		}
+
+		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
+			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
+		}
+		numFolders := strings.Count(pkg_path, "/") + 1
+		dots := make([]string, numFolders)
+		for i := 0; i < numFolders; i++ {
+			dots[i] = ".."
+		}
+		imports = strings.Join(dots, "/")
+	}
+	attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
+
 	return attrs
 }
 
@@ -631,7 +682,8 @@
 		// in order to keep stable order of soong_zip params, we sort the keys here.
 		roots := android.SortedStringKeys(relativeRootMap)
 
-		parArgs := []string{}
+		// Use -symlinks=false so that the symlinks in the bazel output directory are followed
+		parArgs := []string{"-symlinks=false"}
 		if pkgPath != "" {
 			// use package path as path prefix
 			parArgs = append(parArgs, `-P `+pkgPath)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index b4626a0..4d723d6 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -41,10 +41,25 @@
 
 	//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
 	_ = pctx.HostBinToolVariable("bindgenCmd", "bindgen")
+	_ = pctx.VariableFunc("bindgenHostPrebuiltTag", func(ctx android.PackageVarContext) string {
+		if ctx.Config().UseHostMusl() {
+			// This is a hack to use the glibc bindgen binary until we have a musl version checked in.
+			return "linux-x86"
+		} else {
+			return "${config.HostPrebuiltTag}"
+		}
+	})
+	_ = pctx.VariableFunc("bindgenClangLibdir", func(ctx android.PackageVarContext) string {
+		if ctx.Config().UseHostMusl() {
+			return "musl/lib64/"
+		} else {
+			return "lib64/"
+		}
+	})
 	_ = pctx.SourcePathVariable("bindgenClang",
-		"${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
+		"${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
 	_ = pctx.SourcePathVariable("bindgenLibClang",
-		"${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/lib64/")
+		"${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/${bindgenClangLibdir}")
 
 	//TODO(ivanlozano) Switch this to RuleBuilder
 	bindgen = pctx.AndroidStaticRule("bindgen",
diff --git a/rust/compiler.go b/rust/compiler.go
index bcd58c8..bf6a488 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -371,8 +371,9 @@
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
-			// If we're building for the build host, use the prebuilt stdlibs
-			if ctx.Target().Os == android.Linux || ctx.Target().Os == android.Darwin {
+			// If we're building for the build host, use the prebuilt stdlibs, unless the host
+			// is linux_bionic which doesn't have prebuilts.
+			if ctx.Host() && !ctx.Target().HostCross && ctx.Target().Os != android.LinuxBionic {
 				stdlib = "prebuilt_" + stdlib
 			}
 			deps.Stdlibs = append(deps.Stdlibs, stdlib)
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 7757c79..ba40cb0 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -11,6 +11,7 @@
     ],
     srcs: [
         "arm_device.go",
+        "arm_linux_host.go",
         "arm64_device.go",
         "global.go",
         "lints.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 7468579..9129b0e 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -30,9 +30,11 @@
 		"system/core/debuggerd/rust",
 		"system/core/libstats/pull_rust",
 		"system/core/trusty/libtrusty-rs",
+		"system/core/trusty/keymint",
 		"system/extras/profcollectd",
 		"system/extras/simpleperf",
 		"system/hardware/interfaces/keystore2",
+		"system/keymint",
 		"system/librustutils",
 		"system/logging/liblog",
 		"system/logging/rust",
diff --git a/rust/config/arm_linux_host.go b/rust/config/arm_linux_host.go
new file mode 100644
index 0000000..22bdaee
--- /dev/null
+++ b/rust/config/arm_linux_host.go
@@ -0,0 +1,147 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	linuxArmRustflags   = []string{}
+	linuxArmLinkflags   = []string{}
+	linuxArm64Rustflags = []string{}
+	linuxArm64Linkflags = []string{}
+)
+
+func init() {
+	registerToolchainFactory(android.LinuxMusl, android.Arm64, linuxMuslArm64ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.Arm, linuxMuslArmToolchainFactory)
+
+	pctx.StaticVariable("LinuxToolchainArmRustFlags", strings.Join(linuxArmRustflags, " "))
+	pctx.StaticVariable("LinuxToolchainArmLinkFlags", strings.Join(linuxArmLinkflags, " "))
+	pctx.StaticVariable("LinuxToolchainArm64RustFlags", strings.Join(linuxArm64Rustflags, " "))
+	pctx.StaticVariable("LinuxToolchainArm64LinkFlags", strings.Join(linuxArm64Linkflags, " "))
+}
+
+// Base 64-bit linux rust toolchain
+type toolchainLinuxArm64 struct {
+	toolchain64Bit
+}
+
+func (toolchainLinuxArm64) Supported() bool {
+	return true
+}
+
+func (toolchainLinuxArm64) Bionic() bool {
+	return false
+}
+
+func (t *toolchainLinuxArm64) Name() string {
+	return "arm64"
+}
+
+func (t *toolchainLinuxArm64) ToolchainLinkFlags() string {
+	// Prepend the lld flags from cc_config so we stay in sync with cc
+	return "${cc_config.LinuxLldflags} ${cc_config.LinuxArm64Lldflags} " +
+		"${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainArm64LinkFlags}"
+}
+
+func (t *toolchainLinuxArm64) ToolchainRustFlags() string {
+	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainArm64RustFlags}"
+}
+
+// Specialization of the 64-bit linux rust toolchain for musl.  Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslArm64 struct {
+	toolchainLinuxArm64
+}
+
+func (t *toolchainLinuxMuslArm64) RustTriple() string {
+	return "aarch64-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslArm64) ToolchainLinkFlags() string {
+	return t.toolchainLinuxArm64.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslArm64) ToolchainRustFlags() string {
+	return t.toolchainLinuxArm64.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslArm64ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslArm64Singleton
+}
+
+// Base 32-bit linux rust toolchain
+type toolchainLinuxArm struct {
+	toolchain32Bit
+}
+
+func (toolchainLinuxArm) Supported() bool {
+	return true
+}
+
+func (toolchainLinuxArm) Bionic() bool {
+	return false
+}
+
+func (t *toolchainLinuxArm) Name() string {
+	return "arm"
+}
+
+func (toolchainLinuxArm) LibclangRuntimeLibraryArch() string {
+	return "arm"
+}
+
+func (toolchainLinuxArm64) LibclangRuntimeLibraryArch() string {
+	return "arm64"
+}
+
+func (t *toolchainLinuxArm) ToolchainLinkFlags() string {
+	// Prepend the lld flags from cc_config so we stay in sync with cc
+	return "${cc_config.LinuxLldflags} ${cc_config.LinuxArmLldflags} " +
+		"${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainArmLinkFlags}"
+}
+
+func (t *toolchainLinuxArm) ToolchainRustFlags() string {
+	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainArmRustFlags}"
+}
+
+// Specialization of the 32-bit linux rust toolchain for musl.  Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslArm struct {
+	toolchainLinuxArm
+}
+
+func (t *toolchainLinuxMuslArm) RustTriple() string {
+	return "arm-unknown-linux-musleabihf"
+}
+
+func (t *toolchainLinuxMuslArm) ToolchainLinkFlags() string {
+	return t.toolchainLinuxArm.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslArm) ToolchainRustFlags() string {
+	return t.toolchainLinuxArm.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslArmToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslArmSingleton
+}
+
+var toolchainLinuxMuslArm64Singleton Toolchain = &toolchainLinuxMuslArm64{}
+var toolchainLinuxMuslArmSingleton Toolchain = &toolchainLinuxMuslArm{}
diff --git a/rust/config/global.go b/rust/config/global.go
index d11665c..3ef0ecb 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.60.0"
+	RustDefaultVersion = "1.61.0.p2"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -78,7 +78,13 @@
 
 func init() {
 	pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
-	pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+	pctx.VariableConfigMethod("HostPrebuiltTag", func(config android.Config) string {
+		if config.UseHostMusl() {
+			return "linux-musl-x86"
+		} else {
+			return config.PrebuiltOS()
+		}
+	})
 
 	pctx.VariableFunc("RustBase", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
diff --git a/rust/coverage.go b/rust/coverage.go
index 651ce6e..5ea481f 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -56,7 +56,7 @@
 		flags.Coverage = true
 		coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
 		flags.RustFlags = append(flags.RustFlags,
-			"-Z instrument-coverage", "-g")
+			"-C instrument-coverage", "-g")
 		flags.LinkFlags = append(flags.LinkFlags,
 			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index f3cd375..0f599d7 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -56,7 +56,7 @@
 	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
 	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
 
-	rustcCoverageFlags := []string{"-Z instrument-coverage", " -g "}
+	rustcCoverageFlags := []string{"-C instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
 		missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
diff --git a/rust/rust.go b/rust/rust.go
index 48419eb..d5d4929 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -695,6 +695,19 @@
 	panic(fmt.Errorf("CoverageFiles called on non-library module: %q", mod.BaseModuleName()))
 }
 
+// Rust does not produce gcno files, and therefore does not produce a coverage archive.
+func (mod *Module) CoverageOutputFile() android.OptionalPath {
+	return android.OptionalPath{}
+}
+
+func (mod *Module) IsNdk(config android.Config) bool {
+	return false
+}
+
+func (mod *Module) IsStubs() bool {
+	return false
+}
+
 func (mod *Module) installable(apexInfo android.ApexInfo) bool {
 	if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) {
 		return false
diff --git a/rust/sanitize.go b/rust/sanitize.go
index aadc00f..a3c5cb5 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -49,8 +49,7 @@
 			Memtag_heap *bool `android:"arch_variant"`
 		}
 	}
-	SanitizerEnabled bool               `blueprint:"mutated"`
-	SanitizeDepTypes []cc.SanitizerType `blueprint:"mutated"`
+	SanitizerEnabled bool `blueprint:"mutated"`
 
 	// Used when we need to place libraries in their own directory, such as ASAN.
 	InSanitizerDir bool `blueprint:"mutated"`
@@ -175,7 +174,7 @@
 	}
 
 	// Enable Memtag for all components in the include paths (for Aarch64 only)
-	if ctx.Arch().ArchType == android.Arm64 {
+	if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
 				s.Memtag_heap = proptools.BoolPtr(true)
@@ -201,7 +200,7 @@
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
 		s.Hwaddress = nil
 	}
 
@@ -216,7 +215,7 @@
 	}
 
 	// Memtag_heap is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
 		s.Memtag_heap = nil
 	}
 
@@ -235,7 +234,7 @@
 	}
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
-		if ctx.Arch().ArchType == android.Arm64 {
+		if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
 			flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
 		} else {
 			flags.RustFlags = append(flags.RustFlags, asanFlags...)
@@ -283,13 +282,13 @@
 		var deps []string
 
 		if mod.IsSanitizerEnabled(cc.Asan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType != android.Arm64) {
+			(mod.IsSanitizerEnabled(cc.Fuzzer) && (mctx.Arch().ArchType != android.Arm64 || !mctx.Os().Bionic())) {
 			variations = append(variations,
 				blueprint.Variation{Mutator: "link", Variation: "shared"})
 			depTag = cc.SharedDepTag()
 			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
 		} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
+			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64 && mctx.Os().Bionic()) {
 			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
 			if binary, ok := mod.compiler.(binaryInterface); ok {
 				if binary.staticallyLinked() {
@@ -444,28 +443,12 @@
 	return mod.sanitize.isSanitizerExplicitlyDisabled(t)
 }
 
-func (mod *Module) SanitizeDep(t cc.SanitizerType) bool {
-	for _, e := range mod.sanitize.Properties.SanitizeDepTypes {
-		if t == e {
-			return true
-		}
-	}
-
-	return false
-}
-
 func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
 	if !Bool(mod.sanitize.Properties.Sanitize.Never) {
 		mod.sanitize.SetSanitizer(t, b)
 	}
 }
 
-func (c *Module) SetSanitizeDep(t cc.SanitizerType) {
-	if !c.SanitizeDep(t) {
-		c.sanitize.Properties.SanitizeDepTypes = append(c.sanitize.Properties.SanitizeDepTypes, t)
-	}
-}
-
 func (mod *Module) StaticallyLinked() bool {
 	if lib, ok := mod.compiler.(libraryInterface); ok {
 		return lib.rlib() || lib.static()
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 2dacdb5..13ddbe7 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -101,6 +101,9 @@
 				image_name: "art",
 				contents: ["mybootlib"],
 				apex_available: ["com.android.art"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			apex_key {
@@ -124,7 +127,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
@@ -152,41 +155,6 @@
     jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_bootclasspath_fragment {
-    name: "mysdk_mybootclasspathfragment@current",
-    sdk_member_name: "mybootclasspathfragment",
-    visibility: ["//visibility:public"],
-    apex_available: ["com.android.art"],
-    image_name: "art",
-    contents: ["mysdk_mybootlib@current"],
-    hidden_api: {
-        annotation_flags: "hiddenapi/annotation-flags.csv",
-        metadata: "hiddenapi/metadata.csv",
-        index: "hiddenapi/index.csv",
-        signature_patterns: "hiddenapi/signature-patterns.csv",
-        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
-        filtered_flags: "hiddenapi/filtered-flags.csv",
-    },
-}
-
-java_import {
-    name: "mysdk_mybootlib@current",
-    sdk_member_name: "mybootlib",
-    visibility: ["//visibility:public"],
-    apex_available: ["com.android.art"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
-    java_boot_libs: ["mysdk_mybootlib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
@@ -222,7 +190,7 @@
 	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
 }
 
-func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, copyRules string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -234,19 +202,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureWithRootAndroidBp(`
-			sdk {
-				name: "mysdk",
-				bootclasspath_fragments: ["mybootclasspathfragment"],
-				java_sdk_libs: [
-					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
-					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
-					// api.stub_libs property. However, it is specified here to ensure that duplicates are
-					// correctly deduped.
-					"mysdklibrary",
-				],
-			}
-
+		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
 				key: "myapex.key",
@@ -270,6 +226,9 @@
 					// This should be automatically added to the sdk_snapshot as a java_sdk_libs module.
 					stub_libs: ["mycoreplatform"],
 				},
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_library {
@@ -317,7 +276,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
@@ -402,121 +361,7 @@
     },
 }
 		`),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_bootclasspath_fragment {
-    name: "mysdk_mybootclasspathfragment@current",
-    sdk_member_name: "mybootclasspathfragment",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    contents: [
-        "mysdk_mybootlib@current",
-        "mysdk_myothersdklibrary@current",
-    ],
-    api: {
-        stub_libs: ["mysdk_mysdklibrary@current"],
-    },
-    core_platform_api: {
-        stub_libs: ["mysdk_mycoreplatform@current"],
-    },
-    hidden_api: {
-        annotation_flags: "hiddenapi/annotation-flags.csv",
-        metadata: "hiddenapi/metadata.csv",
-        index: "hiddenapi/index.csv",
-        signature_patterns: "hiddenapi/signature-patterns.csv",
-        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
-        filtered_flags: "hiddenapi/filtered-flags.csv",
-    },
-}
-
-java_import {
-    name: "mysdk_mybootlib@current",
-    sdk_member_name: "mybootlib",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
-    permitted_packages: ["mybootlib"],
-}
-
-java_sdk_library_import {
-    name: "mysdk_myothersdklibrary@current",
-    sdk_member_name: "myothersdklibrary",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: true,
-    compile_dex: true,
-    permitted_packages: ["myothersdklibrary"],
-    public: {
-        jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
-        current_api: "sdk_library/public/myothersdklibrary.txt",
-        removed_api: "sdk_library/public/myothersdklibrary-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-java_sdk_library_import {
-    name: "mysdk_mysdklibrary@current",
-    sdk_member_name: "mysdklibrary",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: false,
-    public: {
-        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
-        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
-        current_api: "sdk_library/public/mysdklibrary.txt",
-        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-java_sdk_library_import {
-    name: "mysdk_mycoreplatform@current",
-    sdk_member_name: "mycoreplatform",
-    visibility: ["//visibility:public"],
-    apex_available: ["myapex"],
-    shared_library: true,
-    compile_dex: true,
-    public: {
-        jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
-        stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
-        current_api: "sdk_library/public/mycoreplatform.txt",
-        removed_api: "sdk_library/public/mycoreplatform-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
-    java_boot_libs: ["mysdk_mybootlib@current"],
-    java_sdk_libs: [
-        "mysdk_myothersdklibrary@current",
-        "mysdk_mysdklibrary@current",
-        "mysdk_mycoreplatform@current",
-    ],
-}
-		`),
-		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
-.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-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
-.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
-`),
+		checkAllCopyRules(copyRules),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
 			module := result.ModuleForTests("platform-bootclasspath", "android_common")
@@ -553,6 +398,89 @@
 	)
 }
 
+func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: [
+					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
+					// api.stub_libs property. However, it is specified here to ensure that duplicates are
+					// correctly deduped.
+					"mysdklibrary",
+				],
+			}
+		`, `
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-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
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+`)
+	})
+
+	copyBootclasspathFragmentFromApexVariantRules := `
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-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
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+`
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, copyBootclasspathFragmentFromApexVariantRules)
+	})
+
+	t.Run("added-directly-and-indirectly", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+				// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+				// a bootclasspath_fragments module because it is used in the myapex's
+				// bootclasspath_fragments property. However, it is specified here to ensure that duplicates
+				// are correctly deduped.
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: [
+					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
+					// api.stub_libs property. However, it is specified here to ensure that duplicates are
+					// correctly deduped.
+					"mysdklibrary",
+				],
+			}
+		`, copyBootclasspathFragmentFromApexVariantRules)
+	})
+}
+
 // TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
 // bootclasspath_fragment is correctly output to the sdk snapshot.
 func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
@@ -585,6 +513,9 @@
 				contents: [
 					"myotherlib",
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_library {
@@ -614,6 +545,9 @@
 						module: "myotherbootclasspathfragment"
 					},
 				],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_sdk_library {
@@ -630,7 +564,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
@@ -693,6 +627,9 @@
 			image_name: "art",
 			contents: ["mybootlib"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
 		}
 
 		java_library {
@@ -800,6 +737,7 @@
 					unsupported_packages: [
 							"my-unsupported-packages.txt",
 					],
+					split_packages: ["*"],
 				},
 			}
 
@@ -828,7 +766,7 @@
 	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
diff --git a/sdk/bp.go b/sdk/bp.go
index e2dace8..7ff85a1 100644
--- a/sdk/bp.go
+++ b/sdk/bp.go
@@ -298,15 +298,15 @@
 	return module
 }
 
-func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t identityTransformation) transformPropertySetBeforeContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	return propertySet, tag
 }
 
-func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t identityTransformation) transformPropertySetAfterContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	return propertySet, tag
 }
 
-func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t identityTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
 	return value, tag
 }
 
@@ -332,7 +332,7 @@
 	return &moduleCopy
 }
 
-func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t deepCopyTransformation) transformPropertySetBeforeContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	// Create a shallow copy of the properties map. Any mutable property values will be copied by the
 	// transformer.
 	propertiesCopy := make(map[string]interface{})
@@ -354,7 +354,7 @@
 	}, tag
 }
 
-func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t deepCopyTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
 	// Copy string slice, otherwise return value.
 	if values, ok := value.([]string); ok {
 		valuesCopy := make([]string, len(values))
@@ -372,7 +372,7 @@
 	order   []*bpModule
 }
 
-// Add a module.
+// AddModule adds a module to this.
 //
 // The module must have had its "name" property set to a string value that
 // is unique within this file.
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 571d214..265579a 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -120,7 +120,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -145,48 +145,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_sdkmember@current",
-    sdk_member_name: "sdkmember",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/sdkmember.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    compile_multilib: "64",
-    native_shared_libs: ["mysdk_sdkmember@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/sdkmember/android_arm64_armv8-a_shared/sdkmember.so -> android/arm64/lib/sdkmember.so
 .intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> linux_glibc/x86_64/lib/sdkmember.so
@@ -265,7 +223,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_object {
@@ -289,37 +247,6 @@
     },
 }
 `),
-		// Make sure that the generated sdk_snapshot uses the native_objects property.
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_object {
-    name: "mysdk_crtobj@current",
-    sdk_member_name: "crtobj",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    stl: "none",
-    compile_multilib: "both",
-    system_shared_libs: [],
-    sanitize: {
-        never: true,
-    },
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/crtobj.o"],
-        },
-        arm: {
-            srcs: ["arm/lib/crtobj.o"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_objects: ["mysdk_crtobj@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/crtobj/android_arm64_armv8-a/crtobj.o -> arm64/lib/crtobj.o
 .intermediates/crtobj/android_arm_armv7-a-neon/crtobj.o -> arm/lib/crtobj.o
@@ -403,7 +330,7 @@
 	errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -476,7 +403,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -535,7 +462,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mymodule_exports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -554,33 +481,6 @@
     },
 }
 `),
-		// Make sure that the generated sdk_snapshot uses the native_binaries property.
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "mymodule_exports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    arch: {
-        arm64: {
-            srcs: ["arm64/bin/mynativebinary"],
-        },
-        arm: {
-            srcs: ["arm/bin/mynativebinary"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "mymodule_exports@current",
-    visibility: ["//visibility:public"],
-    native_binaries: ["mymodule_exports_mynativebinary@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativebinary/android_arm64_armv8-a/mynativebinary -> arm64/bin/mynativebinary
 .intermediates/mynativebinary/android_arm_armv7-a-neon/mynativebinary -> arm/bin/mynativebinary
@@ -620,7 +520,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -656,68 +556,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "myexports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc: {
-            compile_multilib: "both",
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/bin/mynativebinary"],
-        },
-        windows: {
-            compile_multilib: "64",
-        },
-        windows_x86_64: {
-            enabled: true,
-            srcs: ["windows/x86_64/bin/mynativebinary.exe"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_binaries: ["myexports_mynativebinary@current"],
-    target: {
-        windows: {
-            compile_multilib: "64",
-        },
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-        windows_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativebinary/linux_glibc_x86_64/mynativebinary -> linux_glibc/x86_64/bin/mynativebinary
 .intermediates/mynativebinary/linux_glibc_x86/mynativebinary -> linux_glibc/x86/bin/mynativebinary
@@ -780,7 +618,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -823,69 +661,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "myexports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/bin/mynativebinary"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.so"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    compile_multilib: "64",
-    native_binaries: ["myexports_mynativebinary@current"],
-    native_shared_libs: ["myexports_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativebinary/linux_bionic_x86_64/mynativebinary -> x86_64/bin/mynativebinary
 .intermediates/mynativelib/linux_bionic_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
@@ -918,7 +693,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mymodule_exports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -947,55 +722,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_binary {
-    name: "mymodule_exports_linker@current",
-    sdk_member_name: "linker",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    static_executable: true,
-    nocrt: true,
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/bin/linker"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/bin/linker"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "mymodule_exports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_binaries: ["mymodule_exports_linker@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/linker/linux_glibc_x86_64/linker -> x86_64/bin/linker
 .intermediates/linker/linux_glibc_x86/linker -> x86/bin/linker
@@ -1026,7 +752,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1127,7 +853,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1224,7 +950,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1255,57 +981,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    sdk_version: "minimum",
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.so"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/lib/mynativelib.so"],
-            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_shared_libs: ["mysdk_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
@@ -1351,7 +1026,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1387,68 +1062,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc: {
-            compile_multilib: "both",
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/mynativelib.so"],
-        },
-        windows: {
-            compile_multilib: "64",
-        },
-        windows_x86_64: {
-            enabled: true,
-            srcs: ["windows/x86_64/lib/mynativelib.dll"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_shared_libs: ["mysdk_mynativelib@current"],
-    target: {
-        windows: {
-            compile_multilib: "64",
-        },
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-        windows_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
 .intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
@@ -1479,7 +1092,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1542,7 +1155,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1572,56 +1185,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_static {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.a"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/lib/mynativelib.a"],
-            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_static_libs: ["myexports_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
@@ -1656,7 +1219,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library {
@@ -1688,46 +1251,6 @@
     },
 }
 `),
-		// Make sure that the generated sdk_snapshot uses the native_libs property.
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    vendor_available: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    arch: {
-        arm64: {
-            static: {
-                srcs: ["arm64/lib/mynativelib.a"],
-            },
-            shared: {
-                srcs: ["arm64/lib/mynativelib.so"],
-            },
-        },
-        arm: {
-            static: {
-                srcs: ["arm/lib/mynativelib.a"],
-            },
-            shared: {
-                srcs: ["arm/lib/mynativelib.so"],
-            },
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    native_libs: ["myexports_mynativelib@current"],
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
@@ -1740,6 +1263,229 @@
 	)
 }
 
+func TestSnapshotSameLibraryWithNativeLibsAndNativeSharedLib(t *testing.T) {
+	result := testSdkWithCc(t, `
+		module_exports {
+			host_supported: true,
+			name: "myexports",
+			target: {
+				android: {
+						native_shared_libs: [
+								"mynativelib",
+						],
+				},
+				not_windows: {
+						native_libs: [
+								"mynativelib",
+						],
+				},
+			},
+		}
+
+		cc_library {
+			name: "mynativelib",
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			stl: "none",
+			recovery_available: true,
+			vendor_available: true,
+		}
+	`)
+
+	CheckSnapshot(t, result, "myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    host_supported: true,
+    vendor_available: true,
+    stl: "none",
+    compile_multilib: "both",
+    target: {
+        host: {
+            enabled: false,
+        },
+        android_arm64: {
+            shared: {
+                srcs: ["android/arm64/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+        android_arm: {
+            shared: {
+                srcs: ["android/arm/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            static: {
+                srcs: ["linux_glibc/x86_64/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+            },
+        },
+        linux_glibc_x86: {
+            enabled: true,
+            static: {
+                srcs: ["linux_glibc/x86/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+            },
+        },
+    },
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> android/arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> android/arm/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> linux_glibc/x86_64/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> linux_glibc/x86/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+`),
+	)
+}
+
+func TestSnapshotSameLibraryWithAndroidNativeLibsAndHostNativeSharedLib(t *testing.T) {
+	result := testSdkWithCc(t, `
+		module_exports {
+			host_supported: true,
+			name: "myexports",
+			target: {
+				android: {
+						native_libs: [
+								"mynativelib",
+						],
+				},
+				not_windows: {
+						native_shared_libs: [
+								"mynativelib",
+						],
+				},
+			},
+		}
+
+		cc_library {
+			name: "mynativelib",
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			stl: "none",
+			recovery_available: true,
+			vendor_available: true,
+		}
+	`)
+
+	CheckSnapshot(t, result, "myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    host_supported: true,
+    vendor_available: true,
+    stl: "none",
+    compile_multilib: "both",
+    target: {
+        host: {
+            enabled: false,
+        },
+        android_arm64: {
+            static: {
+                srcs: ["android/arm64/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["android/arm64/lib/mynativelib.so"],
+            },
+        },
+        android_arm: {
+            static: {
+                srcs: ["android/arm/lib/mynativelib.a"],
+            },
+            shared: {
+                srcs: ["android/arm/lib/mynativelib.so"],
+            },
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            shared: {
+                srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+        linux_glibc_x86: {
+            enabled: true,
+            shared: {
+                srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+            },
+            static: {
+                enabled: false,
+            },
+        },
+    },
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> android/arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> android/arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> android/arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> android/arm/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+`),
+	)
+}
+
+func TestSnapshotSameLibraryWithNativeStaticLibsAndNativeSharedLib(t *testing.T) {
+	testSdkError(t, "Incompatible member types", `
+		module_exports {
+			host_supported: true,
+			name: "myexports",
+			target: {
+				android: {
+						native_shared_libs: [
+								"mynativelib",
+						],
+				},
+				not_windows: {
+						native_static_libs: [
+								"mynativelib",
+						],
+				},
+			},
+		}
+
+		cc_library {
+			name: "mynativelib",
+			host_supported: true,
+			srcs: [
+			],
+			stl: "none",
+			recovery_available: true,
+			vendor_available: true,
+		}
+	`)
+}
+
 func TestHostSnapshotWithMultiLib64(t *testing.T) {
 	result := testSdkWithCc(t, `
 		module_exports {
@@ -1771,7 +1517,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1798,51 +1544,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_static {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    installable: false,
-    stl: "none",
-    compile_multilib: "64",
-    export_include_dirs: [
-        "include/myinclude",
-        "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
-    ],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.a"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    compile_multilib: "64",
-    native_static_libs: ["myexports_mynativelib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
@@ -1868,7 +1569,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -1912,7 +1613,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -1997,7 +1698,7 @@
 	`, trait, property))
 
 		CheckSnapshot(t, result, "mysdk", "",
-			checkUnversionedAndroidBpContents(fmt.Sprintf(`
+			checkAndroidBpContents(fmt.Sprintf(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2046,7 +1747,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2072,51 +1773,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_headers {
-    name: "mysdk_mynativeheaders@current",
-    sdk_member_name: "mynativeheaders",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    native_header_libs: ["mysdk_mynativeheaders@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 `),
@@ -2148,7 +1804,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2179,55 +1835,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_headers {
-    name: "mysdk_mynativeheaders@current",
-    sdk_member_name: "mynativeheaders",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_system_include_dirs: ["common_os/include/myinclude"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        android: {
-            export_include_dirs: ["android/include/myinclude-android"],
-        },
-        linux_glibc: {
-            export_include_dirs: ["linux_glibc/include/myinclude-host"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_header_libs: ["mysdk_mynativeheaders@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 myinclude/Test.h -> common_os/include/myinclude/Test.h
 myinclude-android/AndroidTest.h -> android/include/myinclude-android/AndroidTest.h
@@ -2260,7 +1867,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2333,7 +1940,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2367,59 +1974,7 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_sslvariants@current",
-    sdk_member_name: "sslvariants",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    compile_multilib: "both",
-    target: {
-        host: {
-            enabled: false,
-        },
-        android: {
-            system_shared_libs: [],
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/sslvariants.so"],
-        },
-        android_arm: {
-            srcs: ["android/arm/lib/sslvariants.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/sslvariants.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_shared_libs: ["mysdk_sslvariants@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`))
+	)
 }
 
 func TestStubsLibrary(t *testing.T) {
@@ -2444,7 +1999,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2498,7 +2053,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2537,64 +2092,7 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_stubslib@current",
-    sdk_member_name: "stubslib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    compile_multilib: "both",
-    stubs: {
-        versions: [
-            "1",
-            "2",
-            "3",
-            "current",
-        ],
-    },
-    target: {
-        host: {
-            enabled: false,
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/stubslib.so"],
-        },
-        android_arm: {
-            srcs: ["android/arm/lib/stubslib.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/stubslib.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_shared_libs: ["mysdk_stubslib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`))
+	)
 }
 
 func TestUniqueHostSoname(t *testing.T) {
@@ -2613,7 +2111,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -2645,57 +2143,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-cc_prebuilt_library_shared {
-    name: "mysdk_mylib@current",
-    sdk_member_name: "mylib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    installable: false,
-    unique_host_soname: true,
-    compile_multilib: "both",
-    target: {
-        host: {
-            enabled: false,
-        },
-        android_arm64: {
-            srcs: ["android/arm64/lib/mylib.so"],
-        },
-        android_arm: {
-            srcs: ["android/arm/lib/mylib.so"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["linux_glibc/x86/lib/mylib-host.so"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    native_shared_libs: ["mysdk_mylib@current"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/mylib/android_arm64_armv8-a_shared/mylib.so -> android/arm64/lib/mylib.so
 .intermediates/mylib/android_arm_armv7-a-neon_shared/mylib.so -> android/arm/lib/mylib.so
@@ -2728,7 +2175,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index 00073c2..d166add 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -37,23 +37,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_platform_compat_config {
-    name: "mysdk_myconfig@current",
-    sdk_member_name: "myconfig",
-    visibility: ["//visibility:public"],
-    metadata: "compat_configs/myconfig/myconfig_meta.xml",
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    compat_configs: ["mysdk_myconfig@current"],
-}
-`),
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_platform_compat_config {
diff --git a/sdk/exports.go b/sdk/exports.go
index 9a0ba4e..7645d3f 100644
--- a/sdk/exports.go
+++ b/sdk/exports.go
@@ -31,7 +31,7 @@
 	return newSdkModule(true)
 }
 
-// module_exports_snapshot is a versioned snapshot of prebuilt versions of all the exports
+// module_exports_snapshot is a snapshot of prebuilt versions of all the exports
 // of a mainline module.
 func ModuleExportsSnapshotsFactory() android.Module {
 	s := newSdkModule(true)
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 17ddf17..2605fd1 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -43,7 +43,7 @@
 		})
 
 	CheckSnapshot(t, result, "myexports", "package",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -54,22 +54,5 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_libs: ["myexports_myjavalib@current"],
-}
-`),
 	)
 }
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index f33aa26..d25138f 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -96,7 +96,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -108,24 +108,6 @@
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-    permitted_packages: ["pkg.myjavalib"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -160,7 +142,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -173,27 +155,6 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -220,7 +181,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -239,32 +200,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    target: {
-        android: {
-            jars: ["java/android/myjavalib.jar"],
-        },
-        linux_glibc: {
-            jars: ["java/linux_glibc/myjavalib.jar"],
-        },
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
@@ -298,7 +233,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -309,23 +244,6 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_libs: ["myexports_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -361,7 +279,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -373,25 +291,6 @@
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
-    permitted_packages: ["pkg.myjavalib"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_boot_libs: ["myexports_myjavalib@current"],
-}
-
-`),
 		checkAllCopyRules(`
 .intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
@@ -427,7 +326,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -439,24 +338,6 @@
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar"],
-    permitted_packages: ["pkg.myjavalib"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_systemserver_libs: ["myexports_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myexports/common_os/empty -> java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
@@ -490,7 +371,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -503,27 +384,6 @@
     jars: ["java/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavalib.jar"],
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_libs: ["myexports_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
@@ -549,7 +409,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
@@ -561,24 +421,6 @@
     test_config: "java/myjavatests-AndroidTest.xml",
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_test_import {
-    name: "myexports_myjavatests@current",
-    sdk_member_name: "myjavatests",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavatests.jar"],
-    test_config: "java/myjavatests-AndroidTest.xml",
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    java_tests: ["myexports_myjavatests@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavatests/android_common/javac/myjavatests.jar -> java/myjavatests.jar
 .intermediates/myjavatests/android_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
@@ -607,7 +449,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
@@ -621,28 +463,6 @@
     test_config: "java/myjavatests-AndroidTest.xml",
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_test_import {
-    name: "myexports_myjavatests@current",
-    sdk_member_name: "myjavatests",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/myjavatests.jar"],
-    test_config: "java/myjavatests-AndroidTest.xml",
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_tests: ["myexports_myjavatests@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavatests/linux_glibc_common/javac/myjavatests.jar -> java/myjavatests.jar
 .intermediates/myjavatests/linux_glibc_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
@@ -703,7 +523,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -748,59 +568,6 @@
     ],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_exported-system-module@current",
-    sdk_member_name: "exported-system-module",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/exported-system-module.jar"],
-}
-
-java_import {
-    name: "mysdk_system-module@current",
-    sdk_member_name: "system-module",
-    visibility: ["//visibility:private"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/system-module.jar"],
-}
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: false,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-java_system_modules_import {
-    name: "mysdk_my-system-modules@current",
-    sdk_member_name: "my-system-modules",
-    visibility: ["//visibility:public"],
-    libs: [
-        "mysdk_system-module@current",
-        "mysdk_exported-system-module@current",
-        "mysdk_myjavalib.stubs@current",
-    ],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_exported-system-module@current"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-    java_system_modules: ["mysdk_my-system-modules@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
 .intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
@@ -885,7 +652,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -907,36 +674,6 @@
     libs: ["mysdk_system-module"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_system-module@current",
-    sdk_member_name: "system-module",
-    visibility: ["//visibility:private"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/system-module.jar"],
-}
-
-java_system_modules_import {
-    name: "mysdk_my-system-modules@current",
-    sdk_member_name: "my-system-modules",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    libs: ["mysdk_system-module@current"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    java_system_modules: ["mysdk_my-system-modules@current"],
-}
-`),
 		checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac/system-module.jar -> java/system-module.jar"),
 	)
 }
@@ -979,7 +716,7 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -1016,58 +753,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myexports_hostjavalib@current",
-    sdk_member_name: "hostjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    jars: ["java/hostjavalib.jar"],
-}
-
-java_import {
-    name: "myexports_androidjavalib@current",
-    sdk_member_name: "androidjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/androidjavalib.jar"],
-}
-
-java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    target: {
-        android: {
-            jars: ["java/android/myjavalib.jar"],
-        },
-        linux_glibc: {
-            jars: ["java/linux_glibc/myjavalib.jar"],
-        },
-    },
-}
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    host_supported: true,
-    java_libs: ["myexports_myjavalib@current"],
-    target: {
-        android: {
-            java_header_libs: ["myexports_androidjavalib@current"],
-        },
-        linux_glibc: {
-            java_header_libs: ["myexports_hostjavalib@current"],
-        },
-    },
-}
-`),
 		checkAllCopyRules(`
 .intermediates/hostjavalib/linux_glibc_common/javac/hostjavalib.jar -> java/hostjavalib.jar
 .intermediates/androidjavalib/android_common/turbine-combined/androidjavalib.jar -> java/androidjavalib.jar
@@ -1097,7 +782,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1130,45 +815,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: false,
-    permitted_packages: ["pkg.myjavalib"],
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-    system: {
-        jars: ["sdk_library/system/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
-        current_api: "sdk_library/system/myjavalib.txt",
-        removed_api: "sdk_library/system/myjavalib-removed.txt",
-        sdk_version: "system_current",
-    },
-    test: {
-        jars: ["sdk_library/test/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
-        current_api: "sdk_library/test/myjavalib.txt",
-        removed_api: "sdk_library/test/myjavalib-removed.txt",
-        sdk_version: "test_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1185,12 +831,6 @@
 			".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
 		),
-		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
-			// Make sure that the name of the child modules created by a versioned java_sdk_library_import
-			// module is correct, i.e. the suffix is added before the version and not after.
-			result.Module("mysdk_myjavalib.stubs@current", "android_common")
-			result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
-		}),
 	)
 }
 
@@ -1218,7 +858,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1265,7 +905,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1319,7 +959,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1369,7 +1009,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1436,7 +1076,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1454,30 +1094,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "none",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1508,7 +1124,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1526,30 +1142,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "module_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1583,7 +1175,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1608,37 +1200,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-    system: {
-        jars: ["sdk_library/system/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
-        current_api: "sdk_library/system/myjavalib.txt",
-        removed_api: "sdk_library/system/myjavalib-removed.txt",
-        sdk_version: "system_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1679,7 +1240,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1711,44 +1272,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-    system: {
-        jars: ["sdk_library/system/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
-        current_api: "sdk_library/system/myjavalib.txt",
-        removed_api: "sdk_library/system/myjavalib-removed.txt",
-        sdk_version: "system_current",
-    },
-    module_lib: {
-        jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
-        current_api: "sdk_library/module-lib/myjavalib.txt",
-        removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
-        sdk_version: "module_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1790,7 +1313,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1815,37 +1338,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-    system_server: {
-        jars: ["sdk_library/system-server/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/system-server/myjavalib_stub_sources"],
-        current_api: "sdk_library/system-server/myjavalib.txt",
-        removed_api: "sdk_library/system-server/myjavalib-removed.txt",
-        sdk_version: "system_server_current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1881,7 +1373,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1900,31 +1392,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    naming_scheme: "default",
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
@@ -1963,7 +1430,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -1982,31 +1449,6 @@
     },
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    shared_library: true,
-    doctag_files: ["doctags/docs/known_doctags"],
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_myjavalib@current"],
-}
-`),
 		checkAllCopyRules(`
 .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
 .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
diff --git a/sdk/license_sdk_test.go b/sdk/license_sdk_test.go
index 1ef6fe6..829edf1 100644
--- a/sdk/license_sdk_test.go
+++ b/sdk/license_sdk_test.go
@@ -60,7 +60,7 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 package {
@@ -91,44 +91,6 @@
     ],
 }
 		`),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-package {
-    // A default list here prevents the license LSC from adding its own list which would
-    // be unnecessary as every module in the sdk already has its own licenses property.
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    licenses: ["mysdk_mylicense@current"],
-    jars: ["java/myjavalib.jar"],
-}
-
-license {
-    name: "mysdk_mylicense@current",
-    sdk_member_name: "mylicense",
-    visibility: ["//visibility:private"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-        "legacy_unencumbered",
-    ],
-    license_text: [
-        "licenses/NOTICE1",
-        "licenses/NOTICE2",
-    ],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-		`),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
 NOTICE1 -> licenses/NOTICE1
diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go
index a3db189..99caf13 100644
--- a/sdk/member_trait_test.go
+++ b/sdk/member_trait_test.go
@@ -134,7 +134,7 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -145,23 +145,6 @@
     jars: ["javalibs/myjavalib.jar"],
 }
 `),
-		checkVersionedAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["javalibs/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    fake_members: ["mysdk_myjavalib@current"],
-}
-`),
 	)
 }
 
@@ -216,7 +199,7 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 468ed99..aeeedb4 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -146,7 +146,7 @@
 	return s
 }
 
-// sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
+// sdk_snapshot is a snapshot of an SDK. This is an auto-generated module.
 func SnapshotModuleFactory() android.Module {
 	s := newSdkModule(false)
 	s.properties.Snapshot = true
@@ -292,38 +292,6 @@
 
 var _ android.ExcludeFromApexContentsTag = dependencyTag{}
 
-// For dependencies from an in-development version of an SDK member to frozen versions of the same member
-// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
-//
-// The dependency represented by this tag requires that for every APEX variant created for the
-// `from` module that an equivalent APEX variant is created for the 'to' module. This is because an
-// APEX that requires a specific version of an sdk (via the `uses_sdks` property will replace
-// dependencies on the unversioned sdk member with a dependency on the appropriate versioned sdk
-// member. In order for that to work the versioned sdk member needs to have a variant for that APEX.
-// As it is not known at the time that the APEX variants are created which specific APEX variants of
-// a versioned sdk members will be required it is necessary for the versioned sdk members to have
-// variants for any APEX that it could be used within.
-//
-// If the APEX selects a versioned sdk member then it will not have a dependency on the `from`
-// module at all so any dependencies of that module will not affect the APEX. However, if the APEX
-// selects the unversioned sdk member then it must exclude all the versioned sdk members. In no
-// situation would this dependency cause the `to` module to be added to the APEX hence why this tag
-// also excludes the `to` module from being added to the APEX contents.
-type sdkMemberVersionedDepTag struct {
-	dependencyTag
-	member  string
-	version string
-}
-
-func (t sdkMemberVersionedDepTag) AlwaysRequireApexVariant() bool {
-	return true
-}
-
-// Mark this tag so dependencies that use it are excluded from visibility enforcement.
-func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
-
-var _ android.AlwaysRequireApexVariantTag = sdkMemberVersionedDepTag{}
-
 // Step 1: create dependencies from an SDK module to its members.
 func memberMutator(mctx android.BottomUpMutatorContext) {
 	if s, ok := mctx.Module().(*sdk); ok {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index ccbeb8d..1ec12c3 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -119,18 +119,6 @@
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: [
-        "//other/foo",
-        "//package",
-        "//prebuilts/mysdk",
-    ],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: false,
     visibility: [
@@ -143,14 +131,6 @@
 }
 
 java_import {
-    name: "mysdk_mypublicjavalib@current",
-    sdk_member_name: "mypublicjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/mypublicjavalib.jar"],
-}
-
-java_import {
     name: "mypublicjavalib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -159,18 +139,6 @@
 }
 
 java_import {
-    name: "mysdk_mydefaultedjavalib@current",
-    sdk_member_name: "mydefaultedjavalib",
-    visibility: [
-        "//other/bar",
-        "//package",
-        "//prebuilts/mysdk",
-    ],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/mydefaultedjavalib.jar"],
-}
-
-java_import {
     name: "mydefaultedjavalib",
     prefer: false,
     visibility: [
@@ -183,17 +151,6 @@
 }
 
 java_import {
-    name: "mysdk_myprivatejavalib@current",
-    sdk_member_name: "myprivatejavalib",
-    visibility: [
-        "//package",
-        "//prebuilts/mysdk",
-    ],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myprivatejavalib.jar"],
-}
-
-java_import {
     name: "myprivatejavalib",
     prefer: false,
     visibility: [
@@ -203,20 +160,6 @@
     apex_available: ["//apex_available:platform"],
     jars: ["java/myprivatejavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: [
-        "//other/foo",
-        "//package:__subpackages__",
-    ],
-    java_header_libs: [
-        "mysdk_myjavalib@current",
-        "mysdk_mypublicjavalib@current",
-        "mysdk_mydefaultedjavalib@current",
-        "mysdk_myprivatejavalib@current",
-    ],
-}
 `))
 }
 
@@ -450,26 +393,12 @@
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
 			`),
 		)
 	})
@@ -489,26 +418,12 @@
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: true,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
 			`),
 		)
 	})
@@ -528,14 +443,6 @@
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-java_import {
     name: "myjavalib",
     prefer: false,
     use_source_config_var: {
@@ -546,113 +453,10 @@
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
 			`),
 		)
 	})
 
-	t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			preparer,
-			android.FixtureMergeEnv(map[string]string{
-				"SOONG_SDK_SNAPSHOT_VERSION": "unversioned",
-			}),
-		).RunTest(t)
-
-		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk.zip")
-
-		CheckSnapshot(t, result, "mysdk", "",
-			checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-			`),
-		)
-	})
-
-	t.Run("SOONG_SDK_SNAPSHOT_VERSION=current", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			preparer,
-			android.FixtureMergeEnv(map[string]string{
-				"SOONG_SDK_SNAPSHOT_VERSION": "current",
-			}),
-		).RunTest(t)
-
-		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
-
-		CheckSnapshot(t, result, "mysdk", "",
-			checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@current"],
-}
-			`),
-		)
-	})
-
-	t.Run("SOONG_SDK_SNAPSHOT_VERSION=2", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			preparer,
-			android.FixtureMergeEnv(map[string]string{
-				"SOONG_SDK_SNAPSHOT_VERSION": "2",
-			}),
-		).RunTest(t)
-
-		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-2.zip")
-
-		CheckSnapshot(t, result, "mysdk", "",
-			checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_myjavalib@2",
-    sdk_member_name: "myjavalib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@2",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_myjavalib@2"],
-}
-			`),
-			// A versioned snapshot cannot be used on its own so add the source back in.
-			snapshotTestPreparer(checkSnapshotWithoutSource, android.FixtureWithRootAndroidBp(bp)),
-		)
-	})
-
 	t.Run("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE=S", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
@@ -669,6 +473,9 @@
 				name: "mybootclasspathfragment",
 				apex_available: ["myapex"],
 				contents: ["mysdklibrary"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
 			}
 
 			java_sdk_library {
@@ -685,7 +492,7 @@
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
-			checkUnversionedAndroidBpContents(`
+			checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_bootclasspath_fragment {
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 16e3e7f..1ac405d 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -22,28 +22,21 @@
 	"android/soong/java"
 )
 
-func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
+func testSnapshotWithSystemServerClasspathFragment(t *testing.T, sdk string, targetBuildRelease string, expectedSdkSnapshot string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("mysdklibrary"),
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:mylib", "myapex:mysdklibrary"),
+		android.FixtureModifyEnv(func(env map[string]string) {
+			if targetBuildRelease != "latest" {
+				env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = targetBuildRelease
+			}
+		}),
 		prepareForSdkTestWithApex,
 
-		android.FixtureWithRootAndroidBp(`
-			sdk {
-				name: "mysdk",
-				systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
-				java_sdk_libs: [
-					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
-					// a java_sdk_libs module because it is used in the mysystemserverclasspathfragment's
-					// contents property. However, it is specified here to ensure that duplicates are
-					// correctly deduped.
-					"mysdklibrary",
-				],
-			}
-
+		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
 				key: "myapex.key",
@@ -83,7 +76,27 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkUnversionedAndroidBpContents(`
+		checkAndroidBpContents(expectedSdkSnapshot),
+	)
+}
+
+func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
+
+	commonSdk := `
+sdk {
+	name: "mysdk",
+	systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+	java_sdk_libs: [
+		// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+		// a java_sdk_libs module because it is used in the mysystemserverclasspathfragment's
+		// contents property. However, it is specified here to ensure that duplicates are
+		// correctly deduped.
+		"mysdklibrary",
+	],
+}
+	`
+
+	expectedLatestSnapshot := `
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -120,13 +133,36 @@
         "mysdklibrary",
     ],
 }
-`),
-		checkVersionedAndroidBpContents(`
+`
+
+	t.Run("target-s", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "S", `
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_mysdklibrary@current",
-    sdk_member_name: "mysdklibrary",
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+`)
+	})
+
+	t.Run("target-t", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "Tiramisu", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     shared_library: false,
@@ -140,8 +176,8 @@
 }
 
 java_import {
-    name: "mysdk_mylib@current",
-    sdk_member_name: "mylib",
+    name: "mylib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
@@ -149,23 +185,28 @@
 }
 
 prebuilt_systemserverclasspath_fragment {
-    name: "mysdk_mysystemserverclasspathfragment@current",
-    sdk_member_name: "mysystemserverclasspathfragment",
+    name: "mysystemserverclasspathfragment",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     contents: [
-        "mysdk_mylib@current",
-        "mysdk_mysdklibrary@current",
+        "mylib",
+        "mysdklibrary",
     ],
 }
+`)
+	})
 
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_sdk_libs: ["mysdk_mysdklibrary@current"],
-    java_systemserver_libs: ["mysdk_mylib@current"],
-    systemserverclasspath_fragments: ["mysdk_mysystemserverclasspathfragment@current"],
-}
-`),
-	)
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
+	})
+
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, `latest`, expectedLatestSnapshot)
+	})
 }
diff --git a/sdk/testing.go b/sdk/testing.go
index 72344de..bed11b2 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -122,29 +122,18 @@
 	}
 }
 
-func pathsToStrings(paths android.Paths) []string {
-	var ret []string
-	for _, p := range paths {
-		ret = append(ret, p.String())
-	}
-	return ret
-}
-
 // Analyse the sdk build rules to extract information about what it is doing.
 //
 // e.g. find the src/dest pairs from each cp command, the various zip files
 // generated, etc.
 func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
 	info := &snapshotBuildInfo{
-		t:                            t,
-		r:                            result,
-		version:                      sdk.builderForTests.version,
-		androidBpContents:            sdk.GetAndroidBpContentsForTests(),
-		androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
-		androidVersionedBpContents:   sdk.GetVersionedAndroidBpContentsForTests(),
-		infoContents:                 sdk.GetInfoContentsForTests(),
-		snapshotTestCustomizations:   map[snapshotTest]*snapshotTestCustomization{},
-		targetBuildRelease:           sdk.builderForTests.targetBuildRelease,
+		t:                          t,
+		r:                          result,
+		androidBpContents:          sdk.GetAndroidBpContentsForTests(),
+		infoContents:               sdk.GetInfoContentsForTests(),
+		snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
+		targetBuildRelease:         sdk.builderForTests.targetBuildRelease,
 	}
 
 	buildParams := sdk.BuildParamsForTests()
@@ -258,10 +247,7 @@
 	if dir != "" {
 		dir = filepath.Clean(dir) + "/"
 	}
-	suffix := ""
-	if snapshotBuildInfo.version != soongSdkSnapshotVersionUnversioned {
-		suffix = "-" + snapshotBuildInfo.version
-	}
+	suffix := "-" + soongSdkSnapshotVersionCurrent
 
 	expectedZipPath := fmt.Sprintf(".intermediates/%s%s/%s/%s%s.zip", dir, name, variant, name, suffix)
 	android.AssertStringEquals(t, "Snapshot zip file in wrong place", expectedZipPath, actual)
@@ -345,33 +331,6 @@
 	}
 }
 
-// Check that the snapshot's unversioned generated Android.bp is correct.
-//
-// This func should be used to check the general snapshot generation code.
-//
-// Both the expected and actual string are both trimmed before comparing.
-func checkUnversionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
-	return func(info *snapshotBuildInfo) {
-		info.t.Helper()
-		android.AssertTrimmedStringEquals(info.t, "unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents)
-	}
-}
-
-// Check that the snapshot's versioned generated Android.bp is correct.
-//
-// This func should only be used to check the version specific snapshot generation code,
-// i.e. the encoding of version into module names and the generation of the _snapshot module. The
-// general snapshot generation code should be checked using the checkUnversionedAndroidBpContents()
-// func.
-//
-// Both the expected and actual string are both trimmed before comparing.
-func checkVersionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
-	return func(info *snapshotBuildInfo) {
-		info.t.Helper()
-		android.AssertTrimmedStringEquals(info.t, "versioned Android.bp contents do not match", expected, info.androidVersionedBpContents)
-	}
-}
-
 // Check that the snapshot's copy rules are correct.
 //
 // The copy rules are formatted as <src> -> <dest>, one per line and then compared
@@ -477,20 +436,9 @@
 	// The result from RunTest()
 	r *android.TestResult
 
-	// The version of the generated snapshot.
-	//
-	// See snapshotBuilder.version for more information about this field.
-	version string
-
 	// The contents of the generated Android.bp file
 	androidBpContents string
 
-	// The contents of the unversioned Android.bp file
-	androidUnversionedBpContents string
-
-	// The contents of the versioned Android.bp file
-	androidVersionedBpContents string
-
 	// The contents of the info file.
 	infoContents string
 
diff --git a/sdk/update.go b/sdk/update.go
index e61ae0d..b9ef3d0 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -35,7 +35,7 @@
 // ========================================================
 //
 // SOONG_SDK_SNAPSHOT_PREFER
-//     By default every unversioned module in the generated snapshot has prefer: false. Building it
+//     By default every module in the generated snapshot has prefer: false. Building it
 //     with SOONG_SDK_SNAPSHOT_PREFER=true will force them to use prefer: true.
 //
 // SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR
@@ -69,20 +69,6 @@
 //     maintainable solution has been implemented.
 //     TODO(b/174997203): Remove when no longer necessary.
 //
-// SOONG_SDK_SNAPSHOT_VERSION
-//     This provides control over the version of the generated snapshot.
-//
-//     SOONG_SDK_SNAPSHOT_VERSION=current will generate unversioned and versioned prebuilts and a
-//     versioned snapshot module. This is the default behavior. The zip file containing the
-//     generated snapshot will be <sdk-name>-current.zip.
-//
-//     SOONG_SDK_SNAPSHOT_VERSION=unversioned will generate unversioned prebuilts only and the zip
-//     file containing the generated snapshot will be <sdk-name>.zip.
-//
-//     SOONG_SDK_SNAPSHOT_VERSION=<number> will generate versioned prebuilts and a versioned
-//     snapshot module only. The zip file containing the generated snapshot will be
-//     <sdk-name>-<number>.zip.
-//
 // SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE
 //     This allows the target build release (i.e. the release version of the build within which
 //     the snapshot will be used) of the snapshot to be specified. If unspecified then it defaults
@@ -130,8 +116,7 @@
 )
 
 const (
-	soongSdkSnapshotVersionUnversioned = "unversioned"
-	soongSdkSnapshotVersionCurrent     = "current"
+	soongSdkSnapshotVersionCurrent = "current"
 )
 
 type generatedContents struct {
@@ -163,13 +148,13 @@
 // IndentedPrintf will add spaces to indent the line to the appropriate level before printing the
 // arguments.
 func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) {
-	fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format, args...)
+	_, _ = fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format, args...)
 }
 
 // UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing
 // the arguments.
 func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) {
-	fmt.Fprintf(&(gc.content), format, args...)
+	_, _ = fmt.Fprintf(&(gc.content), format, args...)
 }
 
 func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -254,7 +239,7 @@
 // Finally, the member type slices are concatenated together to form a single slice. The order in
 // which they are concatenated is the order in which the member types were registered in the
 // android.SdkMemberTypesRegistry.
-func (s *sdk) groupMemberVariantsByMemberThenType(ctx android.ModuleContext, memberVariantDeps []sdkMemberVariantDep) []*sdkMember {
+func (s *sdk) groupMemberVariantsByMemberThenType(ctx android.ModuleContext, targetBuildRelease *buildRelease, memberVariantDeps []sdkMemberVariantDep) []*sdkMember {
 	byType := make(map[android.SdkMemberType][]*sdkMember)
 	byName := make(map[string]*sdkMember)
 
@@ -268,22 +253,54 @@
 			member = &sdkMember{memberType: memberType, name: name}
 			byName[name] = member
 			byType[memberType] = append(byType[memberType], member)
+		} else if member.memberType != memberType {
+			// validate whether this is the same member type or and overriding member type
+			if memberType.Overrides(member.memberType) {
+				member.memberType = memberType
+			} else if !member.memberType.Overrides(memberType) {
+				ctx.ModuleErrorf("Incompatible member types %q %q", member.memberType, memberType)
+			}
 		}
 
 		// Only append new variants to the list. This is needed because a member can be both
 		// exported by the sdk and also be a transitive sdk member.
 		member.variants = appendUniqueVariants(member.variants, variant)
 	}
-
 	var members []*sdkMember
 	for _, memberListProperty := range s.memberTypeListProperties() {
-		membersOfType := byType[memberListProperty.memberType]
+		memberType := memberListProperty.memberType
+
+		if !isMemberTypeSupportedByTargetBuildRelease(memberType, targetBuildRelease) {
+			continue
+		}
+
+		membersOfType := byType[memberType]
 		members = append(members, membersOfType...)
 	}
 
 	return members
 }
 
+// isMemberTypeSupportedByTargetBuildRelease returns true if the member type is supported by the
+// target build release.
+func isMemberTypeSupportedByTargetBuildRelease(memberType android.SdkMemberType, targetBuildRelease *buildRelease) bool {
+	supportedByTargetBuildRelease := true
+	supportedBuildReleases := memberType.SupportedBuildReleases()
+	if supportedBuildReleases == "" {
+		supportedBuildReleases = "S+"
+	}
+
+	set, err := parseBuildReleaseSet(supportedBuildReleases)
+	if err != nil {
+		panic(fmt.Errorf("member type %s has invalid supported build releases %q: %s",
+			memberType.SdkPropertyName(), supportedBuildReleases, err))
+	}
+	if !set.contains(targetBuildRelease) {
+		supportedByTargetBuildRelease = false
+	}
+	return supportedByTargetBuildRelease
+}
+
 func appendUniqueVariants(variants []android.SdkAware, newVariant android.SdkAware) []android.SdkAware {
 	for _, v := range variants {
 		if v == newVariant {
@@ -315,12 +332,6 @@
 //         <arch>/lib/
 //            libFoo.so   : a stub library
 
-// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot
-// This isn't visible to users, so could be changed in future.
-func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
-	return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
-}
-
 // buildSnapshot is the main function in this source file. It creates rules to copy
 // the contents (header files, stub libraries, etc) into the zip file.
 func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) {
@@ -372,20 +383,9 @@
 	}
 
 	config := ctx.Config()
-	version := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_VERSION", "current")
 
-	// Generate versioned modules in the snapshot unless an unversioned snapshot has been requested.
-	generateVersioned := version != soongSdkSnapshotVersionUnversioned
-
-	// Generate unversioned modules in the snapshot unless a numbered snapshot has been requested.
-	//
-	// Unversioned modules are not required in that case because the numbered version will be a
-	// finalized version of the snapshot that is intended to be kept separate from the
-	generateUnversioned := version == soongSdkSnapshotVersionUnversioned || version == soongSdkSnapshotVersionCurrent
-	snapshotFileSuffix := ""
-	if generateVersioned {
-		snapshotFileSuffix = "-" + version
-	}
+	// Always add -current to the end
+	snapshotFileSuffix := "-current"
 
 	currentBuildRelease := latestBuildRelease()
 	targetBuildReleaseEnv := config.GetenvWithDefault("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", currentBuildRelease.name)
@@ -398,7 +398,6 @@
 	builder := &snapshotBuilder{
 		ctx:                   ctx,
 		sdk:                   s,
-		version:               version,
 		snapshotDir:           snapshotDir.OutputPath,
 		copies:                make(map[string]string),
 		filesToZip:            []android.Path{bp.path},
@@ -428,12 +427,15 @@
 
 	// Group the variants for each member module together and then group the members of each member
 	// type together.
-	members := s.groupMemberVariantsByMemberThenType(ctx, memberVariantDeps)
+	members := s.groupMemberVariantsByMemberThenType(ctx, targetBuildRelease, memberVariantDeps)
 
 	// Create the prebuilt modules for each of the member modules.
 	traits := s.gatherTraits()
 	for _, member := range members {
 		memberType := member.memberType
+		if !memberType.ArePrebuiltsRequired() {
+			continue
+		}
 
 		name := member.name
 		requiredTraits := traits[name]
@@ -448,38 +450,19 @@
 		s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule))
 	}
 
-	// Create a transformer that will transform an unversioned module into a versioned module.
-	unversionedToVersionedTransformer := unversionedToVersionedTransformation{builder: builder}
-
-	// Create a transformer that will transform an unversioned module by replacing any references
+	// Create a transformer that will transform a module by replacing any references
 	// to internal members with a unique module name and setting prefer: false.
-	unversionedTransformer := unversionedTransformation{
+	snapshotTransformer := snapshotTransformation{
 		builder: builder,
 	}
 
-	for _, unversioned := range builder.prebuiltOrder {
+	for _, module := range builder.prebuiltOrder {
 		// Prune any empty property sets.
-		unversioned = unversioned.transform(pruneEmptySetTransformer{})
+		module = module.transform(pruneEmptySetTransformer{})
 
-		if generateVersioned {
-			// Copy the unversioned module so it can be modified to make it versioned.
-			versioned := unversioned.deepCopy()
-
-			// Transform the unversioned module into a versioned one.
-			versioned.transform(unversionedToVersionedTransformer)
-			bpFile.AddModule(versioned)
-		}
-
-		if generateUnversioned {
-			// Transform the unversioned module to make it suitable for use in the snapshot.
-			unversioned.transform(unversionedTransformer)
-			bpFile.AddModule(unversioned)
-		}
-	}
-
-	if generateVersioned {
-		// Add the sdk/module_exports_snapshot module to the bp file.
-		s.addSnapshotModule(ctx, builder, sdkVariants, memberVariantDeps)
+		// Transform the module module to make it suitable for use in the snapshot.
+		module.transform(snapshotTransformer)
+		bpFile.AddModule(module)
 	}
 
 	// generate Android.bp
@@ -666,7 +649,7 @@
 func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
 	// Collate the set of components that all the modules added to the sdk provide.
 	components := map[string]*sdkMemberVariantDep{}
-	for i, _ := range deps {
+	for i := range deps {
 		dep := &deps[i]
 		for _, c := range dep.exportedComponentsInfo.Components {
 			components[c] = dep
@@ -701,81 +684,6 @@
 	return filtered
 }
 
-// addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
-func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
-	bpFile := builder.bpFile
-
-	snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
-	var snapshotModuleType string
-	if s.properties.Module_exports {
-		snapshotModuleType = "module_exports_snapshot"
-	} else {
-		snapshotModuleType = "sdk_snapshot"
-	}
-	snapshotModule := bpFile.newModule(snapshotModuleType)
-	snapshotModule.AddProperty("name", snapshotName)
-
-	// Make sure that the snapshot has the same visibility as the sdk.
-	visibility := android.EffectiveVisibilityRules(ctx, s).Strings()
-	if len(visibility) != 0 {
-		snapshotModule.AddProperty("visibility", visibility)
-	}
-
-	addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule)
-
-	combinedPropertiesList := s.collateSnapshotModuleInfo(ctx, sdkVariants, memberVariantDeps)
-	commonCombinedProperties := s.optimizeSnapshotModuleProperties(ctx, combinedPropertiesList)
-
-	s.addSnapshotPropertiesToPropertySet(builder, snapshotModule, commonCombinedProperties)
-
-	targetPropertySet := snapshotModule.AddPropertySet("target")
-
-	// Create a mapping from osType to combined properties.
-	osTypeToCombinedProperties := map[android.OsType]*combinedSnapshotModuleProperties{}
-	for _, combined := range combinedPropertiesList {
-		osTypeToCombinedProperties[combined.sdkVariant.Os()] = combined
-	}
-
-	// Iterate over the os types in a fixed order.
-	for _, osType := range s.getPossibleOsTypes() {
-		if combined, ok := osTypeToCombinedProperties[osType]; ok {
-			osPropertySet := targetPropertySet.AddPropertySet(osType.Name)
-
-			s.addSnapshotPropertiesToPropertySet(builder, osPropertySet, combined)
-		}
-	}
-
-	// If host is supported and any member is host OS dependent then disable host
-	// by default, so that we can enable each host OS variant explicitly. This
-	// avoids problems with implicitly enabled OS variants when the snapshot is
-	// used, which might be different from this run (e.g. different build OS).
-	if s.HostSupported() {
-		var supportedHostTargets []string
-		for _, memberVariantDep := range memberVariantDeps {
-			if memberVariantDep.memberType.IsHostOsDependent() && memberVariantDep.variant.Target().Os.Class == android.Host {
-				targetString := memberVariantDep.variant.Target().Os.String() + "_" + memberVariantDep.variant.Target().Arch.ArchType.String()
-				if !android.InList(targetString, supportedHostTargets) {
-					supportedHostTargets = append(supportedHostTargets, targetString)
-				}
-			}
-		}
-		if len(supportedHostTargets) > 0 {
-			hostPropertySet := targetPropertySet.AddPropertySet("host")
-			hostPropertySet.AddProperty("enabled", false)
-		}
-		// Enable the <os>_<arch> variant explicitly when we've disabled it by default on host.
-		for _, hostTarget := range supportedHostTargets {
-			propertySet := targetPropertySet.AddPropertySet(hostTarget)
-			propertySet.AddProperty("enabled", true)
-		}
-	}
-
-	// Prune any empty property sets.
-	snapshotModule.transform(pruneEmptySetTransformer{})
-
-	bpFile.AddModule(snapshotModule)
-}
-
 // Check the syntax of the generated Android.bp file contents and if they are
 // invalid then log an error with the contents (tagged with line numbers) and the
 // errors that were found so that it is easy to see where the problem lies.
@@ -912,92 +820,34 @@
 	}
 }
 
-func (s *sdk) addSnapshotPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, combined *combinedSnapshotModuleProperties) {
-	staticProperties := combined.staticProperties
-	multilib := staticProperties.Compile_multilib
-	if multilib != "" && multilib != "both" {
-		// Compile_multilib defaults to both so only needs to be set when it's specified and not both.
-		propertySet.AddProperty("compile_multilib", multilib)
-	}
-
-	dynamicMemberTypeListProperties := combined.dynamicProperties
-	for _, memberListProperty := range s.memberTypeListProperties() {
-		if memberListProperty.getter == nil {
-			continue
-		}
-		names := memberListProperty.getter(dynamicMemberTypeListProperties)
-		if len(names) > 0 {
-			propertySet.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names, false))
-		}
-	}
-}
-
 type propertyTag struct {
 	name string
 }
 
 var _ android.BpPropertyTag = propertyTag{}
 
-// A BpPropertyTag to add to a property that contains references to other sdk members.
+// BpPropertyTag instances to add to a property that contains references to other sdk members.
 //
-// This will cause the references to be rewritten to a versioned reference in the version
-// specific instance of a snapshot module.
+// These will ensure that the referenced modules are available, if required.
 var requiredSdkMemberReferencePropertyTag = propertyTag{"requiredSdkMemberReferencePropertyTag"}
 var optionalSdkMemberReferencePropertyTag = propertyTag{"optionalSdkMemberReferencePropertyTag"}
 
-// A BpPropertyTag that indicates the property should only be present in the versioned
-// module.
-//
-// This will cause the property to be removed from the unversioned instance of a
-// snapshot module.
-var sdkVersionedOnlyPropertyTag = propertyTag{"sdkVersionedOnlyPropertyTag"}
-
-type unversionedToVersionedTransformation struct {
+type snapshotTransformation struct {
 	identityTransformation
 	builder *snapshotBuilder
 }
 
-func (t unversionedToVersionedTransformation) transformModule(module *bpModule) *bpModule {
-	// Use a versioned name for the module but remember the original name for the
-	// snapshot.
-	name := module.Name()
-	module.setProperty("name", t.builder.versionedSdkMemberName(name, true))
-	module.insertAfter("name", "sdk_member_name", name)
-	// Remove the prefer property if present as versioned modules never need marking with prefer.
-	module.removeProperty("prefer")
-	// Ditto for use_source_config_var
-	module.removeProperty("use_source_config_var")
-	return module
-}
-
-func (t unversionedToVersionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
-	if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
-		required := tag == requiredSdkMemberReferencePropertyTag
-		return t.builder.versionedSdkMemberNames(value.([]string), required), tag
-	} else {
-		return value, tag
-	}
-}
-
-type unversionedTransformation struct {
-	identityTransformation
-	builder *snapshotBuilder
-}
-
-func (t unversionedTransformation) transformModule(module *bpModule) *bpModule {
+func (t snapshotTransformation) transformModule(module *bpModule) *bpModule {
 	// If the module is an internal member then use a unique name for it.
 	name := module.Name()
-	module.setProperty("name", t.builder.unversionedSdkMemberName(name, true))
+	module.setProperty("name", t.builder.snapshotSdkMemberName(name, true))
 	return module
 }
 
-func (t unversionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+func (t snapshotTransformation) transformProperty(_ string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
 	if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
 		required := tag == requiredSdkMemberReferencePropertyTag
-		return t.builder.unversionedSdkMemberNames(value.([]string), required), tag
-	} else if tag == sdkVersionedOnlyPropertyTag {
-		// The property is not allowed in the unversioned module so remove it.
-		return nil, nil
+		return t.builder.snapshotSdkMemberNames(value.([]string), required), tag
 	} else {
 		return value, tag
 	}
@@ -1009,7 +859,7 @@
 
 var _ bpTransformer = (*pruneEmptySetTransformer)(nil)
 
-func (t pruneEmptySetTransformer) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+func (t pruneEmptySetTransformer) transformPropertySetAfterContents(_ string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
 	if len(propertySet.properties) == 0 {
 		return nil, nil
 	} else {
@@ -1018,20 +868,12 @@
 }
 
 func generateBpContents(contents *generatedContents, bpFile *bpFile) {
-	generateFilteredBpContents(contents, bpFile, func(*bpModule) bool {
-		return true
-	})
-}
-
-func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
 	contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n")
 	for _, bpModule := range bpFile.order {
-		if moduleFilter(bpModule) {
-			contents.IndentedPrintf("\n")
-			contents.IndentedPrintf("%s {\n", bpModule.moduleType)
-			outputPropertySet(contents, bpModule.bpPropertySet)
-			contents.IndentedPrintf("}\n")
-		}
+		contents.IndentedPrintf("\n")
+		contents.IndentedPrintf("%s {\n", bpModule.moduleType)
+		outputPropertySet(contents, bpModule.bpPropertySet)
+		contents.IndentedPrintf("}\n")
 	}
 }
 
@@ -1167,36 +1009,10 @@
 	return s.builderForTests.infoContents
 }
 
-func (s *sdk) GetUnversionedAndroidBpContentsForTests() string {
-	contents := &generatedContents{}
-	generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
-		name := module.Name()
-		// Include modules that are either unversioned or have no name.
-		return !strings.Contains(name, "@")
-	})
-	return contents.content.String()
-}
-
-func (s *sdk) GetVersionedAndroidBpContentsForTests() string {
-	contents := &generatedContents{}
-	generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
-		name := module.Name()
-		// Include modules that are either versioned or have no name.
-		return name == "" || strings.Contains(name, "@")
-	})
-	return contents.content.String()
-}
-
 type snapshotBuilder struct {
 	ctx android.ModuleContext
 	sdk *sdk
 
-	// The version of the generated snapshot.
-	//
-	// See the documentation of SOONG_SDK_SNAPSHOT_VERSION above for details of the valid values of
-	// this field.
-	version string
-
 	snapshotDir android.OutputPath
 	bpFile      *bpFile
 
@@ -1350,13 +1166,6 @@
 
 	addHostDeviceSupportedProperties(deviceSupported, hostSupported, m)
 
-	// Disable installation in the versioned module of those modules that are ever installable.
-	if installable, ok := variant.(interface{ EverInstallable() bool }); ok {
-		if installable.EverInstallable() {
-			m.AddPropertyWithTag("installable", false, sdkVersionedOnlyPropertyTag)
-		}
-	}
-
 	s.prebuiltModules[name] = m
 	s.prebuiltOrder = append(s.prebuiltOrder, m)
 	return m
@@ -1389,45 +1198,28 @@
 	return optionalSdkMemberReferencePropertyTag
 }
 
-// Get a versioned name appropriate for the SDK snapshot version being taken.
-func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string, required bool) string {
-	if _, ok := s.allMembersByName[unversionedName]; !ok {
+// Get a name for sdk snapshot member. If the member is private then generate a snapshot specific
+// name. As part of the processing this checks to make sure that any required members are part of
+// the snapshot.
+func (s *snapshotBuilder) snapshotSdkMemberName(name string, required bool) string {
+	if _, ok := s.allMembersByName[name]; !ok {
 		if required {
-			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
+			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", name)
 		}
-		return unversionedName
-	}
-	return versionedSdkMemberName(s.ctx, unversionedName, s.version)
-}
-
-func (s *snapshotBuilder) versionedSdkMemberNames(members []string, required bool) []string {
-	var references []string = nil
-	for _, m := range members {
-		references = append(references, s.versionedSdkMemberName(m, required))
-	}
-	return references
-}
-
-// Get an internal name unique to the sdk.
-func (s *snapshotBuilder) unversionedSdkMemberName(unversionedName string, required bool) string {
-	if _, ok := s.allMembersByName[unversionedName]; !ok {
-		if required {
-			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
-		}
-		return unversionedName
+		return name
 	}
 
-	if s.isInternalMember(unversionedName) {
-		return s.ctx.ModuleName() + "_" + unversionedName
+	if s.isInternalMember(name) {
+		return s.ctx.ModuleName() + "_" + name
 	} else {
-		return unversionedName
+		return name
 	}
 }
 
-func (s *snapshotBuilder) unversionedSdkMemberNames(members []string, required bool) []string {
+func (s *snapshotBuilder) snapshotSdkMemberNames(members []string, required bool) []string {
 	var references []string = nil
 	for _, m := range members {
-		references = append(references, s.unversionedSdkMemberName(m, required))
+		references = append(references, s.snapshotSdkMemberName(m, required))
 	}
 	return references
 }
@@ -1530,6 +1322,119 @@
 	}
 }
 
+// TODO(187910671): BEGIN - Remove once modules do not have an APEX and default variant.
+// variantCoordinate contains the coordinates used to identify a variant of an SDK member.
+type variantCoordinate struct {
+	// osType identifies the OS target of a variant.
+	osType android.OsType
+	// archId identifies the architecture and whether it is for the native bridge.
+	archId archId
+	// image is the image variant name.
+	image string
+	// linkType is the link type name.
+	linkType string
+}
+
+func getVariantCoordinate(ctx *memberContext, variant android.Module) variantCoordinate {
+	linkType := ""
+	if len(ctx.MemberType().SupportedLinkages()) > 0 {
+		linkType = getLinkType(variant)
+	}
+	return variantCoordinate{
+		osType:   variant.Target().Os,
+		archId:   archIdFromTarget(variant.Target()),
+		image:    variant.ImageVariation().Variation,
+		linkType: linkType,
+	}
+}
+
+// selectApexVariantsWhereAvailable filters the input list of variants by selecting the APEX
+// specific variant for a specific variantCoordinate when there is both an APEX and default variant.
+//
+// There is a long-standing issue where a module that is added to an APEX has both an APEX and
+// default/platform variant created even when the module does not require a platform variant. As a
+// result an indirect dependency onto a module via the APEX will use the APEX variant, whereas a
+// direct dependency onto the module will use the default/platform variant. That would result in a
+// failure while attempting to optimize the properties for a member as it would have two variants
+// when only one was expected.
+//
+// This function mitigates that problem by detecting when there are two variants that differ only
+// by apex variant, where one is the default/platform variant and one is the APEX variant. In that
+// case it picks the APEX variant. It picks the APEX variant because that is the behavior that would
+// be expected
+func selectApexVariantsWhereAvailable(ctx *memberContext, variants []android.SdkAware) []android.SdkAware {
+	moduleCtx := ctx.sdkMemberContext
+
+	// Group the variants by coordinates.
+	variantsByCoord := make(map[variantCoordinate][]android.SdkAware)
+	for _, variant := range variants {
+		coord := getVariantCoordinate(ctx, variant)
+		variantsByCoord[coord] = append(variantsByCoord[coord], variant)
+	}
+
+	toDiscard := make(map[android.SdkAware]struct{})
+	for coord, list := range variantsByCoord {
+		count := len(list)
+		if count == 1 {
+			continue
+		}
+
+		variantsByApex := make(map[string]android.SdkAware)
+		conflictDetected := false
+		for _, variant := range list {
+			apexInfo := moduleCtx.OtherModuleProvider(variant, android.ApexInfoProvider).(android.ApexInfo)
+			apexVariationName := apexInfo.ApexVariationName
+			// If there are two variants for a specific APEX variation then there is conflict.
+			if _, ok := variantsByApex[apexVariationName]; ok {
+				conflictDetected = true
+				break
+			}
+			variantsByApex[apexVariationName] = variant
+		}
+
+		// If there are more than 2 apex variations or one of the apex variations is not the
+		// default/platform variation then there is a conflict.
+		if len(variantsByApex) != 2 {
+			conflictDetected = true
+		} else if _, ok := variantsByApex[""]; !ok {
+			conflictDetected = true
+		}
+
+		// If there are no conflicts then add the default/platform variation to the list to remove.
+		if !conflictDetected {
+			toDiscard[variantsByApex[""]] = struct{}{}
+			continue
+		}
+
+		// There are duplicate variants at this coordinate and they are not the default and APEX variant
+		// so fail.
+		variantDescriptions := []string{}
+		for _, m := range list {
+			variantDescriptions = append(variantDescriptions, fmt.Sprintf("    %s", m.String()))
+		}
+
+		moduleCtx.ModuleErrorf("multiple conflicting variants detected for OsType{%s}, %s, Image{%s}, Link{%s}\n%s",
+			coord.osType, coord.archId.String(), coord.image, coord.linkType,
+			strings.Join(variantDescriptions, "\n"))
+	}
+
+	// If there are any variants to discard then remove them from the list of variants, while
+	// preserving the order.
+	if len(toDiscard) > 0 {
+		filtered := []android.SdkAware{}
+		for _, variant := range variants {
+			if _, ok := toDiscard[variant]; !ok {
+				filtered = append(filtered, variant)
+			}
+		}
+		variants = filtered
+	}
+
+	return variants
+}
+
+// TODO(187910671): END - Remove once modules do not have an APEX and default variant.
+
 type baseInfo struct {
 	Properties android.SdkMemberProperties
 }
@@ -1585,7 +1490,14 @@
 
 	if commonVariants, ok := variantsByArchId[commonArchId]; ok {
 		if len(osTypeVariants) != 1 {
-			panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants)))
+			variants := []string{}
+			for _, m := range osTypeVariants {
+				variants = append(variants, fmt.Sprintf("    %s", m.String()))
+			}
+			panic(fmt.Errorf("expected to only have 1 variant of %q when arch type is common but found %d\n%s",
+				ctx.Name(),
+				len(osTypeVariants),
+				strings.Join(variants, "\n")))
 		}
 
 		// A common arch type only has one variant and its properties should be treated
@@ -1783,7 +1695,9 @@
 	// added.
 	archInfo.Properties = variantPropertiesFactory()
 
-	if len(archVariants) == 1 {
+	// if there are multiple supported link variants, we want to nest based on linkage even if there
+	// is only one variant, otherwise, if there is only one variant we can populate based on the arch
+	if len(archVariants) == 1 && len(ctx.MemberType().SupportedLinkages()) <= 1 {
 		archInfo.Properties.PopulateFromVariant(ctx, archVariants[0])
 	} else {
 		// Group the variants by image type.
@@ -1910,11 +1824,13 @@
 	// Create the properties into which the image variant specific properties will be added.
 	imageInfo.Properties = variantPropertiesFactory()
 
-	if len(imageVariants) == 1 {
+	// if there are multiple supported link variants, we want to nest even if there is only one
+	// variant, otherwise, if there is only one variant we can populate based on the image
+	if len(imageVariants) == 1 && len(ctx.MemberType().SupportedLinkages()) <= 1 {
 		imageInfo.Properties.PopulateFromVariant(ctx, imageVariants[0])
 	} else {
 		// There is more than one variant for this image variant which must be differentiated by link
-		// type.
+		// type. Or there are multiple supported linkages and we need to nest based on link type.
 		for _, linkVariant := range imageVariants {
 			linkType := getLinkType(linkVariant)
 			if linkType == "" {
@@ -1958,10 +1874,22 @@
 
 	addSdkMemberPropertiesToSet(ctx, imageInfo.Properties, propertySet)
 
+	usedLinkages := make(map[string]bool, len(imageInfo.linkInfos))
 	for _, linkInfo := range imageInfo.linkInfos {
+		usedLinkages[linkInfo.linkType] = true
 		linkInfo.addToPropertySet(ctx, propertySet)
 	}
 
+	// If not all supported linkages had existing variants, we need to disable the unsupported variant
+	if len(imageInfo.linkInfos) < len(ctx.MemberType().SupportedLinkages()) {
+		for _, l := range ctx.MemberType().SupportedLinkages() {
+			if _, ok := usedLinkages[l]; !ok {
+				otherLinkagePropertySet := propertySet.AddPropertySet(l)
+				otherLinkagePropertySet.AddProperty("enabled", false)
+			}
+		}
+	}
+
 	// If this is for a non-core image variant then make sure that the property set does not contain
 	// any properties as providing non-core image variant specific properties for prebuilts is not
 	// currently supported.
@@ -2049,7 +1977,8 @@
 	memberType := member.memberType
 
 	// Do not add the prefer property if the member snapshot module is a source module type.
-	config := ctx.sdkMemberContext.Config()
+	moduleCtx := ctx.sdkMemberContext
+	config := moduleCtx.Config()
 	if !memberType.UsesSourceModuleTypeInSnapshot() {
 		// Set the prefer based on the environment variable. This is a temporary work around to allow a
 		// snapshot to be created that sets prefer: true.
@@ -2074,9 +2003,10 @@
 		}
 	}
 
+	variants := selectApexVariantsWhereAvailable(ctx, member.variants)
+
 	// Group the variants by os type.
 	variantsByOsType := make(map[android.OsType][]android.Module)
-	variants := member.Variants()
 	for _, variant := range variants {
 		osType := variant.Target().Os
 		variantsByOsType[osType] = append(variantsByOsType[osType], variant)
@@ -2122,7 +2052,7 @@
 	}
 
 	// Extract properties which are common across all architectures and os types.
-	extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
+	extractCommonProperties(moduleCtx, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
 
 	// Add the common properties to the module.
 	addSdkMemberPropertiesToSet(ctx, commonProperties, bpModule)
diff --git a/tests/androidmk_test.sh b/tests/androidmk_test.sh
index 331dc77..d0d382b 100755
--- a/tests/androidmk_test.sh
+++ b/tests/androidmk_test.sh
@@ -5,7 +5,7 @@
 # How to run: bash path-to-script/androidmk_test.sh
 # Tests of converting license functionality of the androidmk tool
 REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
-$REAL_TOP/build/soong/soong_ui.bash --make-mode androidmk
+"$REAL_TOP/build/soong/soong_ui.bash" --make-mode androidmk
 
 source "$(dirname "$0")/lib.sh"
 
@@ -113,11 +113,14 @@
   run_androidmk_test "a/b/c/d/Android.mk" "a/b/c/d/Android.bp"
 }
 
-run_androidmk_test () {
+function run_androidmk_test {
   export ANDROID_BUILD_TOP="$MOCK_TOP"
-
-  local out=$($REAL_TOP/*/host/*/bin/androidmk "$1")
-  local expected=$(<"$2")
+  local -r androidmk=("$REAL_TOP"/*/host/*/bin/androidmk)
+  if [[ ${#androidmk[@]} -ne 1 ]]; then
+    fail "Multiple androidmk binaries found: ${androidmk[*]}"
+  fi
+  local -r out=$("${androidmk[0]}" "$1")
+  local -r expected=$(<"$2")
 
   if [[ "$out" != "$expected" ]]; then
     ANDROID_BUILD_TOP="$REAL_TOP"
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
new file mode 100755
index 0000000..4b2f795
--- /dev/null
+++ b/tests/apex_comparison_tests.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+
+# 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.
+
+set -euo pipefail
+
+# Soong/Bazel integration test for building unbundled apexes in the real source tree.
+#
+# These tests build artifacts from head and compares their contents.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+  echo "$0 must be run from the top of the Android source tree."
+  exit 1
+fi
+
+############
+# Test Setup
+############
+
+OUTPUT_DIR="$(mktemp -d)"
+SOONG_OUTPUT_DIR="$OUTPUT_DIR/soong"
+BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
+
+function cleanup {
+  # call bazel clean because some bazel outputs don't have w bits.
+  call_bazel clean
+  rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+###########
+# Run Soong
+###########
+export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true # don't rely on prebuilts
+export TARGET_BUILD_APPS="com.android.adbd com.android.tzdata build.bazel.examples.apex.minimal"
+packages/modules/common/build/build_unbundled_mainline_module.sh \
+  --product module_arm \
+  --dist_dir "$SOONG_OUTPUT_DIR"
+
+######################
+# Run bp2build / Bazel
+######################
+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 \
+  //packages/modules/adb/apex:com.android.adbd \
+  //system/timezone/apex:com.android.tzdata \
+  //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex
+
+# Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg.
+call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs
+DEBUGFS_PATH="$BAZEL_OUT/linux_x86_64-fastbuild/bin/external/e2fsprogs/debugfs/debugfs"
+
+function run_deapexer() {
+  call_bazel run --config=bp2build --config=linux_x86_64 //system/apex/tools:deapexer \
+    -- \
+    --debugfs_path="$DEBUGFS_PATH" \
+    $@
+}
+
+#######
+# Tests
+#######
+
+function compare_deapexer_list() {
+  local APEX_DIR=$1; shift
+  local APEX=$1; shift
+
+  # 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 SOONG_LIST="$OUTPUT_DIR/soong.list"
+  local BAZEL_LIST="$OUTPUT_DIR/bazel.list"
+
+  run_deapexer list "$SOONG_APEX" > "$SOONG_LIST"
+  run_deapexer list "$BAZEL_APEX" > "$BAZEL_LIST"
+
+  if cmp -s "$SOONG_LIST" "$BAZEL_LIST"
+  then
+    echo "ok: $APEX"
+  else
+    echo "contents of $APEX are different between Soong and Bazel:"
+    echo
+    echo expected
+    echo
+    cat "$SOONG_LIST"
+    echo
+    echo got
+    echo
+    cat "$BAZEL_LIST"
+    exit 1
+  fi
+}
+
+compare_deapexer_list packages/modules/adb/apex com.android.adbd.apex
+compare_deapexer_list system/timezone/apex com.android.tzdata.apex
+compare_deapexer_list build/bazel/examples/apex/minimal build.bazel.examples.apex.minimal.apex
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 78ddced..3cdf6aa 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -11,10 +11,10 @@
 function test_bp2build_null_build() {
   setup
   run_soong bp2build
-  local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+  local -r output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   run_soong bp2build
-  local output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+  local -r output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
     fail "Output bp2build marker file changed on null build"
@@ -36,10 +36,10 @@
   touch foo/bar/a.txt foo/bar/b.txt
 
   run_soong bp2build
-  local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+  local -r output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   run_soong bp2build
-  local output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+  local -r output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
     fail "Output bp2build marker file changed on null build"
@@ -147,10 +147,10 @@
   run_soong bp2build
 
   run_bazel build --package_path=out/soong/workspace //a:qq
-  local output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+  local -r output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   run_bazel build --package_path=out/soong/workspace //a:qq
-  local output_mtime2=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+  local -r output_mtime2=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
     fail "output changed on null build"
@@ -161,7 +161,7 @@
 EOF
 
   run_bazel build --package_path=out/soong/workspace //a:qq
-  local output_mtime3=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
+  local -r output_mtime3=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   if [[ "$output_mtime1" == "$output_mtime3" ]]; then
     fail "output not changed when included header changed"
diff --git a/tests/lib.sh b/tests/lib.sh
index abe84d3..69ad201 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -8,7 +8,7 @@
 
 REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
 
-if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+if [[ -n "$HARDWIRED_MOCK_TOP" ]]; then
   MOCK_TOP="$HARDWIRED_MOCK_TOP"
 else
   MOCK_TOP=$(mktemp -t -d st.XXXXX)
@@ -36,37 +36,38 @@
 }
 
 function info {
-  echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" $*
+  echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" "$*"
 }
 
 function fail {
-  echo -e "\e[91;1mFAILED:\e[0m" $*
+  echo -e "\e[91;1mFAILED:\e[0m" "$*"
   exit 1
 }
 
-function copy_directory() {
+function copy_directory {
   local dir="$1"
-  local parent="$(dirname "$dir")"
+  local -r parent="$(dirname "$dir")"
 
   mkdir -p "$MOCK_TOP/$parent"
   cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
 }
 
-function symlink_file() {
+function symlink_file {
   local file="$1"
 
   mkdir -p "$MOCK_TOP/$(dirname "$file")"
   ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
 }
 
-function symlink_directory() {
+function symlink_directory {
   local dir="$1"
 
   mkdir -p "$MOCK_TOP/$dir"
   # We need to symlink the contents of the directory individually instead of
   # using one symlink for the whole directory because finder.go doesn't follow
   # symlinks when looking for Android.bp files
-  for i in $(ls "$REAL_TOP/$dir"); do
+  for i in "$REAL_TOP/$dir"/*; do
+    i=$(basename "$i")
     local target="$MOCK_TOP/$dir/$i"
     local source="$REAL_TOP/$dir/$i"
 
@@ -96,7 +97,7 @@
   touch "$MOCK_TOP/Android.bp"
 }
 
-function setup() {
+function setup {
   cleanup_mock_top
   mkdir -p "$MOCK_TOP"
 
@@ -108,12 +109,14 @@
   tar xzf "$WARMED_UP_MOCK_TOP"
 }
 
-function run_soong() {
+# shellcheck disable=SC2120
+function run_soong {
   build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
 }
 
-function create_mock_bazel() {
+function create_mock_bazel {
   copy_directory build/bazel
+  copy_directory build/bazel_common_rules
 
   symlink_directory prebuilts/bazel
   symlink_directory prebuilts/clang
@@ -126,7 +129,7 @@
   symlink_file tools/bazel
 }
 
-run_bazel() {
+function run_bazel {
   # Remove the ninja_build output marker file to communicate to buildbot that this is not a regular Ninja build, and its
   # output should not be parsed as such.
   rm -rf out/ninja_build
@@ -134,11 +137,11 @@
   tools/bazel "$@"
 }
 
-run_ninja() {
+function run_ninja {
   build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
 }
 
-info "Starting Soong integration test suite $(basename $0)"
+info "Starting Soong integration test suite $(basename "$0")"
 info "Mock top: $MOCK_TOP"
 
 
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 76a918b..1e07727 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -9,3 +9,7 @@
 "$TOP/build/soong/tests/bp2build_bazel_test.sh"
 "$TOP/build/soong/tests/soong_test.sh"
 "$TOP/build/bazel/ci/rbc_regression_test.sh" aosp_arm64-userdebug
+
+# The following tests build against the full source tree and don't rely on the
+# mock client.
+"$TOP/build/soong/tests/apex_comparison_tests.sh"
diff --git a/ui/build/build.go b/ui/build/build.go
index aadf4af..ec42b70 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -266,6 +266,7 @@
 	}
 
 	if config.StartRBE() {
+		cleanupRBELogsDir(ctx, config)
 		startRBE(ctx, config)
 		defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
 	}
diff --git a/ui/build/config.go b/ui/build/config.go
index 0092ff1..cbf1986 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -19,12 +19,14 @@
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"math/rand"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
+	"syscall"
 	"time"
 
 	"android/soong/shared"
@@ -42,6 +44,15 @@
 	envConfigFetchTimeout = 10 * time.Second
 )
 
+var (
+	rbeRandPrefix int
+)
+
+func init() {
+	rand.Seed(time.Now().UnixNano())
+	rbeRandPrefix = rand.Intn(1000)
+}
+
 type Config struct{ *configImpl }
 
 type configImpl struct {
@@ -145,10 +156,15 @@
 // experiments system to control Soong features dynamically.
 func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
 	configName := envConfigName + "." + jsonSuffix
-	expConfigFetcher := &smpb.ExpConfigFetcher{}
+	expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
 	defer func() {
 		ctx.Metrics.ExpConfigFetcher(expConfigFetcher)
 	}()
+	if !config.GoogleProdCredsExist() {
+		status := smpb.ExpConfigFetcher_MISSING_GCERT
+		expConfigFetcher.Status = &status
+		return nil
+	}
 
 	s, err := os.Stat(configFetcher)
 	if err != nil {
@@ -200,7 +216,7 @@
 	}
 
 	if err := fetchEnvConfig(ctx, config, bc); err != nil {
-		fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v\n", err)
+		ctx.Verbosef("Failed to fetch config file: %v\n", err)
 	}
 
 	configDirs := []string{
@@ -1144,34 +1160,25 @@
 	return true
 }
 
-func (c *configImpl) rbeLogDir() string {
-	for _, f := range []string{"RBE_log_dir", "FLAG_log_dir"} {
+func (c *configImpl) rbeProxyLogsDir() string {
+	for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
 		if v, ok := c.environ.Get(f); ok {
 			return v
 		}
 	}
-	if c.Dist() {
-		return c.LogsDir()
-	}
-	return c.OutDir()
+	buildTmpDir := shared.TempDirForOutDir(c.SoongOutDir())
+	return filepath.Join(buildTmpDir, "rbe")
 }
 
-func (c *configImpl) rbeStatsOutputDir() string {
-	for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
-		if v, ok := c.environ.Get(f); ok {
-			return v
+func (c *configImpl) shouldCleanupRBELogsDir() bool {
+	// Perform a log directory cleanup only when the log directory
+	// is auto created by the build rather than user-specified.
+	for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
+		if _, ok := c.environ.Get(f); ok {
+			return false
 		}
 	}
-	return c.rbeLogDir()
-}
-
-func (c *configImpl) rbeLogPath() string {
-	for _, f := range []string{"RBE_log_path", "FLAG_log_path"} {
-		if v, ok := c.environ.Get(f); ok {
-			return v
-		}
-	}
-	return fmt.Sprintf("text://%v/reproxy_log.txt", c.rbeLogDir())
+	return true
 }
 
 func (c *configImpl) rbeExecRoot() string {
@@ -1223,6 +1230,25 @@
 	return "RBE_use_application_default_credentials", "true"
 }
 
+func (c *configImpl) rbeSockAddr(dir string) (string, error) {
+	maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
+	base := fmt.Sprintf("reproxy_%v.sock", rbeRandPrefix)
+
+	name := filepath.Join(dir, base)
+	if len(name) < maxNameLen {
+		return name, nil
+	}
+
+	name = filepath.Join("/tmp", base)
+	if len(name) < maxNameLen {
+		return name, nil
+	}
+
+	return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
+}
+
+// IsGooglerEnvironment returns true if the current build is running
+// on a Google developer machine and false otherwise.
 func (c *configImpl) IsGooglerEnvironment() bool {
 	cf := "ANDROID_BUILD_ENVIRONMENT_CONFIG"
 	if v, ok := c.environ.Get(cf); ok {
@@ -1231,6 +1257,8 @@
 	return false
 }
 
+// GoogleProdCredsExist determine whether credentials exist on the
+// Googler machine to use remote execution.
 func (c *configImpl) GoogleProdCredsExist() bool {
 	if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
 		return false
@@ -1238,10 +1266,21 @@
 	return true
 }
 
+// UseRemoteBuild indicates whether to use a remote build acceleration system
+// to speed up the build.
 func (c *configImpl) UseRemoteBuild() bool {
 	return c.UseGoma() || c.UseRBE()
 }
 
+// StubbyExists checks whether the stubby binary exists on the machine running
+// the build.
+func (c *configImpl) StubbyExists() bool {
+	if _, err := exec.LookPath("stubby"); err != nil {
+		return false
+	}
+	return true
+}
+
 // RemoteParallel controls how many remote jobs (i.e., commands which contain
 // gomacc) are run in parallel.  Note the parallelism of all other jobs is
 // still limited by Parallel()
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 78d37b4..82fc15f 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -16,12 +16,10 @@
 
 import (
 	"fmt"
-	"math/rand"
 	"os"
 	"path/filepath"
 	"runtime"
-	"syscall"
-	"time"
+	"strings"
 
 	"android/soong/ui/metrics"
 )
@@ -54,34 +52,16 @@
 	return cmdPath
 }
 
-func sockAddr(dir string) (string, error) {
-	maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
-	rand.Seed(time.Now().UnixNano())
-	base := fmt.Sprintf("reproxy_%v.sock", rand.Intn(1000))
-
-	name := filepath.Join(dir, base)
-	if len(name) < maxNameLen {
-		return name, nil
-	}
-
-	name = filepath.Join("/tmp", base)
-	if len(name) < maxNameLen {
-		return name, nil
-	}
-
-	return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
-}
-
 func getRBEVars(ctx Context, config Config) map[string]string {
 	vars := map[string]string{
-		"RBE_log_path":   config.rbeLogPath(),
-		"RBE_log_dir":    config.rbeLogDir(),
+		"RBE_log_dir":    config.rbeProxyLogsDir(),
 		"RBE_re_proxy":   config.rbeReproxy(),
 		"RBE_exec_root":  config.rbeExecRoot(),
-		"RBE_output_dir": config.rbeStatsOutputDir(),
+		"RBE_output_dir": config.rbeProxyLogsDir(),
+		"RBE_proxy_log_dir": config.rbeProxyLogsDir(),
 	}
 	if config.StartRBE() {
-		name, err := sockAddr(absPath(ctx, config.TempDir()))
+		name, err := config.rbeSockAddr(absPath(ctx, config.TempDir()))
 		if err != nil {
 			ctx.Fatalf("Error retrieving socket address: %v", err)
 			return nil
@@ -100,6 +80,17 @@
 	return vars
 }
 
+func cleanupRBELogsDir(ctx Context, config Config) {
+	if !config.shouldCleanupRBELogsDir() {
+		return
+	}
+
+	rbeTmpDir := config.rbeProxyLogsDir()
+	if err := os.RemoveAll(rbeTmpDir); err != nil {
+		fmt.Fprintln(ctx.Writer, "\033[33mUnable to remove RBE log directory: ", err, "\033[0m")
+	}
+}
+
 func startRBE(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 	defer ctx.EndTrace()
@@ -110,6 +101,11 @@
 	if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
 		ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
 	}
+	if _, err := os.Stat(config.rbeProxyLogsDir()); os.IsNotExist(err) {
+		if err := os.MkdirAll(config.rbeProxyLogsDir(), 0744); err != nil {
+			ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err)
+		}
+	}
 
 	cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
 
@@ -119,7 +115,6 @@
 }
 
 func stopRBE(ctx Context, config Config) {
-	defer checkProdCreds(ctx, config)
 	cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
 	output, err := cmd.CombinedOutput()
 	if err != nil {
@@ -132,12 +127,31 @@
 	}
 }
 
-func checkProdCreds(ctx Context, config Config) {
-	if !config.IsGooglerEnvironment() || config.GoogleProdCredsExist() {
+func prodCredsAuthType(config Config) bool {
+	authVar, val := config.rbeAuth()
+	if strings.Contains(authVar, "use_google_prod_creds") && val != "" && val != "false" {
+		return true
+	}
+	return false
+}
+
+// Check whether proper auth exists for RBE builds run within a
+// Google dev environment.
+func CheckProdCreds(ctx Context, config Config) {
+	if !config.IsGooglerEnvironment() {
+		return
+	}
+	if !config.StubbyExists() && prodCredsAuthType(config) {
+		fmt.Fprintln(ctx.Writer, "")
+		fmt.Fprintln(ctx.Writer, fmt.Sprintf("\033[33mWARNING: %q binary not found in $PATH, follow go/build-fast#opting-out-of-loas-credentials instead for authenticating with RBE.\033[0m", "stubby"))
+		fmt.Fprintln(ctx.Writer, "")
+		return
+	}
+	if config.GoogleProdCredsExist() {
 		return
 	}
 	fmt.Fprintln(ctx.Writer, "")
-	fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing RBE builds in the future, see go/build-fast#authentication.\033[0m")
+	fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing builds in the future, see go/rbe-android-default-announcement.\033[0m")
 	fmt.Fprintln(ctx.Writer, "")
 }
 
@@ -161,7 +175,7 @@
 		return
 	}
 
-	outputDir := config.rbeStatsOutputDir()
+	outputDir := config.rbeProxyLogsDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
 	}
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 8ff96bc..266f76b 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -56,7 +56,8 @@
 			env := Environment(tt.env)
 			env.Set("OUT_DIR", tmpDir)
 			env.Set("RBE_DIR", tmpDir)
-			env.Set("RBE_output_dir", t.TempDir())
+			env.Set("RBE_output_dir", tmpDir)
+			env.Set("RBE_proxy_log_dir", tmpDir)
 			config := Config{&configImpl{
 				environ: &env,
 			}}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 4bc713b..2dd8299 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -220,9 +220,10 @@
 type ExpConfigFetcher_ConfigStatus int32
 
 const (
-	ExpConfigFetcher_NO_CONFIG ExpConfigFetcher_ConfigStatus = 0
-	ExpConfigFetcher_CONFIG    ExpConfigFetcher_ConfigStatus = 1
-	ExpConfigFetcher_ERROR     ExpConfigFetcher_ConfigStatus = 2
+	ExpConfigFetcher_NO_CONFIG     ExpConfigFetcher_ConfigStatus = 0
+	ExpConfigFetcher_CONFIG        ExpConfigFetcher_ConfigStatus = 1
+	ExpConfigFetcher_ERROR         ExpConfigFetcher_ConfigStatus = 2
+	ExpConfigFetcher_MISSING_GCERT ExpConfigFetcher_ConfigStatus = 3
 )
 
 // Enum value maps for ExpConfigFetcher_ConfigStatus.
@@ -231,11 +232,13 @@
 		0: "NO_CONFIG",
 		1: "CONFIG",
 		2: "ERROR",
+		3: "MISSING_GCERT",
 	}
 	ExpConfigFetcher_ConfigStatus_value = map[string]int32{
-		"NO_CONFIG": 0,
-		"CONFIG":    1,
-		"ERROR":     2,
+		"NO_CONFIG":     0,
+		"CONFIG":        1,
+		"ERROR":         2,
+		"MISSING_GCERT": 3,
 	}
 )
 
@@ -1578,7 +1581,7 @@
 	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
 	0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
 	0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73,
-	0x49, 0x6e, 0x66, 0x6f, 0x22, 0xc8, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66,
+	0x49, 0x6e, 0x66, 0x6f, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66,
 	0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61,
 	0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
 	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
@@ -1587,22 +1590,23 @@
 	0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
 	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
 	0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x34, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
+	0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
 	0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f,
 	0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46,
-	0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x22,
-	0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12,
+	0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54,
+	0x10, 0x03, 0x22, 0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78,
+	0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78,
+	0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 51dd523..4f8fe7f 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -251,6 +251,7 @@
     NO_CONFIG = 0;
     CONFIG = 1;
     ERROR = 2;
+    MISSING_GCERT = 3;
   }
   // The result of the call to expconfigfetcher
   // NO_CONFIG - Not part of experiment
diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go
index 3157813..cef3b5d 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -46,7 +46,11 @@
 
 func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
 	if level >= s.outputLevel {
-		fmt.Fprintln(s.writer, s.formatter.message(level, message))
+		output := s.formatter.message(level, message)
+		if !s.keepANSI {
+			output = string(stripAnsiEscapes([]byte(output)))
+		}
+		fmt.Fprintln(s.writer, output)
 	}
 }
 
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index 810e31d..b9057d2 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -81,9 +81,9 @@
 		},
 		{
 			name:   "action with output with ansi codes",
-			calls:  actionWithOuptutWithAnsiCodes,
-			smart:  "\r\x1b[1m[  0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n",
-			simple: "[100% 1/1] action1\ncolor\n",
+			calls:  actionWithOutputWithAnsiCodes,
+			smart:  "\r\x1b[1m[  0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n\x1b[31mcolor message\x1b[0m\n",
+			simple: "[100% 1/1] action1\ncolor\ncolor message\n",
 		},
 	}
 
@@ -257,12 +257,14 @@
 	runner.finishAction(result1)
 }
 
-func actionWithOuptutWithAnsiCodes(stat status.StatusOutput) {
+func actionWithOutputWithAnsiCodes(stat status.StatusOutput) {
 	result1WithOutputWithAnsiCodes := status.ActionResult{Action: action1, Output: "\x1b[31mcolor\x1b[0m"}
 
 	runner := newRunner(stat, 1)
 	runner.startAction(action1)
 	runner.finishAction(result1WithOutputWithAnsiCodes)
+
+	stat.Message(status.PrintLvl, "\x1b[31mcolor message\x1b[0m")
 }
 
 func TestSmartStatusOutputWidthChange(t *testing.T) {