Merge "Move LLNDK ABI dumps from vndk/ to platform/"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index a5cea17..ab71d99 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -37,184 +37,186 @@
 
 var (
 	Bp2buildDefaultConfig = Bp2BuildConfig{
-		"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,
-		"build/soong/cc/symbolfile":                          Bp2BuildDefaultTrue,
-		"build/soong/linkerconfig":                           Bp2BuildDefaultTrueRecursively,
-		"build/soong/scripts":                                Bp2BuildDefaultTrueRecursively,
-		"cts/common/device-side/nativetesthelper/jni":        Bp2BuildDefaultTrueRecursively,
-		"development/apps/DevelopmentSettings":               Bp2BuildDefaultTrue,
-		"development/apps/Fallback":                          Bp2BuildDefaultTrue,
-		"development/apps/WidgetPreview":                     Bp2BuildDefaultTrue,
-		"development/samples/BasicGLSurfaceView":             Bp2BuildDefaultTrue,
-		"development/samples/BluetoothChat":                  Bp2BuildDefaultTrue,
-		"development/samples/BrokenKeyDerivation":            Bp2BuildDefaultTrue,
-		"development/samples/Compass":                        Bp2BuildDefaultTrue,
-		"development/samples/ContactManager":                 Bp2BuildDefaultTrue,
-		"development/samples/FixedGridLayout":                Bp2BuildDefaultTrue,
-		"development/samples/HelloEffects":                   Bp2BuildDefaultTrue,
-		"development/samples/Home":                           Bp2BuildDefaultTrue,
-		"development/samples/HoneycombGallery":               Bp2BuildDefaultTrue,
-		"development/samples/JetBoy":                         Bp2BuildDefaultTrue,
-		"development/samples/KeyChainDemo":                   Bp2BuildDefaultTrue,
-		"development/samples/LceDemo":                        Bp2BuildDefaultTrue,
-		"development/samples/LunarLander":                    Bp2BuildDefaultTrue,
-		"development/samples/MultiResolution":                Bp2BuildDefaultTrue,
-		"development/samples/MultiWindow":                    Bp2BuildDefaultTrue,
-		"development/samples/NotePad":                        Bp2BuildDefaultTrue,
-		"development/samples/Obb":                            Bp2BuildDefaultTrue,
-		"development/samples/RSSReader":                      Bp2BuildDefaultTrue,
-		"development/samples/ReceiveShareDemo":               Bp2BuildDefaultTrue,
-		"development/samples/SearchableDictionary":           Bp2BuildDefaultTrue,
-		"development/samples/SipDemo":                        Bp2BuildDefaultTrue,
-		"development/samples/SkeletonApp":                    Bp2BuildDefaultTrue,
-		"development/samples/Snake":                          Bp2BuildDefaultTrue,
-		"development/samples/SpellChecker/":                  Bp2BuildDefaultTrueRecursively,
-		"development/samples/ThemedNavBarKeyboard":           Bp2BuildDefaultTrue,
-		"development/samples/ToyVpn":                         Bp2BuildDefaultTrue,
-		"development/samples/TtsEngine":                      Bp2BuildDefaultTrue,
-		"development/samples/USB/AdbTest":                    Bp2BuildDefaultTrue,
-		"development/samples/USB/MissileLauncher":            Bp2BuildDefaultTrue,
-		"development/samples/VoiceRecognitionService":        Bp2BuildDefaultTrue,
-		"development/samples/VoicemailProviderDemo":          Bp2BuildDefaultTrue,
-		"development/samples/WiFiDirectDemo":                 Bp2BuildDefaultTrue,
-		"development/sdk":                                    Bp2BuildDefaultTrueRecursively,
-		"external/aac":                                       Bp2BuildDefaultTrueRecursively,
-		"external/arm-optimized-routines":                    Bp2BuildDefaultTrueRecursively,
-		"external/auto/android-annotation-stubs":             Bp2BuildDefaultTrueRecursively,
-		"external/auto/common":                               Bp2BuildDefaultTrueRecursively,
-		"external/auto/service":                              Bp2BuildDefaultTrueRecursively,
-		"external/boringssl":                                 Bp2BuildDefaultTrueRecursively,
-		"external/bouncycastle":                              Bp2BuildDefaultTrue,
-		"external/brotli":                                    Bp2BuildDefaultTrue,
-		"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,
-		"external/googletest":                                Bp2BuildDefaultTrueRecursively,
-		"external/gwp_asan":                                  Bp2BuildDefaultTrueRecursively,
-		"external/hamcrest":                                  Bp2BuildDefaultTrueRecursively,
-		"external/icu":                                       Bp2BuildDefaultTrueRecursively,
-		"external/icu/android_icu4j":                         Bp2BuildDefaultFalse, // java rules incomplete
-		"external/icu/icu4j":                                 Bp2BuildDefaultFalse, // java rules incomplete
-		"external/jarjar":                                    Bp2BuildDefaultTrueRecursively,
-		"external/javapoet":                                  Bp2BuildDefaultTrueRecursively,
-		"external/jemalloc_new":                              Bp2BuildDefaultTrueRecursively,
-		"external/jsoncpp":                                   Bp2BuildDefaultTrueRecursively,
-		"external/junit":                                     Bp2BuildDefaultTrueRecursively,
-		"external/libavc":                                    Bp2BuildDefaultTrueRecursively,
-		"external/libcap":                                    Bp2BuildDefaultTrueRecursively,
-		"external/libcxx":                                    Bp2BuildDefaultTrueRecursively,
-		"external/libcxxabi":                                 Bp2BuildDefaultTrueRecursively,
-		"external/libevent":                                  Bp2BuildDefaultTrueRecursively,
-		"external/libgav1":                                   Bp2BuildDefaultTrueRecursively,
-		"external/libhevc":                                   Bp2BuildDefaultTrueRecursively,
-		"external/libmpeg2":                                  Bp2BuildDefaultTrueRecursively,
-		"external/libpng":                                    Bp2BuildDefaultTrueRecursively,
-		"external/lz4/lib":                                   Bp2BuildDefaultTrue,
-		"external/lzma/C":                                    Bp2BuildDefaultTrueRecursively,
-		"external/mdnsresponder":                             Bp2BuildDefaultTrueRecursively,
-		"external/minijail":                                  Bp2BuildDefaultTrueRecursively,
-		"external/pcre":                                      Bp2BuildDefaultTrueRecursively,
-		"external/protobuf":                                  Bp2BuildDefaultTrueRecursively,
-		"external/python/six":                                Bp2BuildDefaultTrueRecursively,
-		"external/rappor":                                    Bp2BuildDefaultTrueRecursively,
-		"external/scudo":                                     Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libselinux":                        Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libsepol":                          Bp2BuildDefaultTrueRecursively,
-		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
-		"external/zopfli":                                    Bp2BuildDefaultTrueRecursively,
-		"external/zstd":                                      Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
-		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
-		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/math":                        Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/nativebase":                  Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/opengl/tests/gl2_cameraeye":       Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/gl2_java":            Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/testLatency":         Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/testPauseResume":     Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/testViewport":        Bp2BuildDefaultTrue,
-		"frameworks/proto_logging/stats/stats_log_api_gen":   Bp2BuildDefaultTrueRecursively,
-		"libnativehelper":                                    Bp2BuildDefaultTrueRecursively,
-		"packages/apps/DevCamera":                            Bp2BuildDefaultTrue,
-		"packages/apps/HTMLViewer":                           Bp2BuildDefaultTrue,
-		"packages/apps/Protips":                              Bp2BuildDefaultTrue,
-		"packages/modules/StatsD/lib/libstatssocket":         Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb":                               Bp2BuildDefaultTrue,
-		"packages/modules/adb/apex":                          Bp2BuildDefaultTrue,
-		"packages/modules/adb/crypto":                        Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/libs":                          Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_auth":                  Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_connection":            Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/proto":                         Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/tls":                           Bp2BuildDefaultTrueRecursively,
-		"packages/providers/MediaProvider/tools/dialogs":     Bp2BuildDefaultTrue,
-		"packages/screensavers/Basic":                        Bp2BuildDefaultTrue,
-		"packages/services/Car/tests/SampleRearViewCamera":   Bp2BuildDefaultTrue,
-		"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,
-		"system/core/libcrypto_utils":                        Bp2BuildDefaultTrueRecursively,
-		"system/core/libcutils":                              Bp2BuildDefaultTrueRecursively,
-		"system/core/libpackagelistparser":                   Bp2BuildDefaultTrueRecursively,
-		"system/core/libprocessgroup":                        Bp2BuildDefaultTrue,
-		"system/core/libprocessgroup/cgrouprc":               Bp2BuildDefaultTrue,
-		"system/core/libprocessgroup/cgrouprc_format":        Bp2BuildDefaultTrue,
-		"system/core/libsystem":                              Bp2BuildDefaultTrueRecursively,
-		"system/core/libutils":                               Bp2BuildDefaultTrueRecursively,
-		"system/core/libvndksupport":                         Bp2BuildDefaultTrueRecursively,
-		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
-		"system/libartpalette":                               Bp2BuildDefaultTrueRecursively,
-		"system/libbase":                                     Bp2BuildDefaultTrueRecursively,
-		"system/libfmq":                                      Bp2BuildDefaultTrue,
-		"system/libhwbinder":                                 Bp2BuildDefaultTrueRecursively,
-		"system/libprocinfo":                                 Bp2BuildDefaultTrue,
-		"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/libunwindstack":                    Bp2BuildDefaultTrueRecursively,
-		"tools/apksig":                                       Bp2BuildDefaultTrue,
-		"tools/platform-compat/java/android/compat":          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,
+		"build/soong/cc/symbolfile":                              Bp2BuildDefaultTrue,
+		"build/soong/linkerconfig":                               Bp2BuildDefaultTrueRecursively,
+		"build/soong/scripts":                                    Bp2BuildDefaultTrueRecursively,
+		"cts/common/device-side/nativetesthelper/jni":            Bp2BuildDefaultTrueRecursively,
+		"development/apps/DevelopmentSettings":                   Bp2BuildDefaultTrue,
+		"development/apps/Fallback":                              Bp2BuildDefaultTrue,
+		"development/apps/WidgetPreview":                         Bp2BuildDefaultTrue,
+		"development/samples/BasicGLSurfaceView":                 Bp2BuildDefaultTrue,
+		"development/samples/BluetoothChat":                      Bp2BuildDefaultTrue,
+		"development/samples/BrokenKeyDerivation":                Bp2BuildDefaultTrue,
+		"development/samples/Compass":                            Bp2BuildDefaultTrue,
+		"development/samples/ContactManager":                     Bp2BuildDefaultTrue,
+		"development/samples/FixedGridLayout":                    Bp2BuildDefaultTrue,
+		"development/samples/HelloEffects":                       Bp2BuildDefaultTrue,
+		"development/samples/Home":                               Bp2BuildDefaultTrue,
+		"development/samples/HoneycombGallery":                   Bp2BuildDefaultTrue,
+		"development/samples/JetBoy":                             Bp2BuildDefaultTrue,
+		"development/samples/KeyChainDemo":                       Bp2BuildDefaultTrue,
+		"development/samples/LceDemo":                            Bp2BuildDefaultTrue,
+		"development/samples/LunarLander":                        Bp2BuildDefaultTrue,
+		"development/samples/MultiResolution":                    Bp2BuildDefaultTrue,
+		"development/samples/MultiWindow":                        Bp2BuildDefaultTrue,
+		"development/samples/NotePad":                            Bp2BuildDefaultTrue,
+		"development/samples/Obb":                                Bp2BuildDefaultTrue,
+		"development/samples/RSSReader":                          Bp2BuildDefaultTrue,
+		"development/samples/ReceiveShareDemo":                   Bp2BuildDefaultTrue,
+		"development/samples/SearchableDictionary":               Bp2BuildDefaultTrue,
+		"development/samples/SipDemo":                            Bp2BuildDefaultTrue,
+		"development/samples/SkeletonApp":                        Bp2BuildDefaultTrue,
+		"development/samples/Snake":                              Bp2BuildDefaultTrue,
+		"development/samples/SpellChecker/":                      Bp2BuildDefaultTrueRecursively,
+		"development/samples/ThemedNavBarKeyboard":               Bp2BuildDefaultTrue,
+		"development/samples/ToyVpn":                             Bp2BuildDefaultTrue,
+		"development/samples/TtsEngine":                          Bp2BuildDefaultTrue,
+		"development/samples/USB/AdbTest":                        Bp2BuildDefaultTrue,
+		"development/samples/USB/MissileLauncher":                Bp2BuildDefaultTrue,
+		"development/samples/VoiceRecognitionService":            Bp2BuildDefaultTrue,
+		"development/samples/VoicemailProviderDemo":              Bp2BuildDefaultTrue,
+		"development/samples/WiFiDirectDemo":                     Bp2BuildDefaultTrue,
+		"development/sdk":                                        Bp2BuildDefaultTrueRecursively,
+		"external/aac":                                           Bp2BuildDefaultTrueRecursively,
+		"external/arm-optimized-routines":                        Bp2BuildDefaultTrueRecursively,
+		"external/auto/android-annotation-stubs":                 Bp2BuildDefaultTrueRecursively,
+		"external/auto/common":                                   Bp2BuildDefaultTrueRecursively,
+		"external/auto/service":                                  Bp2BuildDefaultTrueRecursively,
+		"external/boringssl":                                     Bp2BuildDefaultTrueRecursively,
+		"external/bouncycastle":                                  Bp2BuildDefaultTrue,
+		"external/brotli":                                        Bp2BuildDefaultTrue,
+		"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,
+		"external/googletest":                                    Bp2BuildDefaultTrueRecursively,
+		"external/gwp_asan":                                      Bp2BuildDefaultTrueRecursively,
+		"external/hamcrest":                                      Bp2BuildDefaultTrueRecursively,
+		"external/icu":                                           Bp2BuildDefaultTrueRecursively,
+		"external/icu/android_icu4j":                             Bp2BuildDefaultFalse, // java rules incomplete
+		"external/icu/icu4j":                                     Bp2BuildDefaultFalse, // java rules incomplete
+		"external/jarjar":                                        Bp2BuildDefaultTrueRecursively,
+		"external/javapoet":                                      Bp2BuildDefaultTrueRecursively,
+		"external/jemalloc_new":                                  Bp2BuildDefaultTrueRecursively,
+		"external/jsoncpp":                                       Bp2BuildDefaultTrueRecursively,
+		"external/junit":                                         Bp2BuildDefaultTrueRecursively,
+		"external/libavc":                                        Bp2BuildDefaultTrueRecursively,
+		"external/libcap":                                        Bp2BuildDefaultTrueRecursively,
+		"external/libcxx":                                        Bp2BuildDefaultTrueRecursively,
+		"external/libcxxabi":                                     Bp2BuildDefaultTrueRecursively,
+		"external/libevent":                                      Bp2BuildDefaultTrueRecursively,
+		"external/libgav1":                                       Bp2BuildDefaultTrueRecursively,
+		"external/libhevc":                                       Bp2BuildDefaultTrueRecursively,
+		"external/libmpeg2":                                      Bp2BuildDefaultTrueRecursively,
+		"external/libpng":                                        Bp2BuildDefaultTrueRecursively,
+		"external/lz4/lib":                                       Bp2BuildDefaultTrue,
+		"external/lzma/C":                                        Bp2BuildDefaultTrueRecursively,
+		"external/mdnsresponder":                                 Bp2BuildDefaultTrueRecursively,
+		"external/minijail":                                      Bp2BuildDefaultTrueRecursively,
+		"external/pcre":                                          Bp2BuildDefaultTrueRecursively,
+		"external/protobuf":                                      Bp2BuildDefaultTrueRecursively,
+		"external/python/six":                                    Bp2BuildDefaultTrueRecursively,
+		"external/rappor":                                        Bp2BuildDefaultTrueRecursively,
+		"external/scudo":                                         Bp2BuildDefaultTrueRecursively,
+		"external/selinux/libselinux":                            Bp2BuildDefaultTrueRecursively,
+		"external/selinux/libsepol":                              Bp2BuildDefaultTrueRecursively,
+		"external/zlib":                                          Bp2BuildDefaultTrueRecursively,
+		"external/zopfli":                                        Bp2BuildDefaultTrueRecursively,
+		"external/zstd":                                          Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/codecs":                             Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/services/minijail":                        Bp2BuildDefaultTrueRecursively,
+		"frameworks/base/media/tests/MediaDump":                  Bp2BuildDefaultTrue,
+		"frameworks/base/startop/apps/test":                      Bp2BuildDefaultTrue,
+		"frameworks/base/tests/appwidgets/AppWidgetHostTest":     Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/adbd_auth":                       Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/arect":                           Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/math":                            Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/nativebase":                      Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/opengl/tests/gl2_cameraeye":           Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/gl2_java":                Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/testLatency":             Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/testPauseResume":         Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/testViewport":            Bp2BuildDefaultTrue,
+		"frameworks/proto_logging/stats/stats_log_api_gen":       Bp2BuildDefaultTrueRecursively,
+		"libnativehelper":                                        Bp2BuildDefaultTrueRecursively,
+		"packages/apps/DevCamera":                                Bp2BuildDefaultTrue,
+		"packages/apps/HTMLViewer":                               Bp2BuildDefaultTrue,
+		"packages/apps/Protips":                                  Bp2BuildDefaultTrue,
+		"packages/modules/StatsD/lib/libstatssocket":             Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb":                                   Bp2BuildDefaultTrue,
+		"packages/modules/adb/apex":                              Bp2BuildDefaultTrue,
+		"packages/modules/adb/crypto":                            Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/libs":                              Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/pairing_auth":                      Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/pairing_connection":                Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/proto":                             Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/tls":                               Bp2BuildDefaultTrueRecursively,
+		"packages/providers/MediaProvider/tools/dialogs":         Bp2BuildDefaultTrue,
+		"packages/screensavers/Basic":                            Bp2BuildDefaultTrue,
+		"packages/services/Car/tests/SampleRearViewCamera":       Bp2BuildDefaultTrue,
+		"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,
+		"system/core/libcrypto_utils":                            Bp2BuildDefaultTrueRecursively,
+		"system/core/libcutils":                                  Bp2BuildDefaultTrueRecursively,
+		"system/core/libpackagelistparser":                       Bp2BuildDefaultTrueRecursively,
+		"system/core/libprocessgroup":                            Bp2BuildDefaultTrue,
+		"system/core/libprocessgroup/cgrouprc":                   Bp2BuildDefaultTrue,
+		"system/core/libprocessgroup/cgrouprc_format":            Bp2BuildDefaultTrue,
+		"system/core/libsystem":                                  Bp2BuildDefaultTrueRecursively,
+		"system/core/libutils":                                   Bp2BuildDefaultTrueRecursively,
+		"system/core/libvndksupport":                             Bp2BuildDefaultTrueRecursively,
+		"system/core/property_service/libpropertyinfoparser":     Bp2BuildDefaultTrueRecursively,
+		"system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively,
+		"system/libartpalette":                                   Bp2BuildDefaultTrueRecursively,
+		"system/libbase":                                         Bp2BuildDefaultTrueRecursively,
+		"system/libfmq":                                          Bp2BuildDefaultTrue,
+		"system/libhwbinder":                                     Bp2BuildDefaultTrueRecursively,
+		"system/libprocinfo":                                     Bp2BuildDefaultTrue,
+		"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/tools/sysprop":                                   Bp2BuildDefaultTrue,
+		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
+		"tools/apksig":                                           Bp2BuildDefaultTrue,
+		"tools/platform-compat/java/android/compat":              Bp2BuildDefaultTrueRecursively,
 	}
 
 	Bp2buildKeepExistingBuildFile = map[string]bool{
@@ -255,6 +257,7 @@
 		"packages/apps/QuickSearchBox":/* recursive = */ true,
 		"packages/apps/WallpaperPicker":/* recursive = */ false,
 
+		"prebuilts/bazel":/* recursive = */ true,
 		"prebuilts/bundletool":/* recursive = */ true,
 		"prebuilts/gcc":/* recursive = */ true,
 		"prebuilts/build-tools":/* recursive = */ true,
diff --git a/android/androidmk.go b/android/androidmk.go
index 832c7df..d6fe06d 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -605,7 +605,7 @@
 	}
 
 	if len(base.noticeFiles) > 0 {
-		a.SetString("LOCAL_NOTICE_FILE", strings.Join(base.noticeFiles.Strings(), " "))
+		a.AddStrings("LOCAL_NOTICE_FILE", strings.Join(base.noticeFiles.Strings(), " "))
 	}
 
 	if host {
diff --git a/android/arch.go b/android/arch.go
index e72614c..6acc8ad 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -655,7 +655,8 @@
 	prefer32 := os == Windows
 
 	// Determine the multilib selection for this module.
-	multilib, extraMultilib := decodeMultilib(base, os)
+	ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice()
+	multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice)
 
 	// Convert the multilib selection into a list of Targets.
 	targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
@@ -730,7 +731,7 @@
 // multilib from the factory's call to InitAndroidArchModule if none was set.  For modules that
 // called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
 // the actual multilib in extraMultilib.
-func decodeMultilib(base *ModuleBase, os OsType) (multilib, extraMultilib string) {
+func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) {
 	// First check the "android.compile_multilib" or "host.compile_multilib" properties.
 	switch os.Class {
 	case Device:
@@ -749,6 +750,13 @@
 		multilib = base.commonProperties.Default_multilib
 	}
 
+	// If a device is configured with multiple targets, this option
+	// force all device targets that prefer32 to be compiled only as
+	// the first target.
+	if ignorePrefer32OnDevice && os.Class == Device && (multilib == "prefer32" || multilib == "first_prefer32") {
+		multilib = "first"
+	}
+
 	if base.commonProperties.UseTargetVariants {
 		// Darwin has the concept of "universal binaries" which is implemented in Soong by
 		// building both x86_64 and arm64 variants, and having select module types know how to
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index f1ec55e..5804a46 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -34,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)
 }
@@ -76,6 +91,10 @@
 	osType OsType
 }
 
+func (c configKey) String() string {
+	return fmt.Sprintf("%s::%s", c.arch, c.osType)
+}
+
 // Map key to describe bazel cquery requests.
 type cqueryKey struct {
 	label       string
@@ -83,6 +102,11 @@
 	configKey   configKey
 }
 
+func (c cqueryKey) String() string {
+	return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
+
+}
+
 // BazelContext is a context object useful for interacting with Bazel during
 // the course of a build. Use of Bazel to evaluate part of the build graph
 // is referred to as a "mixed build". (Some modules are managed by Soong,
@@ -108,6 +132,9 @@
 	// TODO(b/232976601): Remove.
 	GetPythonBinary(label string, cfgKey configKey) (string, error)
 
+	// Returns the results of the GetApexInfo query (including output files)
+	GetApexInfo(label string, cfgkey configKey) (cquery.ApexCqueryInfo, error)
+
 	// ** end Cquery Results Retrieval Functions
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -171,28 +198,33 @@
 	LabelToOutputFiles  map[string][]string
 	LabelToCcInfo       map[string]cquery.CcInfo
 	LabelToPythonBinary map[string]string
+	LabelToApexInfo     map[string]cquery.ApexCqueryInfo
 }
 
-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(config Config) error {
+func (n MockBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
+	panic("unimplemented")
+}
+
+func (m MockBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
 
@@ -246,23 +278,35 @@
 	return "", fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (n noopBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexCqueryInfo, error) {
+	key := cqueryKey{label, cquery.GetApexInfo, cfgKey}
+	if rawString, ok := bazelCtx.results[key]; ok {
+		return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)), nil
+	}
+	return cquery.ApexCqueryInfo{}, fmt.Errorf("no bazel response found for %v", key)
+}
+
+func (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(config Config) error {
+func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
+	panic("unimplemented")
+}
+
+func (n noopBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
 
@@ -304,7 +348,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 {
@@ -365,10 +409,8 @@
 	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 {
@@ -386,50 +428,48 @@
 func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) (string, string, error) {
 	cmdFlags := []string{
-		// --noautodetect_server_javabase has the practical consequence of preventing Bazel from
-		// attempting to download rules_java, which is incompatible with
-		// --experimental_repository_disable_download set further below.
-		// rules_java is also not needed until mixed builds start building java targets.
-		// TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
-		"--noautodetect_server_javabase",
 		"--output_base=" + absolutePath(paths.outputBase),
 		command.command,
-	}
-	cmdFlags = append(cmdFlags, command.expression)
-	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
+		command.expression,
+		// 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...)
 	bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
-	bazelCmd.Env = append(os.Environ(),
-		"HOME="+paths.homeDir,
+	extraEnv := []string{
+		"HOME=" + paths.homeDir,
 		pwdPrefix(),
-		"BUILD_DIR="+absolutePath(paths.soongOutDir),
+		"BUILD_DIR=" + absolutePath(paths.soongOutDir),
 		// Make OUT_DIR absolute here so tools/bazel.sh uses the correct
 		// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
-		"OUT_DIR="+absolutePath(paths.outDir()),
+		"OUT_DIR=" + absolutePath(paths.outDir()),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
-		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
+		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
+	}
+	bazelCmd.Env = append(os.Environ(), extraEnv...)
 	stderr := &bytes.Buffer{}
 	bazelCmd.Stderr = stderr
 
@@ -640,6 +680,15 @@
     fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
     return "UNKNOWN"
 
+def json_for_file(key, file):
+    return '"' + key + '":"' + file.path + '"'
+
+def json_for_files(key, files):
+    return '"' + key + '":[' + ",".join(['"' + f.path + '"' for f in files]) + ']'
+
+def json_for_labels(key, ll):
+    return '"' + key + '":[' + ",".join(['"' + str(x) + '"' for x in ll]) + ']'
+
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
@@ -682,8 +731,6 @@
 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())
@@ -700,45 +747,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
+		_ = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
 	}
-
 	if err != nil {
 		return err
 	}
@@ -750,7 +779,6 @@
 			cqueryResults[splitLine[0]] = splitLine[1]
 		}
 	}
-
 	for val := range context.requests {
 		if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
 			context.results[val] = cqueryResult
@@ -762,37 +790,27 @@
 
 	// Issue an aquery command to retrieve action information about the bazel build tree.
 	//
-	var aqueryOutput string
-	var coverageFlags []string
+	// 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) {
-		coverageFlags = append(coverageFlags, "--collect_code_coverage")
-		if len(config.productVariables.NativeCoveragePaths) > 0 ||
-			len(config.productVariables.NativeCoverageExcludePaths) > 0 {
-			includePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoveragePaths, "+", ",")
-			excludePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoverageExcludePaths, "-", ",")
-			if len(includePaths) > 0 && len(excludePaths) > 0 {
-				includePaths += ","
-			}
-			coverageFlags = append(coverageFlags, fmt.Sprintf(`--instrumentation_filter=%s`,
-				includePaths+excludePaths))
+		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, ","))
 		}
 	}
-
-	extraFlags := append([]string{"--output=jsonproto"}, coverageFlags...)
-
-	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.
-		extraFlags...)
-
-	if err != nil {
-		return err
+	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))
 	}
-
-	context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
 	if err != nil {
 		return err
 	}
@@ -800,12 +818,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
 	}
 
@@ -874,13 +888,56 @@
 	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)
-		createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
-		desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
-		rule.Build(fmt.Sprintf("bazel %d", index), desc)
 	}
 }
 
diff --git a/android/config.go b/android/config.go
index 47346fc..a5337d0 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1725,6 +1725,10 @@
 	return c.config.productVariables.GenerateAidlNdkPlatformBackend
 }
 
+func (c *config) IgnorePrefer32OnDevice() bool {
+	return c.productVariables.IgnorePrefer32OnDevice
+}
+
 // The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
 // Such lists are used in the build system for things like bootclasspath jars or system server jars.
 // The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
diff --git a/android/module.go b/android/module.go
index 2925081..7173c0d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2328,14 +2328,6 @@
 		}
 		if optPath.Valid() {
 			m.noticeFiles = append(m.noticeFiles, optPath.Path())
-		} else {
-			for _, notice = range []string{"LICENSE", "LICENCE", "NOTICE"} {
-				noticePath := filepath.Join(ctx.ModuleDir(), notice)
-				optPath = ExistentPathForSource(ctx, noticePath)
-				if optPath.Valid() {
-					m.noticeFiles = append(m.noticeFiles, optPath.Path())
-				}
-			}
 		}
 
 		licensesPropertyFlattener(ctx)
diff --git a/android/neverallow.go b/android/neverallow.go
index aa47bca..357cae5 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -58,6 +58,7 @@
 	AddNeverAllowRules(createMakefileGoalRules()...)
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
+	AddNeverAllowRules(createNoticeDeprecationRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -238,6 +239,15 @@
 	}
 }
 
+func createNoticeDeprecationRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			WithMatcher("notice", isSetMatcherInstance).
+			NotIn("vendor/linaro/linux-firmware/").
+			Because("notice has been replaced by licenses/default_applicable_licenses"),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
diff --git a/android/sdk.go b/android/sdk.go
index 2dc0bd7..a71f7f2 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
@@ -673,6 +677,10 @@
 	// 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
@@ -773,7 +781,17 @@
 	// 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
@@ -781,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
 }
@@ -793,6 +816,10 @@
 	return !b.BpPropertyNotRequired
 }
 
+func (b *SdkMemberTypeBase) SupportedBuildReleases() string {
+	return b.SupportedBuildReleaseSpecification
+}
+
 func (b *SdkMemberTypeBase) UsableWithSdkAndSdkSnapshot() bool {
 	return b.SupportsSdk
 }
@@ -801,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
 }
@@ -954,6 +985,10 @@
 
 	// RequiresTrait returns true if this member is expected to provide the specified trait.
 	RequiresTrait(trait SdkMemberTrait) bool
+
+	// IsTargetBuildBeforeTiramisu return true if the target build release for which this snapshot is
+	// being generated is before Tiramisu, i.e. S.
+	IsTargetBuildBeforeTiramisu() bool
 }
 
 // ExportedComponentsInfo contains information about the components that this module exports to an
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 2004c92..c188c48 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -28,6 +28,9 @@
 	// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
 	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+	// ReplaceMaxSdkVersionPlaceholder returns SdkSpec to replace the maxSdkVersion property of permission and
+	// uses-permission tags if it is set.
+	ReplaceMaxSdkVersionPlaceholder(ctx EarlyModuleContext) SdkSpec
 	// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
 	TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
diff --git a/android/variable.go b/android/variable.go
index 50fc304..874b69d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -447,6 +447,8 @@
 	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
 
 	GenerateAidlNdkPlatformBackend bool `json:",omitempty"`
+
+	IgnorePrefer32OnDevice bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/Android.bp b/apex/Android.bp
index fcdf8e6..6533c61 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -23,6 +23,7 @@
     srcs: [
         "androidmk.go",
         "apex.go",
+        "apex_sdk_member.go",
         "apex_singleton.go",
         "builder.go",
         "constants.go",
diff --git a/apex/apex.go b/apex/apex.go
index 5678b06..4394a30 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -17,6 +17,7 @@
 package apex
 
 import (
+	"android/soong/bazel/cquery"
 	"fmt"
 	"path/filepath"
 	"regexp"
@@ -606,6 +607,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 +630,7 @@
 }
 
 var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
+var _ android.SdkMemberDependencyTag = &dependencyTag{}
 
 var (
 	androidAppTag   = &dependencyTag{name: "androidApp", payload: true}
@@ -624,9 +638,9 @@
 	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}
-	compatConfigTag = &dependencyTag{name: "compatConfig", 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, memberType: java.CompatConfigSdkMemberType}
 	javaLibTag      = &dependencyTag{name: "javaLib", payload: true}
 	jniLibTag       = &dependencyTag{name: "jniLib", payload: true}
 	keyTag          = &dependencyTag{name: "key"}
@@ -1790,6 +1804,181 @@
 	}
 }
 
+var _ android.MixedBuildBuildable = (*apexBundle)(nil)
+
+func (a *apexBundle) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+	return ctx.ModuleType() == "apex" && a.properties.ApexType == imageApex
+}
+
+func (a *apexBundle) QueueBazelCall(ctx android.BaseModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	bazelCtx.QueueBazelRequest(a.GetBazelLabel(ctx, a), cquery.GetApexInfo, android.GetConfigKey(ctx))
+}
+
+func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+	if !a.commonBuildActions(ctx) {
+		return
+	}
+
+	a.setApexTypeAndSuffix(ctx)
+	a.setPayloadFsType(ctx)
+	a.setSystemLibLink(ctx)
+
+	if a.properties.ApexType != zipApex {
+		a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
+	}
+
+	bazelCtx := ctx.Config().BazelContext
+	outputs, err := bazelCtx.GetApexInfo(a.GetBazelLabel(ctx, a), android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+		return
+	}
+	a.installDir = android.PathForModuleInstall(ctx, "apex")
+	a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedOutput)
+	a.outputFile = a.outputApexFile
+	a.setCompression(ctx)
+
+	a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyPair[0])
+	a.privateKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyPair[1])
+	a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyPair[0])
+	a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyPair[1])
+	apexType := a.properties.ApexType
+	switch apexType {
+	case imageApex:
+		// TODO(asmundak): Bazel does not create these files yet.
+		// b/190817312
+		a.htmlGzNotice = android.PathForBazelOut(ctx, "NOTICE.html.gz")
+		// b/239081457
+		a.bundleModuleFile = android.PathForBazelOut(ctx, a.Name()+apexType.suffix()+"-base.zip")
+		// b/239081455
+		a.nativeApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.txt"))
+		// b/239081456
+		a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_backing.txt"))
+		// b/239084755
+		a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.xml"))
+		a.installedFile = ctx.InstallFile(a.installDir, a.Name()+a.installSuffix(), a.outputFile,
+			a.compatSymlinks.Paths()...)
+	default:
+		panic(fmt.Errorf("unexpected apex_type for the ProcessBazelQuery: %v", a.properties.ApexType))
+	}
+
+	/*
+			TODO(asmundak): compared to building an APEX with Soong, building it with Bazel does not
+			return filesInfo and requiredDeps fields (in the Soong build the latter is updated).
+			Fix this, as these fields are subsequently used in apex/androidmk.go and in apex/builder/go
+			To find out what Soong build puts there, run:
+			vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)}
+			ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
+		      return a.depVisitor(&vctx, ctx, child, parent)
+		    })
+			vctx.normalizeFileInfo()
+	*/
+
+}
+
+func (a *apexBundle) setCompression(ctx android.ModuleContext) {
+	a.isCompressed = (a.properties.ApexType == imageApex) &&
+		((ctx.Config().CompressedApex() &&
+			proptools.BoolDefault(a.overridableProperties.Compressible, false) &&
+			!a.testApex && !ctx.Config().UnbundledBuildApps()) ||
+			a.testOnlyShouldForceCompression())
+}
+
+func (a apexBundle) installSuffix() string {
+	if a.isCompressed {
+		return imageCapexSuffix
+	}
+	return imageApexSuffix
+}
+
+func (a *apexBundle) setSystemLibLink(ctx android.ModuleContext) {
+	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
+	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
+	// the same library in the system partition, thus effectively sharing the same libraries
+	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
+	// in the APEX.
+	a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable()
+
+	// APEXes targeting other than system/system_ext partitions use vendor/product variants.
+	// So we can't link them to /system/lib libs which are core variants.
+	if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+		a.linkToSystemLib = false
+	}
+
+	forced := ctx.Config().ForceApexSymlinkOptimization()
+	updatable := a.Updatable() || a.FutureUpdatable()
+
+	// We don't need the optimization for updatable APEXes, as it might give false signal
+	// to the system health when the APEXes are still bundled (b/149805758).
+	if !forced && updatable && a.properties.ApexType == imageApex {
+		a.linkToSystemLib = false
+	}
+
+	// We also don't want the optimization for host APEXes, because it doesn't make sense.
+	if ctx.Host() {
+		a.linkToSystemLib = false
+	}
+}
+
+func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) {
+	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+	case ext4FsType:
+		a.payloadFsType = ext4
+	case f2fsFsType:
+		a.payloadFsType = f2fs
+	case erofsFsType:
+		a.payloadFsType = erofs
+	default:
+		ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
+	}
+}
+
+func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) {
+	// Set suffix and primaryApexType depending on the ApexType
+	buildFlattenedAsDefault := ctx.Config().FlattenApex()
+	switch a.properties.ApexType {
+	case imageApex:
+		if buildFlattenedAsDefault {
+			a.suffix = imageApexSuffix
+		} else {
+			a.suffix = ""
+			a.primaryApexType = true
+
+			if ctx.Config().InstallExtraFlattenedApexes() {
+				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+			}
+		}
+	case zipApex:
+		if proptools.String(a.properties.Payload_type) == "zip" {
+			a.suffix = ""
+			a.primaryApexType = true
+		} else {
+			a.suffix = zipApexSuffix
+		}
+	case flattenedApex:
+		if buildFlattenedAsDefault {
+			a.suffix = ""
+			a.primaryApexType = true
+		} else {
+			a.suffix = flattenedSuffix
+		}
+	}
+}
+
+func (a *apexBundle) commonBuildActions(ctx android.ModuleContext) bool {
+	a.checkApexAvailability(ctx)
+	a.checkUpdatable(ctx)
+	a.CheckMinSdkVersion(ctx)
+	a.checkStaticLinkingToStubLibraries(ctx)
+	a.checkStaticExecutables(ctx)
+	if len(a.properties.Tests) > 0 && !a.testApex {
+		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+		return false
+	}
+	return true
+}
+
 type visitorContext struct {
 	// all the files that will be included in this APEX
 	filesInfo []apexFile
@@ -2175,16 +2364,9 @@
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 1) do some validity checks such as apex_available, min_sdk_version, etc.
-	a.checkApexAvailability(ctx)
-	a.checkUpdatable(ctx)
-	a.CheckMinSdkVersion(ctx)
-	a.checkStaticLinkingToStubLibraries(ctx)
-	a.checkStaticExecutables(ctx)
-	if len(a.properties.Tests) > 0 && !a.testApex {
-		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+	if !a.commonBuildActions(ctx) {
 		return
 	}
-
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 2) traverse the dependency tree to collect apexFile structs from them.
 
@@ -2206,74 +2388,9 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = vctx.filesInfo
 
-	// Set suffix and primaryApexType depending on the ApexType
-	buildFlattenedAsDefault := ctx.Config().FlattenApex()
-	switch a.properties.ApexType {
-	case imageApex:
-		if buildFlattenedAsDefault {
-			a.suffix = imageApexSuffix
-		} else {
-			a.suffix = ""
-			a.primaryApexType = true
-
-			if ctx.Config().InstallExtraFlattenedApexes() {
-				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
-			}
-		}
-	case zipApex:
-		if proptools.String(a.properties.Payload_type) == "zip" {
-			a.suffix = ""
-			a.primaryApexType = true
-		} else {
-			a.suffix = zipApexSuffix
-		}
-	case flattenedApex:
-		if buildFlattenedAsDefault {
-			a.suffix = ""
-			a.primaryApexType = true
-		} else {
-			a.suffix = flattenedSuffix
-		}
-	}
-
-	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
-	case ext4FsType:
-		a.payloadFsType = ext4
-	case f2fsFsType:
-		a.payloadFsType = f2fs
-	case erofsFsType:
-		a.payloadFsType = erofs
-	default:
-		ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
-	}
-
-	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
-	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
-	// the same library in the system partition, thus effectively sharing the same libraries
-	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
-	// in the APEX.
-	a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable()
-
-	// APEXes targeting other than system/system_ext partitions use vendor/product variants.
-	// So we can't link them to /system/lib libs which are core variants.
-	if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
-		a.linkToSystemLib = false
-	}
-
-	forced := ctx.Config().ForceApexSymlinkOptimization()
-	updatable := a.Updatable() || a.FutureUpdatable()
-
-	// We don't need the optimization for updatable APEXes, as it might give false signal
-	// to the system health when the APEXes are still bundled (b/149805758).
-	if !forced && updatable && a.properties.ApexType == imageApex {
-		a.linkToSystemLib = false
-	}
-
-	// We also don't want the optimization for host APEXes, because it doesn't make sense.
-	if ctx.Host() {
-		a.linkToSystemLib = false
-	}
-
+	a.setApexTypeAndSuffix(ctx)
+	a.setPayloadFsType(ctx)
+	a.setSystemLibLink(ctx)
 	if a.properties.ApexType != zipApex {
 		a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
 	}
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 dbe9180..6abd8ff 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -949,8 +949,10 @@
 	// mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	// ensureNotContains(t, mylib2Cflags, "-include ")
 
-	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"])
+	// Ensure that genstub for platform-provided lib is invoked with --systemapi
+	ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+	// Ensure that genstub for apex-provided lib is invoked with --apex
+	ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex")
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
@@ -1134,8 +1136,8 @@
 	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
-	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"])
+	// Ensure that genstub is invoked with --systemapi
+	ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi")
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
diff --git a/apex/builder.go b/apex/builder.go
index 7e2b924..95435f5 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -76,16 +76,12 @@
 		Command: `rm -f $out && ${jsonmodify} $in ` +
 			`-a provideNativeLibs ${provideNativeLibs} ` +
 			`-a requireNativeLibs ${requireNativeLibs} ` +
+			`-se version 0 ${default_version} ` +
 			`${opt} ` +
 			`-o $out`,
 		CommandDeps: []string{"${jsonmodify}"},
 		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}",
-	})
+	}, "provideNativeLibs", "requireNativeLibs", "default_version", "opt")
 
 	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
 		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
@@ -210,21 +206,15 @@
 		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:  manifestJsonCommentsStripped,
+		Input:  src,
 		Output: manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
+			"default_version":   defaultManifestVersion,
 			"opt":               strings.Join(optCommands, " "),
 		},
 	})
@@ -667,8 +657,6 @@
 			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
 		}
 
-		optFlags = append(optFlags, "--apex_version "+defaultManifestVersion)
-
 		optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())
 
 		ctx.Build(pctx, android.BuildParams{
@@ -818,8 +806,8 @@
 		return
 	}
 
-	if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
-		a.isCompressed = true
+	a.setCompression(ctx)
+	if a.isCompressed {
 		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")
 
 		compressRule := android.NewRuleBuilder(pctx, ctx)
@@ -849,17 +837,12 @@
 		a.outputFile = signedCompressedOutputFile
 	}
 
-	installSuffix := suffix
-	if a.isCompressed {
-		installSuffix = imageCapexSuffix
-	}
-
 	if !a.installable() {
 		a.SkipInstall()
 	}
 
 	// Install to $OUT/soong/{target,host}/.../apex.
-	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
+	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+a.installSuffix(), a.outputFile,
 		a.compatSymlinks.Paths()...)
 
 	// installed-files.txt is dist'ed
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 1d1f49c..418b143 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -21,7 +21,6 @@
 	"fmt"
 	"path/filepath"
 	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 
@@ -83,6 +82,7 @@
 	OutputIds            []artifactId
 	TemplateContent      string
 	Substitutions        []KeyValuePair
+	FileContents         string
 }
 
 // actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -110,6 +110,7 @@
 	// 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
@@ -139,9 +140,6 @@
 	"%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"
 
@@ -225,20 +223,12 @@
 			// 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) ||
 			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.
-			// 3) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
+			// 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 {
 			directArtifactPaths = append(directArtifactPaths, path)
@@ -346,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 {
@@ -452,54 +444,6 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) pythonZipperActionBuildStatement(actionEntry action, prevBuildStatements []BuildStatement) (BuildStatement, error) {
-	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
-	if err != nil {
-		return BuildStatement{}, err
-	}
-	outputPaths, depfile, err := a.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, 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 (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
@@ -532,6 +476,47 @@
 	return buildStatement, nil
 }
 
+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 {
@@ -589,7 +574,7 @@
 
 // expandTemplateContent substitutes the tokens in a template.
 func expandTemplateContent(actionEntry action) string {
-	replacerString := []string{}
+	var replacerString []string
 	for _, pair := range actionEntry.Substitutions {
 		value := pair.Value
 		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
@@ -614,76 +599,35 @@
 	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
-	var 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
-}
-
-// 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, 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", "../bazel_tools/tools/zip/zipper/zipper", 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 {
+func (a action) isSymlinkAction() bool {
 	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
 }
 
-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
 	}
-	// Skip "Fail" actions, which are placeholder actions designed to always fail.
-	if a.Mnemonic == "Fail" {
+	// PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
+	if a.Mnemonic == "PythonZipper" {
 		return true
 	}
-	// TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
-	// about the contents that are written.
-	if a.Mnemonic == "FileWrite" {
+	// Skip "Fail" actions, which are placeholder actions designed to always fail.
+	if a.Mnemonic == "Fail" {
 		return true
 	}
 	if a.Mnemonic == "BaselineCoverage" {
@@ -703,7 +647,7 @@
 		}
 		labels = append([]string{currFragment.Label}, labels...)
 		if currId == currFragment.ParentId {
-			return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+			return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
 		}
 		currId = currFragment.ParentId
 	}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index c759d56..3a2bf0f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -460,6 +460,43 @@
 	}
 }
 
+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": [{
@@ -861,151 +898,67 @@
 	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 }],
+    { "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" }]
-}`
+    { "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{
-		{
-			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",
-		},
-		{
-			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{"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 ["python_binary.py"], output []`)
+	assertBuildStatements(t, []BuildStatement{
+		{
+			OutputPaths: []string{"foo.manifest"},
+			Mnemonic:    "SourceSymlinkManifest",
+		},
+	}, actual)
 }
 
 func assertError(t *testing.T, err error, expected string) {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index f5435f2..d32e619 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -1,6 +1,7 @@
 package cquery
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 )
@@ -9,6 +10,7 @@
 	GetOutputFiles  = &getOutputFilesRequestType{}
 	GetPythonBinary = &getPythonBinaryRequestType{}
 	GetCcInfo       = &getCcInfoType{}
+	GetApexInfo     = &getApexInfoType{}
 )
 
 type CcInfo struct {
@@ -179,7 +181,7 @@
 	const expectedLen = 10
 	splitString := strings.Split(rawString, "|")
 	if len(splitString) != expectedLen {
-		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+		return CcInfo{}, fmt.Errorf("expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
 	ccObjectsString := splitString[1]
@@ -215,6 +217,54 @@
 	}, nil
 }
 
+// Query Bazel for the artifacts generated by the apex modules.
+type getApexInfoType struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getApexInfoType) Name() string {
+	return "getApexInfo"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information. The function should have the following properties:
+//   - `target` is the only parameter to this function (a configured target).
+//   - The return value must be a string.
+//   - The function body should not be indented outside of its own scope.
+func (g getApexInfoType) StarlarkFunctionBody() string {
+	return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"]
+return "{%s}" % ",".join([
+    json_for_file("signed_output", info.signed_output),
+    json_for_file("unsigned_output", info.unsigned_output),
+    json_for_labels("provides_native_libs", info.provides_native_libs),
+    json_for_labels("requires_native_libs", info.requires_native_libs),
+    json_for_files("bundle_key_pair", info.bundle_key_pair),
+    json_for_files("container_key_pair", info.container_key_pair)
+    ])`
+}
+
+type ApexCqueryInfo struct {
+	SignedOutput     string   `json:"signed_output"`
+	UnsignedOutput   string   `json:"unsigned_output"`
+	ProvidesLibs     []string `json:"provides_native_libs"`
+	RequiresLibs     []string `json:"requires_native_libs"`
+	BundleKeyPair    []string `json:"bundle_key_pair"`
+	ContainerKeyPair []string `json:"container_key_pair"`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo {
+	var info ApexCqueryInfo
+	if err := json.Unmarshal([]byte(rawString), &info); err != nil {
+		panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err))
+	}
+	return info
+}
+
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
 // if the given string is empty.
 func splitOrEmpty(s string, sep string) []string {
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 606e285..34248ce 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -148,13 +148,13 @@
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", expectedSplits+1), // 2 too many
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
+			expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
 		},
 	}
 	for _, tc := range testCases {
@@ -167,3 +167,40 @@
 		}
 	}
 }
+
+func TestGetApexInfoParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput ApexCqueryInfo
+	}{
+		{
+			description:    "no result",
+			input:          "{}",
+			expectedOutput: ApexCqueryInfo{},
+		},
+		{
+			description: "one result",
+			input: `{"signed_output":"my.apex",` +
+				`"unsigned_output":"my.apex.unsigned",` +
+				`"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],` +
+				`"bundle_key_pair":["foo.pem","foo.privkey"],` +
+				`"container_key_pair":["foo.x509.pem", "foo.pk8"],` +
+				`"provides_native_libs":[]}`,
+			expectedOutput: ApexCqueryInfo{
+				SignedOutput:     "my.apex",
+				UnsignedOutput:   "my.apex.unsigned",
+				RequiresLibs:     []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
+				ProvidesLibs:     []string{},
+				BundleKeyPair:    []string{"foo.pem", "foo.privkey"},
+				ContainerKeyPair: []string{"foo.x509.pem", "foo.pk8"},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetApexInfo.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index b0c3899..5bff956 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -40,7 +40,7 @@
 		fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
 		os.Exit(1)
 	}
-	bp2buildFiles := CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
+	bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index f3345a6..6cb9509 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -189,7 +189,7 @@
 			content: "irrelevant",
 		},
 	}
-	files := CreateBazelFiles(ruleShims, make(map[string]BazelTargets), QueryView)
+	files := CreateBazelFiles(android.NullConfig("out", "out/soong"), ruleShims, make(map[string]BazelTargets), QueryView)
 
 	var actualSoongModuleBzl BazelFile
 	for _, f := range files {
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 4b013d9..4246f7d 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -50,6 +50,7 @@
 }
 
 func CreateBazelFiles(
+	cfg android.Config,
 	ruleShims map[string]RuleShim,
 	buildToTargets map[string]BazelTargets,
 	mode CodegenMode) []BazelFile {
@@ -74,16 +75,19 @@
 		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
 	}
 
-	files = append(files, createBuildFiles(buildToTargets, mode)...)
+	files = append(files, createBuildFiles(cfg, buildToTargets, mode)...)
 
 	return files
 }
 
-func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
+func createBuildFiles(cfg android.Config, buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
+	warnNotWriting := cfg.IsEnvTrue("BP2BUILD_VERBOSE")
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
 		if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
-			fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
+			if warnNotWriting {
+				fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
+			}
 			continue
 		}
 		targets := buildToTargets[dir]
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index e49d855..b0d0740 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -27,7 +27,8 @@
 }
 
 func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
-	files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
+	files := CreateBazelFiles(android.NullConfig("out", "out/soong"),
+		map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
 	expectedFilePaths := []bazelFilepath{
 		{
 			dir:      "",
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 04fac44..3a21c34 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -116,8 +116,6 @@
 	}
 	if _, err := os.Stat(metricsFile); err != nil {
 		fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile)
-	} else {
-		fmt.Printf("\nWrote bp2build metrics to: %s\n", metricsFile)
 	}
 }
 
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 818d7ae..c5075e5 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -1,6 +1,7 @@
 package bp2build
 
 import (
+	"android/soong/android"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -114,7 +115,7 @@
 // contain every file in buildFilesDir and srcDir excluding the files in
 // exclude. Collects every directory encountered during the traversal of srcDir
 // into acc.
-func plantSymlinkForestRecursive(topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string, okay *bool) {
+func plantSymlinkForestRecursive(cfg android.Config, topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string, okay *bool) {
 	if exclude != nil && exclude.excluded {
 		// This directory is not needed, bail out
 		return
@@ -179,7 +180,7 @@
 			if bDir && excludeChild != nil {
 				// Not in the source tree, but we have to exclude something from under
 				// this subtree, so descend
-				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+				plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 			} else {
 				// Not in the source tree, symlink BUILD file
 				symlinkIntoForest(topdir, forestChild, buildFilesChild)
@@ -188,19 +189,21 @@
 			if sDir && excludeChild != nil {
 				// Not in the build file tree, but we have to exclude something from
 				// under this subtree, so descend
-				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+				plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 			} else {
 				// Not in the build file tree, symlink source tree, carry on
 				symlinkIntoForest(topdir, forestChild, srcChild)
 			}
 		} else if sDir && bDir {
 			// Both are directories. Descend.
-			plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+			plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 		} else if !sDir && !bDir {
 			// Neither is a directory. Prioritize BUILD files generated by bp2build
 			// over any BUILD file imported into external/.
-			fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
-				buildFilesChild, srcChild, forestChild)
+			if cfg.IsEnvTrue("BP2BUILD_VERBOSE") {
+				fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
+					buildFilesChild, srcChild, forestChild)
+			}
 			symlinkIntoForest(topdir, forestChild, buildFilesChild)
 		} else {
 			// Both exist and one is a file. This is an error.
@@ -216,12 +219,12 @@
 // "srcDir" while excluding paths listed in "exclude". Returns the set of paths
 // under srcDir on which readdir() had to be called to produce the symlink
 // forest.
-func PlantSymlinkForest(topdir string, forest string, buildFiles string, srcDir string, exclude []string) []string {
+func PlantSymlinkForest(cfg android.Config, topdir string, forest string, buildFiles string, srcDir string, exclude []string) []string {
 	deps := make([]string, 0)
 	os.RemoveAll(shared.JoinPath(topdir, forest))
 	excludeTree := treeFromExcludePathList(exclude)
 	okay := true
-	plantSymlinkForestRecursive(topdir, forest, buildFiles, srcDir, excludeTree, &deps, &okay)
+	plantSymlinkForestRecursive(cfg, topdir, forest, buildFiles, srcDir, excludeTree, &deps, &okay)
 	if !okay {
 		os.Exit(1)
 	}
diff --git a/cc/builder.go b/cc/builder.go
index 70bbd6a..ab2b80a 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -63,10 +63,10 @@
 	ld, ldRE = pctx.RemoteStaticRules("ld",
 		blueprint.RuleParams{
 			Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
-				"${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
+				"${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
 			CommandDeps:    []string{"$ldCmd"},
 			Rspfile:        "${out}.rsp",
-			RspfileContent: "${in}",
+			RspfileContent: "${in} ${libFlags}",
 			// clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
 			Restat: true,
 		},
diff --git a/cc/cc.go b/cc/cc.go
index f04b6f0..bc95813 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -61,6 +61,9 @@
 		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
 		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
 
+		ctx.TopDown("fuzz_deps", fuzzMutatorDeps)
+		ctx.BottomUp("fuzz", fuzzMutator)
+
 		ctx.BottomUp("coverage", coverageMutator).Parallel()
 
 		ctx.TopDown("afdo_deps", afdoDepsMutator)
@@ -838,6 +841,7 @@
 	stl      *stl
 	sanitize *sanitize
 	coverage *coverage
+	fuzzer   *fuzzer
 	sabi     *sabi
 	vndkdep  *vndkdep
 	lto      *lto
@@ -1163,6 +1167,9 @@
 	if c.coverage != nil {
 		c.AddProperties(c.coverage.props()...)
 	}
+	if c.fuzzer != nil {
+		c.AddProperties(c.fuzzer.props()...)
+	}
 	if c.sabi != nil {
 		c.AddProperties(c.sabi.props()...)
 	}
@@ -1680,6 +1687,7 @@
 	module.stl = &stl{}
 	module.sanitize = &sanitize{}
 	module.coverage = &coverage{}
+	module.fuzzer = &fuzzer{}
 	module.sabi = &sabi{}
 	module.vndkdep = &vndkdep{}
 	module.lto = &lto{}
@@ -1901,6 +1909,9 @@
 	if c.coverage != nil {
 		flags, deps = c.coverage.flags(ctx, flags, deps)
 	}
+	if c.fuzzer != nil {
+		flags = c.fuzzer.flags(ctx, flags)
+	}
 	if c.lto != nil {
 		flags = c.lto.flags(ctx, flags)
 	}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index b6d196c..24732bf 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3342,6 +3342,125 @@
 	`)
 }
 
+func TestAFLFuzzTarget(t *testing.T) {
+	ctx := testCc(t, `
+		cc_afl_fuzz {
+			name: "test_afl_fuzz_target",
+			srcs: ["foo.c"],
+			host_supported: true,
+			static_libs: [
+				"afl_fuzz_static_lib",
+			],
+			shared_libs: [
+				"afl_fuzz_shared_lib",
+			],
+		}
+		cc_fuzz {
+			name: "test_fuzz_target",
+			srcs: ["foo.c"],
+			static_libs: [
+				"afl_fuzz_static_lib",
+				"libfuzzer_only_static_lib",
+			],
+			shared_libs: [
+				"afl_fuzz_shared_lib",
+			],
+		}
+		cc_library {
+			name: "afl_fuzz_static_lib",
+			host_supported: true,
+			srcs: ["static_file.c"],
+		}
+		cc_library {
+			name: "libfuzzer_only_static_lib",
+			host_supported: true,
+			srcs: ["static_file.c"],
+		}
+		cc_library {
+			name: "afl_fuzz_shared_lib",
+			host_supported: true,
+			srcs: ["shared_file.c"],
+			static_libs: [
+				"second_static_lib",
+			],
+		}
+		cc_library_headers {
+			name: "libafl_headers",
+			vendor_available: true,
+			host_supported: true,
+			export_include_dirs: [
+				"include",
+				"instrumentation",
+			],
+		}
+		cc_object {
+			name: "afl-compiler-rt",
+			vendor_available: true,
+			host_supported: true,
+			cflags: [
+				"-fPIC",
+			],
+			srcs: [
+				"instrumentation/afl-compiler-rt.o.c",
+			],
+		}
+		cc_library {
+			name: "second_static_lib",
+			host_supported: true,
+			srcs: ["second_file.c"],
+		}
+		filegroup {
+			name: "aflpp_driver",
+			srcs: [
+				"aflpp_driver.c",
+			],
+		}`)
+
+	checkPcGuardFlag := func(
+		modName string, variantName string, shouldHave bool) {
+		cc := ctx.ModuleForTests(modName, variantName).Rule("cc")
+
+		cFlags, ok := cc.Args["cFlags"]
+		if !ok {
+			t.Errorf("Could not find cFlags for module %s and variant %s",
+				modName, variantName)
+		}
+
+		if strings.Contains(
+			cFlags, "-fsanitize-coverage=trace-pc-guard") != shouldHave {
+			t.Errorf("Flag was found: %t. Expected to find flag:  %t. "+
+				"Test failed for module %s and variant %s",
+				!shouldHave, shouldHave, modName, variantName)
+		}
+	}
+
+	for _, vnt := range ctx.ModuleVariantsForTests("libfuzzer_only_static_lib") {
+		if strings.Contains(vnt, "fuzzer_afl") {
+			t.Errorf("libfuzzer_only_static_lib has afl variant and should not")
+		}
+	}
+
+	moduleName := "test_afl_fuzz_target"
+	variantName := "android_arm64_armv8-a_fuzzer_afl"
+	checkPcGuardFlag(moduleName, variantName, true)
+
+	moduleName = "afl_fuzz_static_lib"
+	variantName = "android_arm64_armv8-a_static"
+	checkPcGuardFlag(moduleName, variantName, false)
+	checkPcGuardFlag(moduleName, variantName+"_fuzzer", false)
+	checkPcGuardFlag(moduleName, variantName+"_fuzzer_afl", true)
+
+	moduleName = "second_static_lib"
+	checkPcGuardFlag(moduleName, variantName, false)
+	checkPcGuardFlag(moduleName, variantName+"_fuzzer", false)
+	checkPcGuardFlag(moduleName, variantName+"_fuzzer_afl", true)
+
+	ctx.ModuleForTests("afl_fuzz_shared_lib",
+		"android_arm64_armv8-a_shared").Rule("cc")
+	ctx.ModuleForTests("afl_fuzz_shared_lib",
+		"android_arm64_armv8-a_shared_fuzzer_afl").Rule("cc")
+}
+
 // Simple smoke test for the cc_fuzz target that ensures the rule compiles
 // correctly.
 func TestFuzzTarget(t *testing.T) {
diff --git a/cc/config/global.go b/cc/config/global.go
index c5fde55..26d93ab 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -228,7 +228,6 @@
 		// New warnings to be fixed after clang-r383902.
 		"-Wno-deprecated-copy",                      // http://b/153746672
 		"-Wno-range-loop-construct",                 // http://b/153747076
-		"-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-pessimizing-move",                     // http://b/154270751
@@ -239,6 +238,8 @@
 		// New warnings to be fixed after clang-r433403
 		"-Wno-error=unused-but-set-variable",  // http://b/197240255
 		"-Wno-error=unused-but-set-parameter", // http://b/197240255
+		// New warnings to be fixed after clang-r458507
+		"-Wno-error=unqualified-std-cast-call", // http://b/239662094
 	}
 
 	noOverrideExternalGlobalCflags = []string{
@@ -247,6 +248,8 @@
 		"-Wno-unused-but-set-parameter",
 		// http://b/215753485
 		"-Wno-bitwise-instead-of-logical",
+		// http://b/232926688
+		"-Wno-misleading-indentation",
 	}
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -278,6 +281,12 @@
 
 		// http://b/175068488
 		"-Wno-string-concatenation",
+
+		// http://b/239661264
+		"-Wno-deprecated-non-prototype",
+	}
+
+	llvmNextExtraCommonGlobalCflags = []string{
 	}
 
 	IllegalFlags = []string{
@@ -291,8 +300,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r450784e"
-	ClangDefaultShortVersion = "14.0.7"
+	ClangDefaultVersion      = "clang-r458507"
+	ClangDefaultShortVersion = "15.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -361,6 +370,15 @@
 		if ctx.Config().IsEnvTrue("USE_CCACHE") {
 			flags = append(flags, "-Wno-unused-command-line-argument")
 		}
+
+		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+			flags = append(flags, llvmNextExtraCommonGlobalCflags...)
+		}
+
+		if ctx.Config().IsEnvTrue("ALLOW_UNKNOWN_WARNING_OPTION") {
+			flags = append(flags, "-Wno-error=unknown-warning-option")
+		}
+
 		return strings.Join(flags, " ")
 	})
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 23d81d6..d6af97f 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -27,29 +27,113 @@
 )
 
 func init() {
-	android.RegisterModuleType("cc_fuzz", FuzzFactory)
+	android.RegisterModuleType("cc_afl_fuzz", AFLFuzzFactory)
+	android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
 	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
+	android.RegisterSingletonType("cc_afl_fuzz_packaging", fuzzAFLPackagingFactory)
+}
+
+type FuzzProperties struct {
+	AFLEnabled  bool `blueprint:"mutated"`
+	AFLAddFlags bool `blueprint:"mutated"`
+}
+
+type fuzzer struct {
+	Properties FuzzProperties
+}
+
+func (fuzzer *fuzzer) flags(ctx ModuleContext, flags Flags) Flags {
+	if fuzzer.Properties.AFLAddFlags {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-coverage=trace-pc-guard")
+	}
+
+	return flags
+}
+
+func (fuzzer *fuzzer) props() []interface{} {
+	return []interface{}{&fuzzer.Properties}
+}
+
+func fuzzMutatorDeps(mctx android.TopDownMutatorContext) {
+	currentModule, ok := mctx.Module().(*Module)
+	if !ok {
+		return
+	}
+
+	if currentModule.fuzzer == nil || !currentModule.fuzzer.Properties.AFLEnabled {
+		return
+	}
+
+	mctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+		c, ok := child.(*Module)
+		if !ok {
+			return false
+		}
+
+		if c.sanitize == nil {
+			return false
+		}
+
+		isFuzzerPointer := c.sanitize.getSanitizerBoolPtr(Fuzzer)
+		if isFuzzerPointer == nil || !*isFuzzerPointer {
+			return false
+		}
+
+		if c.fuzzer == nil {
+			return false
+		}
+
+		c.fuzzer.Properties.AFLEnabled = true
+		c.fuzzer.Properties.AFLAddFlags = true
+		return true
+	})
+}
+
+func fuzzMutator(mctx android.BottomUpMutatorContext) {
+	if c, ok := mctx.Module().(*Module); ok && c.fuzzer != nil {
+		if !c.fuzzer.Properties.AFLEnabled {
+			return
+		}
+
+		if c.Binary() {
+			m := mctx.CreateVariations("afl")
+			m[0].(*Module).fuzzer.Properties.AFLEnabled = true
+			m[0].(*Module).fuzzer.Properties.AFLAddFlags = true
+		} else {
+			m := mctx.CreateVariations("", "afl")
+			m[0].(*Module).fuzzer.Properties.AFLEnabled = false
+			m[0].(*Module).fuzzer.Properties.AFLAddFlags = false
+
+			m[1].(*Module).fuzzer.Properties.AFLEnabled = true
+			m[1].(*Module).fuzzer.Properties.AFLAddFlags = true
+		}
+	}
 }
 
 // cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
 // $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on
 // your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree.
-func FuzzFactory() android.Module {
-	module := NewFuzz(android.HostAndDeviceSupported)
+func LibFuzzFactory() android.Module {
+	module := NewFuzzer(android.HostAndDeviceSupported, fuzz.Cc)
 	return module.Init()
 }
 
-func NewFuzzInstaller() *baseInstaller {
-	return NewBaseInstaller("fuzz", "fuzz", InstallInData)
+// cc_afl_fuzz creates a host/device AFL++ fuzzer binary.
+// AFL++ is an open source framework used to fuzz libraries
+// Host binaries can be found at $ANDROID_HOST_OUT/afl_fuzz/ and device
+// binaries can be found at $ANDROID_PRODUCT_OUT/data/afl_fuzz in your
+// build tree
+func AFLFuzzFactory() android.Module {
+	module := NewFuzzer(android.HostAndDeviceSupported, fuzz.AFL)
+	return module.Init()
 }
 
 type fuzzBinary struct {
 	*binaryDecorator
 	*baseCompiler
-
-	fuzzPackagedModule fuzz.FuzzPackagedModule
-
+	fuzzPackagedModule  fuzz.FuzzPackagedModule
 	installedSharedDeps []string
+	fuzzType            fuzz.FuzzType
 }
 
 func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -66,11 +150,17 @@
 	fuzz.binaryDecorator.linkerInit(ctx)
 }
 
-func (fuzz *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.StaticLibs = append(deps.StaticLibs,
-		config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
-	deps = fuzz.binaryDecorator.linkerDeps(ctx, deps)
-	return deps
+func (fuzzBin *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	if fuzzBin.fuzzType == fuzz.AFL {
+		deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers")
+		deps = fuzzBin.binaryDecorator.linkerDeps(ctx, deps)
+		return deps
+
+	} else {
+		deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+		deps = fuzzBin.binaryDecorator.linkerDeps(ctx, deps)
+		return deps
+	}
 }
 
 func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -80,6 +170,7 @@
 	// target packages.
 	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
 	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+
 	return flags
 }
 
@@ -149,63 +240,68 @@
 }
 
 func sharedLibraryInstallLocation(
-	libraryPath android.Path, isHost bool, archString string) string {
+	libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
 	installLocation := "$(PRODUCT_OUT)/data"
 	if isHost {
 		installLocation = "$(HOST_OUT)"
 	}
 	installLocation = filepath.Join(
-		installLocation, "fuzz", archString, "lib", libraryPath.Base())
+		installLocation, fuzzDir, archString, "lib", libraryPath.Base())
 	return installLocation
 }
 
 // Get the device-only shared library symbols install directory.
-func sharedLibrarySymbolsInstallLocation(libraryPath android.Path, archString string) string {
-	return filepath.Join("$(PRODUCT_OUT)/symbols/data/fuzz/", archString, "/lib/", libraryPath.Base())
+func sharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
+	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
 }
 
-func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
-	fuzz.binaryDecorator.baseInstaller.dir = filepath.Join(
-		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
-	fuzz.binaryDecorator.baseInstaller.dir64 = filepath.Join(
-		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
-	fuzz.binaryDecorator.baseInstaller.install(ctx, file)
+func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
+	installBase := "fuzz"
+	if fuzzBin.fuzzType == fuzz.AFL {
+		installBase = "afl_fuzz"
+	}
 
-	fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
+	fuzzBin.binaryDecorator.baseInstaller.dir = filepath.Join(
+		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzzBin.binaryDecorator.baseInstaller.dir64 = filepath.Join(
+		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
+
+	fuzzBin.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzBin.fuzzPackagedModule.FuzzProperties.Corpus)
 	builder := android.NewRuleBuilder(pctx, ctx)
 	intermediateDir := android.PathForModuleOut(ctx, "corpus")
-	for _, entry := range fuzz.fuzzPackagedModule.Corpus {
+	for _, entry := range fuzzBin.fuzzPackagedModule.Corpus {
 		builder.Command().Text("cp").
 			Input(entry).
 			Output(intermediateDir.Join(ctx, entry.Base()))
 	}
 	builder.Build("copy_corpus", "copy corpus")
-	fuzz.fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
+	fuzzBin.fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
 
-	fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
+	fuzzBin.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzBin.fuzzPackagedModule.FuzzProperties.Data)
 	builder = android.NewRuleBuilder(pctx, ctx)
 	intermediateDir = android.PathForModuleOut(ctx, "data")
-	for _, entry := range fuzz.fuzzPackagedModule.Data {
+	for _, entry := range fuzzBin.fuzzPackagedModule.Data {
 		builder.Command().Text("cp").
 			Input(entry).
 			Output(intermediateDir.Join(ctx, entry.Rel()))
 	}
 	builder.Build("copy_data", "copy data")
-	fuzz.fuzzPackagedModule.DataIntermediateDir = intermediateDir
+	fuzzBin.fuzzPackagedModule.DataIntermediateDir = intermediateDir
 
-	if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
-		fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
-		if fuzz.fuzzPackagedModule.Dictionary.Ext() != ".dict" {
+	if fuzzBin.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+		fuzzBin.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzBin.fuzzPackagedModule.FuzzProperties.Dictionary)
+		if fuzzBin.fuzzPackagedModule.Dictionary.Ext() != ".dict" {
 			ctx.PropertyErrorf("dictionary",
 				"Fuzzer dictionary %q does not have '.dict' extension",
-				fuzz.fuzzPackagedModule.Dictionary.String())
+				fuzzBin.fuzzPackagedModule.Dictionary.String())
 		}
 	}
 
-	if fuzz.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+	if fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
 		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
-		android.WriteFileRule(ctx, configPath, fuzz.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
-		fuzz.fuzzPackagedModule.Config = configPath
+		android.WriteFileRule(ctx, configPath, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+		fuzzBin.fuzzPackagedModule.Config = configPath
 	}
 
 	// Grab the list of required shared libraries.
@@ -225,31 +321,36 @@
 	})
 
 	for _, lib := range sharedLibraries {
-		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			sharedLibraryInstallLocation(
-				lib, ctx.Host(), ctx.Arch().ArchType.String()))
+				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
-			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
-				sharedLibrarySymbolsInstallLocation(lib, ctx.Arch().ArchType.String()))
+			fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
+				sharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 }
 
-func NewFuzz(hod android.HostOrDeviceSupported) *Module {
+func NewFuzzer(hod android.HostOrDeviceSupported, fuzzType fuzz.FuzzType) *Module {
 	module, binary := newBinary(hod, false)
+	baseInstallerPath := "fuzz"
+	if fuzzType == fuzz.AFL {
+		baseInstallerPath = "afl_fuzz"
+	}
 
-	binary.baseInstaller = NewFuzzInstaller()
+	binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData)
 	module.sanitize.SetSanitizer(Fuzzer, true)
 
-	fuzz := &fuzzBinary{
+	fuzzBin := &fuzzBinary{
 		binaryDecorator: binary,
 		baseCompiler:    NewBaseCompiler(),
+		fuzzType:        fuzzType,
 	}
-	module.compiler = fuzz
-	module.linker = fuzz
-	module.installer = fuzz
+	module.compiler = fuzzBin
+	module.linker = fuzzBin
+	module.installer = fuzzBin
 
 	// The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
@@ -268,6 +369,17 @@
 		ctx.AppendProperties(&disableDarwinAndLinuxBionic)
 	})
 
+	if fuzzType == fuzz.AFL {
+		// Add cc_objects to Srcs
+		fuzzBin.baseCompiler.Properties.Srcs = append(fuzzBin.baseCompiler.Properties.Srcs, ":aflpp_driver", ":afl-compiler-rt")
+		module.fuzzer.Properties.AFLEnabled = true
+		module.compiler.appendCflags([]string{
+			"-Wno-unused-result",
+			"-Wno-unused-parameter",
+			"-Wno-unused-function",
+		})
+	}
+
 	return module
 }
 
@@ -275,10 +387,30 @@
 // their architecture & target/host specific zip file.
 type ccFuzzPackager struct {
 	fuzz.FuzzPackager
+	fuzzPackagingArchModules         string
+	fuzzTargetSharedDepsInstallPairs string
+	allFuzzTargetsName               string
 }
 
 func fuzzPackagingFactory() android.Singleton {
-	return &ccFuzzPackager{}
+
+	fuzzPackager := &ccFuzzPackager{
+		fuzzPackagingArchModules:         "SOONG_FUZZ_PACKAGING_ARCH_MODULES",
+		fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+		allFuzzTargetsName:               "ALL_FUZZ_TARGETS",
+	}
+	fuzzPackager.FuzzType = fuzz.Cc
+	return fuzzPackager
+}
+
+func fuzzAFLPackagingFactory() android.Singleton {
+	fuzzPackager := &ccFuzzPackager{
+		fuzzPackagingArchModules:         "SOONG_AFL_FUZZ_PACKAGING_ARCH_MODULES",
+		fuzzTargetSharedDepsInstallPairs: "AFL_FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+		allFuzzTargetsName:               "ALL_AFL_FUZZ_TARGETS",
+	}
+	fuzzPackager.FuzzType = fuzz.AFL
+	return fuzzPackager
 }
 
 func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
@@ -306,8 +438,9 @@
 			return
 		}
 
+		sharedLibsInstallDirPrefix := "lib"
 		fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
-		if !ok {
+		if !ok || fuzzModule.fuzzType != s.FuzzType {
 			return
 		}
 
@@ -316,8 +449,18 @@
 			hostOrTargetString = "host"
 		}
 
+		fpm := fuzz.FuzzPackagedModule{}
+		if ok {
+			fpm = fuzzModule.fuzzPackagedModule
+		}
+
+		intermediatePath := "fuzz"
+		if s.FuzzType == fuzz.AFL {
+			intermediatePath = "afl_fuzz"
+		}
+
 		archString := ccModule.Arch().ArchType.String()
-		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
+		archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
 		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
 		// Grab the list of required shared libraries.
@@ -327,22 +470,21 @@
 		builder := android.NewRuleBuilder(pctx, ctx)
 
 		// Package the corpus, data, dict and config into a zipfile.
-		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
+		files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
 
 		// Package shared libraries
-		files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)
+		files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
 		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
 
-		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
 			return
 		}
 	})
 
-	s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
-
+	s.CreateFuzzPackage(ctx, archDirs, s.FuzzType, pctx)
 }
 
 func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
@@ -353,27 +495,34 @@
 	// ready to handle phony targets created in Soong. In the meantime, this
 	// exports the phony 'fuzz' target and dependencies on packages to
 	// core/main.mk so that we can use dist-for-goals.
-	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-	ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+
+	ctx.Strict(s.fuzzPackagingArchModules, strings.Join(packages, " "))
+
+	ctx.Strict(s.fuzzTargetSharedDepsInstallPairs,
 		strings.Join(s.FuzzPackager.SharedLibInstallStrings, " "))
 
 	// Preallocate the slice of fuzz targets to minimise memory allocations.
-	s.PreallocateSlice(ctx, "ALL_FUZZ_TARGETS")
+	s.PreallocateSlice(ctx, s.allFuzzTargetsName)
 }
 
 // GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
 // packaging.
-func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
+func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
 	var files []fuzz.FileToZip
 
+	fuzzDir := "fuzz"
+	if s.FuzzType == fuzz.AFL {
+		fuzzDir = "afl_fuzz"
+	}
+
 	for _, library := range sharedLibraries {
-		files = append(files, fuzz.FileToZip{library, "lib"})
+		files = append(files, fuzz.FileToZip{library, destinationPathPrefix})
 
 		// For each architecture-specific shared library dependency, we need to
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
 		installDestination := sharedLibraryInstallLocation(
-			library, module.Host(), archString)
+			library, module.Host(), fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
 		}
@@ -391,7 +540,7 @@
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
 		if !module.Host() {
-			symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, archString)
+			symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
diff --git a/cc/image.go b/cc/image.go
index 3a0857b..921b2bb 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -533,7 +533,7 @@
 		}
 	} else {
 		// This is either in /system (or similar: /data), or is a
-		// modules built with the NDK. Modules built with the NDK
+		// module built with the NDK. Modules built with the NDK
 		// will be restricted using the existing link type checks.
 		coreVariantNeeded = true
 	}
diff --git a/cc/library.go b/cc/library.go
index 849d18a..bd6ccb5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1032,9 +1032,19 @@
 			ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
 			return Objects{}
 		}
+		// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
+		// systemapi, respectively. The former is for symbols defined in platform libraries
+		// and the latter is for symbols defined in APEXes.
+		var flag string
+		if ctx.Module().(android.ApexModule).NotInPlatform() {
+			flag = "--apex"
+		} else {
+			// TODO(b/239274367) drop --apex when #apex is replaced with #systemapi
+			// in the map.txt files of platform libraries
+			flag = "--systemapi --apex"
+		}
 		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
-			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion),
-			"--apex")
+			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
 		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		library.versionScriptPath = android.OptionalPathForPath(
 			nativeAbiResult.versionScript)
diff --git a/cc/ndk_api_coverage_parser/__init__.py b/cc/ndk_api_coverage_parser/__init__.py
index 8b9cd66..752f7d4 100755
--- a/cc/ndk_api_coverage_parser/__init__.py
+++ b/cc/ndk_api_coverage_parser/__init__.py
@@ -23,6 +23,7 @@
 from xml.etree.ElementTree import Element, SubElement, tostring
 from symbolfile import (
     ALL_ARCHITECTURES,
+    Filter,
     FUTURE_API_LEVEL,
     MultiplyDefinedSymbolError,
     SymbolFileParser,
@@ -139,9 +140,8 @@
 
     with open(args.symbol_file) as symbol_file:
         try:
-            versions = SymbolFileParser(
-                symbol_file, api_map, "", FUTURE_API_LEVEL, True, True
-            ).parse()
+            filt = Filter("", FUTURE_API_LEVEL, True, True, True)
+            versions = SymbolFileParser(symbol_file, api_map, filt).parse()
         except MultiplyDefinedSymbolError as ex:
             sys.exit('{}: error: {}'.format(args.symbol_file, ex))
 
diff --git a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
index 141059c..7c6ef68 100644
--- a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
+++ b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
@@ -20,7 +20,7 @@
 import unittest
 
 from xml.etree.ElementTree import fromstring
-from symbolfile import FUTURE_API_LEVEL, SymbolFileParser
+from symbolfile import Filter, FUTURE_API_LEVEL, SymbolFileParser
 import ndk_api_coverage_parser as nparser
 
 
@@ -78,9 +78,8 @@
         """
             )
         )
-        parser = SymbolFileParser(
-            input_file, {}, "", FUTURE_API_LEVEL, True, True
-        )
+        filt = Filter("", FUTURE_API_LEVEL, True, True, True)
+        parser = SymbolFileParser(input_file, {}, filt)
         generator = nparser.XmlGenerator(io.StringIO())
         result = generator.convertToXml(parser.parse())
         expected = fromstring(
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 5e6b8f5..f893d41 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -29,15 +29,12 @@
 class Generator:
     """Output generator that writes stub source files and version scripts."""
     def __init__(self, src_file: TextIO, version_script: TextIO,
-                 symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
-                 apex: bool) -> None:
+                 symbol_list: TextIO, filt: symbolfile.Filter) -> None:
         self.src_file = src_file
         self.version_script = version_script
         self.symbol_list = symbol_list
-        self.arch = arch
-        self.api = api
-        self.llndk = llndk
-        self.apex = apex
+        self.filter = filt
+        self.api = filt.api
 
     def write(self, versions: Iterable[Version]) -> None:
         """Writes all symbol data to the output files."""
@@ -47,8 +44,7 @@
 
     def write_version(self, version: Version) -> None:
         """Writes a single version block's data to the output files."""
-        if symbolfile.should_omit_version(version, self.arch, self.api,
-                                          self.llndk, self.apex):
+        if self.filter.should_omit_version(version):
             return
 
         section_versioned = symbolfile.symbol_versioned_in_api(
@@ -56,8 +52,7 @@
         version_empty = True
         pruned_symbols = []
         for symbol in version.symbols:
-            if symbolfile.should_omit_symbol(symbol, self.arch, self.api,
-                                             self.llndk, self.apex):
+            if self.filter.should_omit_symbol(symbol):
                 continue
 
             if symbolfile.symbol_versioned_in_api(symbol.tags, self.api):
@@ -110,12 +105,12 @@
     parser.add_argument(
         '--apex',
         action='store_true',
-        help='Use the APEX variant. Note: equivalent to --system-api.')
+        help='Use the APEX variant.')
     parser.add_argument(
-        '--system-api',
+        '--systemapi',
         action='store_true',
-        dest='apex',
-        help='Use the SystemAPI variant. Note: equivalent to --apex.')
+        dest='systemapi',
+        help='Use the SystemAPI variant.')
 
     parser.add_argument('--api-map',
                         type=resolved_path,
@@ -152,11 +147,10 @@
         verbosity = 2
     logging.basicConfig(level=verbose_map[verbosity])
 
+    filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi)
     with args.symbol_file.open() as symbol_file:
         try:
-            versions = symbolfile.SymbolFileParser(symbol_file, api_map,
-                                                   args.arch, api, args.llndk,
-                                                   args.apex).parse()
+          versions = symbolfile.SymbolFileParser(symbol_file, api_map, filt).parse()
         except symbolfile.MultiplyDefinedSymbolError as ex:
             sys.exit(f'{args.symbol_file}: error: {ex}')
 
@@ -164,7 +158,7 @@
         with args.version_script.open('w') as version_script:
             with args.symbol_list.open('w') as symbol_list:
                 generator = Generator(src_file, version_script, symbol_list,
-                                      args.arch, api, args.llndk, args.apex)
+                                      filt)
                 generator.write(versions)
 
 
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index c8cd056..450719b 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -18,6 +18,7 @@
 import io
 import textwrap
 import unittest
+from copy import copy
 
 import symbolfile
 from symbolfile import Arch, Tags
@@ -29,6 +30,9 @@
 
 
 class GeneratorTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
+
     def test_omit_version(self) -> None:
         # Thorough testing of the cases involved here is handled by
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
@@ -37,7 +41,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
 
         version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
             symbolfile.Symbol('foo', Tags()),
@@ -70,7 +74,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
 
         version = symbolfile.Version('VERSION_1', None, Tags(), [
             symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
@@ -106,7 +110,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
 
         versions = [
             symbolfile.Version('VERSION_1', None, Tags(), [
@@ -162,6 +166,9 @@
 
 
 class IntegrationTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
+
     def test_integration(self) -> None:
         api_map = {
             'O': 9000,
@@ -199,8 +206,7 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
-                                             9, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, self.filter)
         versions = parser.parse()
 
         src_file = io.StringIO()
@@ -208,7 +214,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -263,16 +269,18 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
-                                             9001, False, False)
+        f = copy(self.filter)
+        f.api = 9001
+        parser = symbolfile.SymbolFileParser(input_file, api_map, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
         symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.api = 9001
         generator = ndkstubgen.Generator(src_file,
-                                         version_file, symbol_list_file,
-                                         Arch('arm'), 9001, False, False)
+                                         version_file, symbol_list_file, f)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -322,8 +330,9 @@
             } VERSION_2;
 
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        f = copy(self.filter)
+        f.api = 16
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
 
         with self.assertRaises(
                 symbolfile.MultiplyDefinedSymbolError) as ex_context:
@@ -370,16 +379,18 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
-                                             9, False, True)
+        f = copy(self.filter)
+        f.apex = True
+        parser = symbolfile.SymbolFileParser(input_file, api_map, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
         symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.apex = True
         generator = ndkstubgen.Generator(src_file,
-                                         version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, True)
+                                         version_file, symbol_list_file, f)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -428,20 +439,19 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
-                                             9, llndk=False, apex=True)
+        f = copy(self.filter)
+        f.apex = True
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
         symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.apex = True
         generator = ndkstubgen.Generator(src_file,
                                          version_file,
-                                         symbol_list_file,
-                                         Arch('arm'),
-                                         9,
-                                         llndk=False,
-                                         apex=True)
+                                         symbol_list_file, f)
         generator.write(versions)
 
         self.assertEqual('', src_file.getvalue())
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index f8d1841..471a12f 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -78,12 +78,17 @@
     @property
     def has_mode_tags(self) -> bool:
         """Returns True if any mode tags (apex, llndk, etc) are set."""
-        return self.has_apex_tags or self.has_llndk_tags
+        return self.has_apex_tags or self.has_llndk_tags or self.has_systemapi_tags
 
     @property
     def has_apex_tags(self) -> bool:
         """Returns True if any APEX tags are set."""
-        return 'apex' in self.tags or 'systemapi' in self.tags
+        return 'apex' in self.tags
+
+    @property
+    def has_systemapi_tags(self) -> bool:
+        """Returns True if any APEX tags are set."""
+        return 'systemapi' in self.tags
 
     @property
     def has_llndk_tags(self) -> bool:
@@ -198,50 +203,57 @@
     """
     return split_tag(tag)[1]
 
-
-def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
-                      apex: bool) -> bool:
-    """Returns True if the tagged object should be omitted.
-
-    This defines the rules shared between version tagging and symbol tagging.
+class Filter:
+    """A filter encapsulates a condition that tells whether a version or a
+    symbol should be omitted or not
     """
-    # The apex and llndk tags will only exclude APIs from other modes. If in
-    # APEX or LLNDK mode and neither tag is provided, we fall back to the
-    # default behavior because all NDK symbols are implicitly available to APEX
-    # and LLNDK.
-    if tags.has_mode_tags:
-        if not apex and not llndk:
+
+    def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi: bool = False):
+        self.arch = arch
+        self.api = api
+        self.llndk = llndk
+        self.apex = apex
+        self.systemapi = systemapi
+
+    def _should_omit_tags(self, tags: Tags) -> bool:
+        """Returns True if the tagged object should be omitted.
+
+        This defines the rules shared between version tagging and symbol tagging.
+        """
+        # The apex and llndk tags will only exclude APIs from other modes. If in
+        # APEX or LLNDK mode and neither tag is provided, we fall back to the
+        # default behavior because all NDK symbols are implicitly available to
+        # APEX and LLNDK.
+        if tags.has_mode_tags:
+            if self.apex and tags.has_apex_tags:
+                return False
+            if self.llndk and tags.has_llndk_tags:
+                return False
+            if self.systemapi and tags.has_systemapi_tags:
+                return False
             return True
-        if apex and not tags.has_apex_tags:
+        if not symbol_in_arch(tags, self.arch):
             return True
-        if llndk and not tags.has_llndk_tags:
+        if not symbol_in_api(tags, self.arch, self.api):
             return True
-    if not symbol_in_arch(tags, arch):
-        return True
-    if not symbol_in_api(tags, arch, api):
-        return True
-    return False
+        return False
 
+    def should_omit_version(self, version: Version) -> bool:
+        """Returns True if the version section should be omitted.
 
-def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
-                        apex: bool) -> bool:
-    """Returns True if the version section should be omitted.
+        We want to omit any sections that do not have any symbols we'll have in
+        the stub library. Sections that contain entirely future symbols or only
+        symbols for certain architectures.
+        """
+        if version.is_private:
+            return True
+        if version.tags.has_platform_only_tags:
+            return True
+        return self._should_omit_tags(version.tags)
 
-    We want to omit any sections that do not have any symbols we'll have in the
-    stub library. Sections that contain entirely future symbols or only symbols
-    for certain architectures.
-    """
-    if version.is_private:
-        return True
-    if version.tags.has_platform_only_tags:
-        return True
-    return _should_omit_tags(version.tags, arch, api, llndk, apex)
-
-
-def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
-                       apex: bool) -> bool:
-    """Returns True if the symbol should be omitted."""
-    return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
+    def should_omit_symbol(self, symbol: Symbol) -> bool:
+        """Returns True if the symbol should be omitted."""
+        return self._should_omit_tags(symbol.tags)
 
 
 def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
@@ -316,14 +328,10 @@
 
 class SymbolFileParser:
     """Parses NDK symbol files."""
-    def __init__(self, input_file: TextIO, api_map: ApiMap, arch: Arch,
-                 api: int, llndk: bool, apex: bool) -> None:
+    def __init__(self, input_file: TextIO, api_map: ApiMap, filt: Filter) -> None:
         self.input_file = input_file
         self.api_map = api_map
-        self.arch = arch
-        self.api = api
-        self.llndk = llndk
-        self.apex = apex
+        self.filter = filt
         self.current_line: Optional[str] = None
 
     def parse(self) -> List[Version]:
@@ -352,13 +360,11 @@
         symbol_names = set()
         multiply_defined_symbols = set()
         for version in versions:
-            if should_omit_version(version, self.arch, self.api, self.llndk,
-                                   self.apex):
+            if self.filter.should_omit_version(version):
                 continue
 
             for symbol in version.symbols:
-                if should_omit_symbol(symbol, self.arch, self.api, self.llndk,
-                                      self.apex):
+                if self.filter.should_omit_symbol(symbol):
                     continue
 
                 if symbol.name in symbol_names:
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index c1e8219..e17a8d0 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,7 +19,8 @@
 import unittest
 
 import symbolfile
-from symbolfile import Arch, Tag, Tags, Version
+from symbolfile import Arch, Tag, Tags, Version, Symbol, Filter
+from copy import copy
 
 # pylint: disable=missing-docstring
 
@@ -202,178 +203,188 @@
 
 
 class OmitVersionTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = Filter(arch = Arch('arm'), api = 9)
+        self.version = Version('foo', None, Tags(), [])
+
+    def assertOmit(self, f: Filter, v: Version) -> None:
+        self.assertTrue(f.should_omit_version(v))
+
+    def assertInclude(self, f: Filter, v: Version) -> None:
+        self.assertFalse(f.should_omit_version(v))
+
     def test_omit_private(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, False))
+        f = self.filter
+        v = self.version
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo_PRIVATE', None, Tags(), []),
-                Arch('arm'), 9, False, False))
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo_PLATFORM', None, Tags(), []),
-                Arch('arm'), 9, False, False))
+        self.assertInclude(f, v)
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None,
-                                   Tags.from_strs(['platform-only']), []),
-                Arch('arm'), 9, False, False))
+        v.name = 'foo_PRIVATE'
+        self.assertOmit(f, v)
+
+        v.name = 'foo_PLATFORM'
+        self.assertOmit(f, v)
+
+        v.name = 'foo'
+        v.tags = Tags.from_strs(['platform-only'])
+        self.assertOmit(f, v)
 
     def test_omit_llndk(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
-                Arch('arm'), 9, False, False))
+        f = self.filter
+        v = self.version
+        v_llndk = copy(v)
+        v_llndk.tags = Tags.from_strs(['llndk'])
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                True, False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
-                Arch('arm'), 9, True, False))
+        self.assertOmit(f, v_llndk)
+
+        f.llndk = True
+        self.assertInclude(f, v)
+        self.assertInclude(f, v_llndk)
 
     def test_omit_apex(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
-                Arch('arm'), 9, False, False))
+        f = self.filter
+        v = self.version
+        v_apex = copy(v)
+        v_apex.tags = Tags.from_strs(['apex'])
+        v_systemapi = copy(v)
+        v_systemapi.tags = Tags.from_strs(['systemapi'])
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, True))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
-                Arch('arm'), 9, False, True))
+        self.assertOmit(f, v_apex)
+
+        f.apex = True
+        self.assertInclude(f, v)
+        self.assertInclude(f, v_apex)
+        self.assertOmit(f, v_systemapi)
 
     def test_omit_systemapi(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
-                                   []), Arch('arm'), 9, False, False))
+        f = self.filter
+        v = self.version
+        v_apex = copy(v)
+        v_apex.tags = Tags.from_strs(['apex'])
+        v_systemapi = copy(v)
+        v_systemapi.tags = Tags.from_strs(['systemapi'])
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, True))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
-                                   []), Arch('arm'), 9, False, True))
+        self.assertOmit(f, v_systemapi)
+
+        f.systemapi = True
+        self.assertInclude(f, v)
+        self.assertInclude(f, v_systemapi)
+        self.assertOmit(f, v_apex)
 
     def test_omit_arch(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
-                Arch('arm'), 9, False, False))
+        f_arm = self.filter
+        v_none = self.version
+        self.assertInclude(f_arm, v_none)
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
-                Arch('arm'), 9, False, False))
+        v_arm = copy(v_none)
+        v_arm.tags = Tags.from_strs(['arm'])
+        self.assertInclude(f_arm, v_arm)
+
+        v_x86 = copy(v_none)
+        v_x86.tags = Tags.from_strs(['x86'])
+        self.assertOmit(f_arm, v_x86)
 
     def test_omit_api(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None,
-                                   Tags.from_strs(['introduced=9']), []),
-                Arch('arm'), 9, False, False))
+        f_api9 = self.filter
+        v_none = self.version
+        self.assertInclude(f_api9, v_none)
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None,
-                                   Tags.from_strs(['introduced=14']), []),
-                Arch('arm'), 9, False, False))
+        v_api9 = copy(v_none)
+        v_api9.tags = Tags.from_strs(['introduced=9'])
+        self.assertInclude(f_api9, v_api9)
+
+        v_api14 = copy(v_none)
+        v_api14.tags = Tags.from_strs(['introduced=14'])
+        self.assertOmit(f_api9, v_api14)
 
 
 class OmitSymbolTest(unittest.TestCase):
-    def test_omit_llndk(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
-                Arch('arm'), 9, False, False))
+    def setUp(self) -> None:
+        self.filter = Filter(arch = Arch('arm'), api = 9)
 
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, True, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
-                Arch('arm'), 9, True, False))
+    def assertOmit(self, f: Filter, s: Symbol) -> None:
+        self.assertTrue(f.should_omit_symbol(s))
+
+    def assertInclude(self, f: Filter, s: Symbol) -> None:
+        self.assertFalse(f.should_omit_symbol(s))
+
+    def test_omit_llndk(self) -> None:
+        f_none = self.filter
+        f_llndk = copy(f_none)
+        f_llndk.llndk = True
+
+        s_none = Symbol('foo', Tags())
+        s_llndk = Symbol('foo', Tags.from_strs(['llndk']))
+
+        self.assertOmit(f_none, s_llndk)
+        self.assertInclude(f_llndk, s_none)
+        self.assertInclude(f_llndk, s_llndk)
 
     def test_omit_apex(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
-                Arch('arm'), 9, False, False))
+        f_none = self.filter
+        f_apex = copy(f_none)
+        f_apex.apex = True
 
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, True))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
-                Arch('arm'), 9, False, True))
+        s_none = Symbol('foo', Tags())
+        s_apex = Symbol('foo', Tags.from_strs(['apex']))
+        s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+
+        self.assertOmit(f_none, s_apex)
+        self.assertInclude(f_apex, s_none)
+        self.assertInclude(f_apex, s_apex)
+        self.assertOmit(f_apex, s_systemapi)
 
     def test_omit_systemapi(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
-                Arch('arm'), 9, False, False))
+        f_none = self.filter
+        f_systemapi = copy(f_none)
+        f_systemapi.systemapi = True
 
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, True))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
-                Arch('arm'), 9, False, True))
+        s_none = Symbol('foo', Tags())
+        s_apex = Symbol('foo', Tags.from_strs(['apex']))
+        s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+
+        self.assertOmit(f_none, s_systemapi)
+        self.assertInclude(f_systemapi, s_none)
+        self.assertInclude(f_systemapi, s_systemapi)
+        self.assertOmit(f_systemapi, s_apex)
+
+    def test_omit_apex_and_systemapi(self) -> None:
+        f = self.filter
+        f.systemapi = True
+        f.apex = True
+
+        s_none = Symbol('foo', Tags())
+        s_apex = Symbol('foo', Tags.from_strs(['apex']))
+        s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+        self.assertInclude(f, s_none)
+        self.assertInclude(f, s_apex)
+        self.assertInclude(f, s_systemapi)
 
     def test_omit_arch(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
-                9, False, False))
+        f_arm = self.filter
+        s_none = Symbol('foo', Tags())
+        s_arm = Symbol('foo', Tags.from_strs(['arm']))
+        s_x86 = Symbol('foo', Tags.from_strs(['x86']))
 
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
-                9, False, False))
+        self.assertInclude(f_arm, s_none)
+        self.assertInclude(f_arm, s_arm)
+        self.assertOmit(f_arm, s_x86)
 
     def test_omit_api(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
-                Arch('arm'), 9, False, False))
+        f_api9 = self.filter
+        s_none = Symbol('foo', Tags())
+        s_api9 = Symbol('foo', Tags.from_strs(['introduced=9']))
+        s_api14 = Symbol('foo', Tags.from_strs(['introduced=14']))
 
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
-                Arch('arm'), 9, False, False))
+        self.assertInclude(f_api9, s_none)
+        self.assertInclude(f_api9, s_api9)
+        self.assertOmit(f_api9, s_api14)
 
 
 class SymbolFileParseTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = Filter(arch = Arch('arm'), api = 16)
+
     def test_next_line(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo
@@ -382,8 +393,7 @@
             # baz
             qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         self.assertIsNone(parser.current_line)
 
         self.assertEqual('foo', parser.next_line().strip())
@@ -409,8 +419,7 @@
             VERSION_2 {
             } VERSION_1; # asdf
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
         parser.next_line()
         version = parser.parse_version()
@@ -419,8 +428,8 @@
         self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
 
         expected_symbols = [
-            symbolfile.Symbol('baz', Tags()),
-            symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
+            Symbol('baz', Tags()),
+            Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -434,8 +443,7 @@
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -446,8 +454,7 @@
                 foo:
             }
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -457,8 +464,7 @@
             foo;
             bar; # baz qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
         parser.next_line()
         symbol = parser.parse_symbol()
@@ -476,8 +482,7 @@
                 *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -489,8 +494,7 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         version = parser.parse_version()
         self.assertEqual([], version.symbols)
@@ -501,8 +505,7 @@
                 foo
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -510,8 +513,7 @@
     def test_parse_fails_invalid_input(self) -> None:
         with self.assertRaises(symbolfile.ParseError):
             input_file = io.StringIO('foo')
-            parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
-                                                 16, False, False)
+            parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
             parser.parse()
 
     def test_parse(self) -> None:
@@ -532,19 +534,18 @@
                     qwerty;
             } VERSION_1;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         versions = parser.parse()
 
         expected = [
             symbolfile.Version('VERSION_1', None, Tags(), [
-                symbolfile.Symbol('foo', Tags()),
-                symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
+                Symbol('foo', Tags()),
+                Symbol('bar', Tags.from_strs(['baz'])),
             ]),
             symbolfile.Version(
                 'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
-                    symbolfile.Symbol('woodly', Tags()),
-                    symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
+                    Symbol('woodly', Tags()),
+                    Symbol('doodly', Tags.from_strs(['asdf'])),
                 ]),
         ]
 
@@ -559,8 +560,9 @@
                 qux; # apex
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, True)
+        f = copy(self.filter)
+        f.llndk = True
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
 
         parser.next_line()
         version = parser.parse_version()
@@ -568,10 +570,10 @@
         self.assertIsNone(version.base)
 
         expected_symbols = [
-            symbolfile.Symbol('foo', Tags()),
-            symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
-            symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
-            symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
+            Symbol('foo', Tags()),
+            Symbol('bar', Tags.from_strs(['llndk'])),
+            Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
+            Symbol('qux', Tags.from_strs(['apex'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
diff --git a/cc/testing.go b/cc/testing.go
index 077fcda..6b858d5 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -534,7 +534,8 @@
 	android.PrepareForTestWithAndroidBuildComponents,
 	android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+		ctx.RegisterModuleType("cc_fuzz", LibFuzzFactory)
+		ctx.RegisterModuleType("cc_afl_fuzz", AFLFuzzFactory)
 		ctx.RegisterModuleType("cc_test", TestFactory)
 		ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
 		ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
@@ -648,7 +649,8 @@
 func CreateTestContext(config android.Config) *android.TestContext {
 	ctx := android.NewTestArchContext(config)
 	genrule.RegisterGenruleBuildComponents(ctx)
-	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+	ctx.RegisterModuleType("cc_fuzz", LibFuzzFactory)
+	ctx.RegisterModuleType("cc_afl_fuzz", AFLFuzzFactory)
 	ctx.RegisterModuleType("cc_test", TestFactory)
 	ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 8a3d6e0..c583a49 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -552,7 +552,7 @@
 		excludes = append(excludes, getTemporaryExcludes()...)
 
 		symlinkForestDeps := bp2build.PlantSymlinkForest(
-			topDir, workspaceRoot, generatedRoot, ".", excludes)
+			configuration, topDir, workspaceRoot, generatedRoot, ".", excludes)
 
 		ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
@@ -566,7 +566,9 @@
 	// 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
 	// 1:1 mapping for each module.
-	metrics.Print()
+	if configuration.IsEnvTrue("BP2BUILD_VERBOSE") {
+		metrics.Print()
+	}
 	writeBp2BuildMetrics(&metrics, configuration, eventHandler)
 }
 
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index d63ded5..983dbf0 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -32,7 +32,8 @@
 		panic(err)
 	}
 
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), bp2build.QueryView)
+	filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(),
+		bp2build.QueryView)
 	for _, f := range filesToWrite {
 		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index de139c4..d8011d6 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -394,10 +394,14 @@
 	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
 		var compilerFilter string
 		if systemServerJars.ContainsJar(module.Name) {
-			// Jars of system server, use the product option if it is set, speed otherwise.
 			if global.SystemServerCompilerFilter != "" {
+				// Use the product option if it is set.
 				compilerFilter = global.SystemServerCompilerFilter
+			} else if profile != nil {
+				// Use "speed-profile" for system server jars that have a profile.
+				compilerFilter = "speed-profile"
 			} else {
+				// Use "speed" for system server jars that do not have a profile.
 				compilerFilter = "speed"
 			}
 		} else if contains(global.SpeedApps, module.Name) || contains(global.SystemServerApps, module.Name) {
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 700cdf0..1a87b30 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -18,6 +18,7 @@
 
 import (
 	"encoding/json"
+	"fmt"
 	"sort"
 	"strings"
 
@@ -26,12 +27,13 @@
 	"android/soong/android"
 )
 
-type Lang string
+type FuzzType string
 
 const (
-	Cc   Lang = ""
-	Rust Lang = "rust"
-	Java Lang = "java"
+	Cc   FuzzType = ""
+	Rust FuzzType = "rust"
+	Java FuzzType = "java"
+	AFL  FuzzType = "AFL"
 )
 
 var BoolDefault = proptools.BoolDefault
@@ -46,6 +48,7 @@
 	Packages                android.Paths
 	FuzzTargets             map[string]bool
 	SharedLibInstallStrings []string
+	FuzzType                FuzzType
 }
 
 type FileToZip struct {
@@ -59,9 +62,65 @@
 	Dir          string
 }
 
+type PrivilegedLevel string
+
+const (
+	// Environment with the most minimal permissions.
+	Constrained PrivilegedLevel = "Constrained"
+	// Typical execution environment running unprivileged code.
+	Unprivileged = "Unprivileged"
+	// May have access to elevated permissions.
+	Privileged = "Privileged"
+	// Trusted computing base.
+	Tcb = "TCB"
+	// Bootloader chain.
+	Bootloader = "Bootloader"
+	// Tusted execution environment.
+	Tee = "Tee"
+	// Secure enclave.
+	Se = "Se"
+	// Other.
+	Other = "Other"
+)
+
+func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
+	var config = fuzzModule.FuzzProperties.Fuzz_config
+	if config != nil {
+		var level = PrivilegedLevel(config.Privilege_level)
+		if level != "" {
+			switch level {
+			case Constrained, Unprivileged, Privileged, Tcb, Bootloader, Tee, Se, Other:
+				return true
+			}
+			panic(fmt.Errorf("Invalid privileged level in fuzz config in %s", moduleName))
+		}
+		return true
+	} else {
+		return false
+	}
+}
+
 type FuzzConfig struct {
 	// Email address of people to CC on bugs or contact about this fuzz target.
 	Cc []string `json:"cc,omitempty"`
+	// A brief description of what the fuzzed code does.
+	Description string `json:"description,omitempty"`
+	// Can this code be triggered remotely or only locally.
+	Remotely_accessible bool `json:"remotely_accessible,omitempty"`
+	// Is the fuzzed code host only, i.e. test frameworks or support utilities.
+	Host_only bool `json:"host_only,omitempty"`
+	// Can third party/untrusted apps supply data to fuzzed code.
+	Untrusted_data bool `json:"untrusted_data,omitempty"`
+	// Is the code being fuzzed in a privileged, constrained or any other
+	// context from:
+	// https://source.android.com/security/overview/updates-resources#context_types.
+	Privilege_level PrivilegedLevel `json:"privilege_level,omitempty"`
+	// Can the fuzzed code isolated or can be called by multiple users/processes.
+	Isolated bool `json:"users_isolation,omitempty"`
+	// When code was relaeased or will be released.
+	Production_date string `json:"production_date,omitempty"`
+	// Prevents critical service functionality like phone calls, bluetooth, etc.
+	Critical bool `json:"critical,omitempty"`
 	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
 	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
 	// Specify whether to enable continuous fuzzing on host. Defaults to true.
@@ -157,7 +216,7 @@
 	}
 
 	// Additional fuzz config.
-	if fuzzModule.Config != nil {
+	if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
 		files = append(files, FileToZip{fuzzModule.Config, ""})
 	}
 
@@ -208,7 +267,7 @@
 	return string(b)
 }
 
-func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang, pctx android.PackageContext) {
+func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, fuzzType FuzzType, pctx android.PackageContext) {
 	var archOsList []ArchOs
 	for archOs := range archDirs {
 		archOsList = append(archOsList, archOs)
@@ -221,12 +280,15 @@
 		hostOrTarget := archOs.HostOrTarget
 		builder := android.NewRuleBuilder(pctx, ctx)
 		zipFileName := "fuzz-" + hostOrTarget + "-" + arch + ".zip"
-		if lang == Rust {
+		if fuzzType == Rust {
 			zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
 		}
-		if lang == Java {
+		if fuzzType == Java {
 			zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
 		}
+		if fuzzType == AFL {
+			zipFileName = "fuzz-afl-" + hostOrTarget + "-" + arch + ".zip"
+		}
 		outputFile := android.PathForOutput(ctx, zipFileName)
 
 		s.Packages = append(s.Packages, outputFile)
@@ -237,7 +299,6 @@
 			Flag("-L 0") // No need to try and re-compress the zipfiles.
 
 		for _, fileToZip := range filesToZip {
-
 			if fileToZip.DestinationPathPrefix != "" {
 				command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
 			} else {
@@ -256,6 +317,7 @@
 	for target, _ := range s.FuzzTargets {
 		fuzzTargets = append(fuzzTargets, target)
 	}
+
 	sort.Strings(fuzzTargets)
 	ctx.Strict(targets, strings.Join(fuzzTargets, " "))
 }
diff --git a/java/aar.go b/java/aar.go
index 00ff7e7..cf84309 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -678,6 +678,10 @@
 	return a.SdkVersion(ctx)
 }
 
+func (a *AARImport) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, "")
+}
+
 func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return a.SdkVersion(ctx)
 }
diff --git a/java/android_manifest.go b/java/android_manifest.go
index a297b2c..c61823d 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -136,6 +136,11 @@
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
 
+		replaceMaxSdkVersionPlaceholder, err := params.SdkContext.ReplaceMaxSdkVersionPlaceholder(ctx).EffectiveVersion(ctx)
+		if err != nil {
+			ctx.ModuleErrorf("invalid ReplaceMaxSdkVersionPlaceholder: %s", err)
+		}
+
 		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
 			minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
 			deps = append(deps, ApiFingerprintPath(ctx))
@@ -145,6 +150,7 @@
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
 		args = append(args, "--minSdkVersion ", minSdkVersion)
+		args = append(args, "--replaceMaxSdkVersionPlaceholder ", strconv.Itoa(replaceMaxSdkVersionPlaceholder.FinalOrFutureInt()))
 		args = append(args, "--raise-min-sdk-version")
 	}
 
diff --git a/java/base.go b/java/base.go
index c399c40..94daf37 100644
--- a/java/base.go
+++ b/java/base.go
@@ -204,6 +204,10 @@
 	// Defaults to empty string "". See sdk_version for possible values.
 	Max_sdk_version *string
 
+	// if not blank, set the maxSdkVersion properties of permission and uses-permission tags.
+	// Defaults to empty string "". See sdk_version for possible values.
+	Replace_max_sdk_version_placeholder *string
+
 	// if not blank, set the targetSdkVersion in the AndroidManifest.xml.
 	// Defaults to sdk_version if not set. See sdk_version for possible values.
 	Target_sdk_version *string
@@ -649,6 +653,11 @@
 	return android.SdkSpecFrom(ctx, maxSdkVersion)
 }
 
+func (j *Module) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	replaceMaxSdkVersionPlaceholder := proptools.StringDefault(j.deviceProperties.Replace_max_sdk_version_placeholder, "")
+	return android.SdkSpecFrom(ctx, replaceMaxSdkVersionPlaceholder)
+}
+
 func (j *Module) MinSdkVersionString() string {
 	return j.minSdkVersion.Raw
 }
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 0591012..f08b64b 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -32,12 +32,7 @@
 func init() {
 	registerBootclasspathFragmentBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&bootclasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "bootclasspath_fragments",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(BootclasspathFragmentSdkMemberType)
 }
 
 func registerBootclasspathFragmentBuildComponents(ctx android.RegistrationContext) {
@@ -46,6 +41,15 @@
 	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
 }
diff --git a/java/dexpreopt.go_v1 b/java/dexpreopt.go_v1
deleted file mode 100644
index 0adaf99..0000000
--- a/java/dexpreopt.go_v1
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright 2018 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-import (
-	"path/filepath"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/dexpreopt"
-)
-
-type DexpreopterInterface interface {
-	IsInstallable() bool // Structs that embed dexpreopter must implement this.
-	dexpreoptDisabled(ctx android.BaseModuleContext) bool
-	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
-	AndroidMkEntriesForApex() []android.AndroidMkEntries
-}
-
-type dexpreopterInstall struct {
-	// A unique name to distinguish an output from others for the same java library module. Usually in
-	// the form of `<arch>-<encoded-path>.odex/vdex/art`.
-	name string
-
-	// The name of the input java module.
-	moduleName string
-
-	// The path to the dexpreopt output on host.
-	outputPathOnHost android.Path
-
-	// The directory on the device for the output to install to.
-	installDirOnDevice android.InstallPath
-
-	// The basename (the last segment of the path) for the output to install as.
-	installFileOnDevice string
-}
-
-// The full module name of the output in the makefile.
-func (install *dexpreopterInstall) FullModuleName() string {
-	return install.moduleName + install.SubModuleName()
-}
-
-// The sub-module name of the output in the makefile (the name excluding the java module name).
-func (install *dexpreopterInstall) SubModuleName() string {
-	return "-dexpreopt-" + install.name
-}
-
-// Returns Make entries for installing the file.
-//
-// This function uses a value receiver rather than a pointer receiver to ensure that the object is
-// safe to use in `android.AndroidMkExtraEntriesFunc`.
-func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
-		Class:      "ETC",
-		SubName:    install.SubModuleName(),
-		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
-				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
-			},
-		},
-	}
-}
-
-type dexpreopter struct {
-	dexpreoptProperties DexpreoptProperties
-
-	installPath         android.InstallPath
-	uncompressedDex     bool
-	isSDKLibrary        bool
-	isApp               bool
-	isTest              bool
-	isPresignedPrebuilt bool
-	preventInstall      bool
-
-	manifestFile        android.Path
-	statusFile          android.WritablePath
-	enforceUsesLibs     bool
-	classLoaderContexts dexpreopt.ClassLoaderContextMap
-
-	// See the `dexpreopt` function for details.
-	builtInstalled        string
-	builtInstalledForApex []dexpreopterInstall
-
-	// The config is used for two purposes:
-	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
-	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
-	//   Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
-	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
-	//   dexpreopt another partition).
-	configPath android.WritablePath
-}
-
-type DexpreoptProperties struct {
-	Dex_preopt struct {
-		// If false, prevent dexpreopting.  Defaults to true.
-		Enabled *bool
-
-		// If true, generate an app image (.art file) for this module.
-		App_image *bool
-
-		// If true, use a checked-in profile to guide optimization.  Defaults to false unless
-		// a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
-		// that matches the name of this module, in which case it is defaulted to true.
-		Profile_guided *bool
-
-		// If set, provides the path to profile relative to the Android.bp file.  If not set,
-		// defaults to searching for a file that matches the name of this module in the default
-		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
-		Profile *string `android:"path"`
-	}
-}
-
-func init() {
-	dexpreopt.DexpreoptRunningInSoong = true
-}
-
-func isApexVariant(ctx android.BaseModuleContext) bool {
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	return !apexInfo.IsForPlatform()
-}
-
-func forPrebuiltApex(ctx android.BaseModuleContext) bool {
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	return apexInfo.ForPrebuiltApex
-}
-
-func moduleName(ctx android.BaseModuleContext) string {
-	// Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
-	// expected by dexpreopter.
-	return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
-}
-
-func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
-	if !ctx.Device() {
-		return true
-	}
-
-	if d.isTest {
-		return true
-	}
-
-	if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
-		return true
-	}
-
-	// If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
-	// dexpreopted.
-	if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
-		return true
-	}
-
-	if !android.IsModulePreferred(ctx.Module()) {
-		return true
-	}
-
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	if global.DisablePreopt {
-		return true
-	}
-
-	if inList(moduleName(ctx), global.DisablePreoptModules) {
-		return true
-	}
-
-	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
-	if isApexVariant(ctx) {
-		// Don't preopt APEX variant module unless the module is an APEX system server jar and we are
-		// building the entire system image.
-		if !isApexSystemServerJar || ctx.Config().UnbundledBuild() {
-			return true
-		}
-	} else {
-		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
-		if isApexSystemServerJar {
-			return true
-		}
-	}
-
-	// TODO: contains no java code
-
-	return false
-}
-
-func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
-	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
-		return
-	}
-	dexpreopt.RegisterToolDeps(ctx)
-}
-
-func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
-	return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
-}
-
-// Returns the install path of the dex jar of a module.
-//
-// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
-// than the `name` in the path `/apex/<name>` as suggested in its comment.
-//
-// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
-// system server jar, which is fine because we currently only preopt system server jars for APEXes.
-func (d *dexpreopter) getInstallPath(
-	ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
-	global := dexpreopt.GetGlobalConfig(ctx)
-	if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) {
-		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx))
-		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
-	}
-	if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
-		filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
-		ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
-	}
-	return defaultInstallPath
-}
-
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
-	// the dexpreopter struct hasn't been fully initialized before we're called,
-	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
-	// disabled, even if installable is true.
-	if d.installPath.Base() == "." {
-		return
-	}
-
-	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
-
-	providesUsesLib := moduleName(ctx)
-	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
-		name := ulib.ProvidesUsesLib()
-		if name != nil {
-			providesUsesLib = *name
-		}
-	}
-
-	// If it is test, make config files regardless of its dexpreopt setting.
-	// The config files are required for apps defined in make which depend on the lib.
-	if d.isTest && d.dexpreoptDisabled(ctx) {
-		return
-	}
-
-	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
-
-	bootImage := defaultBootImageConfig(ctx)
-	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
-
-	targets := ctx.MultiTargets()
-	if len(targets) == 0 {
-		// assume this is a java library, dexpreopt for all arches for now
-		for _, target := range ctx.Config().Targets[android.Android] {
-			if target.NativeBridge == android.NativeBridgeDisabled {
-				targets = append(targets, target)
-			}
-		}
-		if isSystemServerJar && !d.isSDKLibrary {
-			// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
-			targets = targets[:1]
-		}
-	}
-
-	var archs []android.ArchType
-	var images android.Paths
-	var imagesDeps []android.OutputPaths
-	for _, target := range targets {
-		archs = append(archs, target.Arch.ArchType)
-		variant := bootImage.getVariant(target)
-		images = append(images, variant.imagePathOnHost)
-		imagesDeps = append(imagesDeps, variant.imagesDeps)
-	}
-	// The image locations for all Android variants are identical.
-	hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
-
-	var profileClassListing android.OptionalPath
-	var profileBootListing android.OptionalPath
-	profileIsTextListing := false
-	if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
-		// If dex_preopt.profile_guided is not set, default it based on the existence of the
-		// dexprepot.profile option or the profile class listing.
-		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
-			profileClassListing = android.OptionalPathForPath(
-				android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
-			profileBootListing = android.ExistentPathForSource(ctx,
-				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
-			profileIsTextListing = true
-		} else if global.ProfileDir != "" {
-			profileClassListing = android.ExistentPathForSource(ctx,
-				global.ProfileDir, moduleName(ctx)+".prof")
-		}
-	}
-
-	// Full dexpreopt config, used to create dexpreopt build rules.
-	dexpreoptConfig := &dexpreopt.ModuleConfig{
-		Name:            moduleName(ctx),
-		DexLocation:     dexLocation,
-		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
-		DexPath:         dexJarFile,
-		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
-		UncompressedDex: d.uncompressedDex,
-		HasApkLibraries: false,
-		PreoptFlags:     nil,
-
-		ProfileClassListing:  profileClassListing,
-		ProfileIsTextListing: profileIsTextListing,
-		ProfileBootListing:   profileBootListing,
-
-		EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
-		EnforceUsesLibraries:           d.enforceUsesLibs,
-		ProvidesUsesLibrary:            providesUsesLib,
-		ClassLoaderContexts:            d.classLoaderContexts,
-
-		Archs:                           archs,
-		DexPreoptImagesDeps:             imagesDeps,
-		DexPreoptImageLocationsOnHost:   hostImageLocations,
-		DexPreoptImageLocationsOnDevice: deviceImageLocations,
-
-		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
-		PreoptBootClassPathDexLocations: dexLocations,
-
-		PreoptExtractedApk: false,
-
-		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
-		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
-
-		PresignedPrebuilt: d.isPresignedPrebuilt,
-	}
-
-	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
-	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
-
-	if d.dexpreoptDisabled(ctx) {
-		return
-	}
-
-	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
-
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
-	if err != nil {
-		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
-		return
-	}
-
-	dexpreoptRule.Build("dexpreopt", "dexpreopt")
-
-	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
-
-	for _, install := range dexpreoptRule.Installs() {
-		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
-		installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
-		installBase := filepath.Base(install.To)
-		arch := filepath.Base(installDir)
-		installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
-
-		if isApexSystemServerJar {
-			// APEX variants of java libraries are hidden from Make, so their dexpreopt
-			// outputs need special handling. Currently, for APEX variants of java
-			// libraries, only those in the system server classpath are handled here.
-			// Preopting of boot classpath jars in the ART APEX are handled in
-			// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
-			// The installs will be handled by Make as sub-modules of the java library.
-			d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
-				name:                arch + "-" + installBase,
-				moduleName:          moduleName(ctx),
-				outputPathOnHost:    install.From,
-				installDirOnDevice:  installPath,
-				installFileOnDevice: installBase,
-			})
-		} else if !d.preventInstall {
-			ctx.InstallFile(installPath, installBase, install.From)
-		}
-	}
-
-	if !isApexSystemServerJar {
-		d.builtInstalled = dexpreoptRule.Installs().String()
-	}
-}
-
-func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
-	return d.builtInstalledForApex
-}
-
-func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
-	var entries []android.AndroidMkEntries
-	for _, install := range d.builtInstalledForApex {
-		entries = append(entries, install.ToMakeEntries())
-	}
-	return entries
-}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 7c4da3e..b4cd07a 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -785,24 +785,26 @@
 	}
 
 	defaultProfile := "frameworks/base/config/boot-image-profile.txt"
+	extraProfile := "frameworks/base/config/boot-image-profile-extra.txt"
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 
-	var bootImageProfile android.Path
-	if len(global.BootImageProfiles) > 1 {
-		combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
-		rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile)
-		bootImageProfile = combinedBootImageProfile
-	} else if len(global.BootImageProfiles) == 1 {
-		bootImageProfile = global.BootImageProfiles[0]
+	var profiles android.Paths
+	if len(global.BootImageProfiles) > 0 {
+		profiles = append(profiles, global.BootImageProfiles...)
 	} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
-		bootImageProfile = path.Path()
+		profiles = append(profiles, path.Path())
 	} else {
 		// No profile (not even a default one, which is the case on some branches
 		// like master-art-host that don't have frameworks/base).
 		// Return nil and continue without profile.
 		return nil
 	}
+	if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() {
+		profiles = append(profiles, path.Path())
+	}
+	bootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
+	rule.Command().Text("cat").Inputs(profiles).Text(">").Output(bootImageProfile)
 
 	profile := image.dir.Join(ctx, "boot.prof")
 
diff --git a/java/dexpreopt_bootjars.go_v1 b/java/dexpreopt_bootjars.go_v1
deleted file mode 100644
index 07a357b..0000000
--- a/java/dexpreopt_bootjars.go_v1
+++ /dev/null
@@ -1,952 +0,0 @@
-// Copyright 2019 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-import (
-	"path/filepath"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/dexpreopt"
-
-	"github.com/google/blueprint/proptools"
-)
-
-// =================================================================================================
-// WIP - see http://b/177892522 for details
-//
-// The build support for boot images is currently being migrated away from singleton to modules so
-// the documentation may not be strictly accurate. Rather than update the documentation at every
-// step which will create a lot of churn the changes that have been made will be listed here and the
-// documentation will be updated once it is closer to the final result.
-//
-// Changes:
-// 1) dex_bootjars is now a singleton module and not a plain singleton.
-// 2) Boot images are now represented by the boot_image module type.
-// 3) The art boot image is called "art-boot-image", the framework boot image is called
-//    "framework-boot-image".
-// 4) They are defined in art/build/boot/Android.bp and frameworks/base/boot/Android.bp
-//    respectively.
-// 5) Each boot_image retrieves the appropriate boot image configuration from the map returned by
-//    genBootImageConfigs() using the image_name specified in the boot_image module.
-// =================================================================================================
-
-// This comment describes:
-//   1. ART boot images in general (their types, structure, file layout, etc.)
-//   2. build system support for boot images
-//
-// 1. ART boot images
-// ------------------
-//
-// A boot image in ART is a set of files that contain AOT-compiled native code and a heap snapshot
-// of AOT-initialized classes for the bootclasspath Java libraries. A boot image is compiled from a
-// set of DEX jars by the dex2oat compiler. A boot image is used for two purposes: 1) it is
-// installed on device and loaded at runtime, and 2) other Java libraries and apps are compiled
-// against it (compilation may take place either on host, known as "dexpreopt", or on device, known
-// as "dexopt").
-//
-// A boot image is not a single file, but a collection of interrelated files. Each boot image has a
-// number of components that correspond to the Java libraries that constitute it. For each component
-// there are multiple files:
-//   - *.oat or *.odex file with native code (architecture-specific, one per instruction set)
-//   - *.art file with pre-initialized Java classes (architecture-specific, one per instruction set)
-//   - *.vdex file with verification metadata for the DEX bytecode (architecture independent)
-//
-// *.vdex files for the boot images do not contain the DEX bytecode itself, because the
-// bootclasspath DEX files are stored on disk in uncompressed and aligned form. Consequently a boot
-// image is not self-contained and cannot be used without its DEX files. To simplify the management
-// of boot image files, ART uses a certain naming scheme and associates the following metadata with
-// each boot image:
-//   - A stem, which is a symbolic name that is prepended to boot image file names.
-//   - A location (on-device path to the boot image files).
-//   - A list of boot image locations (on-device paths to dependency boot images).
-//   - A set of DEX locations (on-device paths to the DEX files, one location for one DEX file used
-//     to compile the boot image).
-//
-// There are two kinds of boot images:
-//   - primary boot images
-//   - boot image extensions
-//
-// 1.1. Primary boot images
-// ------------------------
-//
-// A primary boot image is compiled for a core subset of bootclasspath Java libraries. It does not
-// depend on any other images, and other boot images may depend on it.
-//
-// For example, assuming that the stem is "boot", the location is /apex/com.android.art/javalib/,
-// the set of core bootclasspath libraries is A B C, and the boot image is compiled for ARM targets
-// (32 and 64 bits), it will have three components with the following files:
-//   - /apex/com.android.art/javalib/{arm,arm64}/boot.{art,oat,vdex}
-//   - /apex/com.android.art/javalib/{arm,arm64}/boot-B.{art,oat,vdex}
-//   - /apex/com.android.art/javalib/{arm,arm64}/boot-C.{art,oat,vdex}
-//
-// The files of the first component are special: they do not have the component name appended after
-// the stem. This naming convention dates back to the times when the boot image was not split into
-// components, and there were just boot.oat and boot.art. The decision to split was motivated by
-// licensing reasons for one of the bootclasspath libraries.
-//
-// As of November 2020 the only primary boot image in Android is the image in the ART APEX
-// com.android.art. The primary ART boot image contains the Core libraries that are part of the ART
-// module. When the ART module gets updated, the primary boot image will be updated with it, and all
-// dependent images will get invalidated (the checksum of the primary image stored in dependent
-// images will not match), unless they are updated in sync with the ART module.
-//
-// 1.2. Boot image extensions
-// --------------------------
-//
-// A boot image extension is compiled for a subset of bootclasspath Java libraries (in particular,
-// this subset does not include the Core bootclasspath libraries that go into the primary boot
-// image). A boot image extension depends on the primary boot image and optionally some other boot
-// image extensions. Other images may depend on it. In other words, boot image extensions can form
-// acyclic dependency graphs.
-//
-// The motivation for boot image extensions comes from the Mainline project. Consider a situation
-// when the list of bootclasspath libraries is A B C, and both A and B are parts of the Android
-// platform, but C is part of an updatable APEX com.android.C. When the APEX is updated, the Java
-// code for C might have changed compared to the code that was used to compile the boot image.
-// Consequently, the whole boot image is obsolete and invalidated (even though the code for A and B
-// that does not depend on C is up to date). To avoid this, the original monolithic boot image is
-// split in two parts: the primary boot image that contains A B, and the boot image extension that
-// contains C and depends on the primary boot image (extends it).
-//
-// For example, assuming that the stem is "boot", the location is /system/framework, the set of
-// bootclasspath libraries is D E (where D is part of the platform and is located in
-// /system/framework, and E is part of a non-updatable APEX com.android.E and is located in
-// /apex/com.android.E/javalib), and the boot image is compiled for ARM targets (32 and 64 bits),
-// it will have two components with the following files:
-//   - /system/framework/{arm,arm64}/boot-D.{art,oat,vdex}
-//   - /system/framework/{arm,arm64}/boot-E.{art,oat,vdex}
-//
-// As of November 2020 the only boot image extension in Android is the Framework boot image
-// extension. It extends the primary ART boot image and contains Framework libraries and other
-// bootclasspath libraries from the platform and non-updatable APEXes that are not included in the
-// ART image. The Framework boot image extension is updated together with the platform. In the
-// future other boot image extensions may be added for some updatable modules.
-//
-//
-// 2. Build system support for boot images
-// ---------------------------------------
-//
-// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
-// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
-// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
-// core libraries as they are already part of the primary ART boot image.
-//
-// 2.1. Libraries that go in the boot images
-// -----------------------------------------
-//
-// The contents of each boot image are determined by the PRODUCT variables. The primary ART APEX
-// boot image contains libraries listed in the ART_APEX_JARS variable in the AOSP makefiles. The
-// Framework boot image extension contains libraries specified in the PRODUCT_BOOT_JARS and
-// PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries,
-// but more product-specific libraries can be added in the product makefiles.
-//
-// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is a
-// colon-separated pair <apex>:<library>, where <apex> is the variant name of a non-updatable APEX,
-// "platform" if the library is a part of the platform in the system partition, or "system_ext" if
-// it's in the system_ext partition.
-//
-// In these variables APEXes are identified by their "variant names", i.e. the names they get
-// mounted as in /apex on device. In Soong modules that is the name set in the "apex_name"
-// properties, which default to the "name" values. For example, many APEXes have both
-// com.android.xxx and com.google.android.xxx modules in Soong, but take the same place
-// /apex/com.android.xxx at runtime. In these cases the variant name is always com.android.xxx,
-// regardless which APEX goes into the product. See also android.ApexInfo.ApexVariationName and
-// apex.apexBundleProperties.Apex_name.
-//
-// A related variable PRODUCT_APEX_BOOT_JARS contains bootclasspath libraries that are in APEXes.
-// They are not included in the boot image. The only exception here are ART jars and core-icu4j.jar
-// that have been historically part of the boot image and are now in apexes; they are in boot images
-// and core-icu4j.jar is generally treated as being part of PRODUCT_BOOT_JARS.
-//
-// One exception to the above rules are "coverage" builds (a special build flavor which requires
-// setting environment variable EMMA_INSTRUMENT_FRAMEWORK=true). In coverage builds the Java code in
-// boot image libraries is instrumented, which means that the instrumentation library (jacocoagent)
-// needs to be added to the list of bootclasspath DEX jars.
-//
-// In general, there is a requirement that the source code for a boot image library must be
-// available at build time (e.g. it cannot be a stub that has a separate implementation library).
-//
-// 2.2. Static configs
-// -------------------
-//
-// Because boot images are used to dexpreopt other Java modules, the paths to boot image files must
-// be known by the time dexpreopt build rules for the dependent modules are generated. Boot image
-// configs are constructed very early during the build, before build rule generation. The configs
-// provide predefined paths to boot image files (these paths depend only on static build
-// configuration, such as PRODUCT variables, and use hard-coded directory names).
-//
-// 2.3. Singleton
-// --------------
-//
-// Build rules for the boot images are generated with a Soong singleton. Because a singleton has no
-// dependencies on other modules, it has to find the modules for the DEX jars using VisitAllModules.
-// Soong loops through all modules and compares each module against a list of bootclasspath library
-// names. Then it generates build rules that copy DEX jars from their intermediate module-specific
-// locations to the hard-coded locations predefined in the boot image configs.
-//
-// It would be possible to use a module with proper dependencies instead, but that would require
-// changes in the way Soong generates variables for Make: a singleton can use one MakeVars() method
-// that writes variables to out/soong/make_vars-*.mk, which is included early by the main makefile,
-// but module(s) would have to use out/soong/Android-*.mk which has a group of LOCAL_* variables
-// for each module, and is included later.
-//
-// 2.4. Install rules
-// ------------------
-//
-// The primary boot image and the Framework extension are installed in different ways. The primary
-// boot image is part of the ART APEX: it is copied into the APEX intermediate files, packaged
-// together with other APEX contents, extracted and mounted on device. The Framework boot image
-// extension is installed by the rules defined in makefiles (make/core/dex_preopt_libart.mk). Soong
-// writes out a few DEXPREOPT_IMAGE_* variables for Make; these variables contain boot image names,
-// paths and so on.
-//
-
-var artApexNames = []string{
-	"com.android.art",
-	"com.android.art.debug",
-	"com.android.art.testing",
-	"com.google.android.art",
-	"com.google.android.art.debug",
-	"com.google.android.art.testing",
-}
-
-func init() {
-	RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
-}
-
-// Target-independent description of a boot image.
-type bootImageConfig struct {
-	// If this image is an extension, the image that it extends.
-	extends *bootImageConfig
-
-	// Image name (used in directory names and ninja rule names).
-	name string
-
-	// Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}.
-	stem string
-
-	// Output directory for the image files.
-	dir android.OutputPath
-
-	// Output directory for the image files with debug symbols.
-	symbolsDir android.OutputPath
-
-	// Subdirectory where the image files are installed.
-	installDirOnHost string
-
-	// Subdirectory where the image files on device are installed.
-	installDirOnDevice string
-
-	// Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
-	// needed.
-	profileInstallPathInApex string
-
-	// A list of (location, jar) pairs for the Java modules in this image.
-	modules android.ConfiguredJarList
-
-	// File paths to jars.
-	dexPaths     android.WritablePaths // for this image
-	dexPathsDeps android.WritablePaths // for the dependency images and in this image
-
-	// Map from module name (without prebuilt_ prefix) to the predefined build path.
-	dexPathsByModule map[string]android.WritablePath
-
-	// File path to a zip archive with all image files (or nil, if not needed).
-	zip android.WritablePath
-
-	// Rules which should be used in make to install the outputs.
-	profileInstalls android.RuleBuilderInstalls
-
-	// Path to the license metadata file for the module that built the profile.
-	profileLicenseMetadataFile android.OptionalPath
-
-	// Path to the image profile file on host (or empty, if profile is not generated).
-	profilePathOnHost android.Path
-
-	// Target-dependent fields.
-	variants []*bootImageVariant
-
-	// Path of the preloaded classes file.
-	preloadedClassesFile string
-}
-
-// Target-dependent description of a boot image.
-type bootImageVariant struct {
-	*bootImageConfig
-
-	// Target for which the image is generated.
-	target android.Target
-
-	// The "locations" of jars.
-	dexLocations     []string // for this image
-	dexLocationsDeps []string // for the dependency images and in this image
-
-	// Paths to image files.
-	imagePathOnHost   android.OutputPath // first image file path on host
-	imagePathOnDevice string             // first image file path on device
-
-	// All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
-	imagesDeps android.OutputPaths
-
-	// The path to the primary image variant's imagePathOnHost field, where primary image variant
-	// means the image variant that this extends.
-	//
-	// This is only set for a variant of an image that extends another image.
-	primaryImages android.OutputPath
-
-	// The paths to the primary image variant's imagesDeps field, where primary image variant
-	// means the image variant that this extends.
-	//
-	// This is only set for a variant of an image that extends another image.
-	primaryImagesDeps android.Paths
-
-	// Rules which should be used in make to install the outputs on host.
-	installs           android.RuleBuilderInstalls
-	vdexInstalls       android.RuleBuilderInstalls
-	unstrippedInstalls android.RuleBuilderInstalls
-
-	// Rules which should be used in make to install the outputs on device.
-	deviceInstalls android.RuleBuilderInstalls
-
-	// Path to the license metadata file for the module that built the image.
-	licenseMetadataFile android.OptionalPath
-}
-
-// Get target-specific boot image variant for the given boot image config and target.
-func (image bootImageConfig) getVariant(target android.Target) *bootImageVariant {
-	for _, variant := range image.variants {
-		if variant.target.Os == target.Os && variant.target.Arch.ArchType == target.Arch.ArchType {
-			return variant
-		}
-	}
-	return nil
-}
-
-// Return any (the first) variant which is for the device (as opposed to for the host).
-func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant {
-	for _, variant := range image.variants {
-		if variant.target.Os == android.Android {
-			return variant
-		}
-	}
-	return nil
-}
-
-// Return the name of a boot image module given a boot image config and a component (module) index.
-// A module name is a combination of the Java library name, and the boot image stem (that is stored
-// in the config).
-func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string {
-	// The first module of the primary boot image is special: its module name has only the stem, but
-	// not the library name. All other module names are of the form <stem>-<library name>
-	m := image.modules.Jar(idx)
-	name := image.stem
-	if idx != 0 || image.extends != nil {
-		name += "-" + android.ModuleStem(m)
-	}
-	return name
-}
-
-// Return the name of the first boot image module, or stem if the list of modules is empty.
-func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
-	if image.modules.Len() > 0 {
-		return image.moduleName(ctx, 0)
-	} else {
-		return image.stem
-	}
-}
-
-// Return filenames for the given boot image component, given the output directory and a list of
-// extensions.
-func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
-	ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts))
-	for i := 0; i < image.modules.Len(); i++ {
-		name := image.moduleName(ctx, i)
-		for _, ext := range exts {
-			ret = append(ret, dir.Join(ctx, name+ext))
-		}
-	}
-	return ret
-}
-
-// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
-func (image *bootImageConfig) apexVariants() []*bootImageVariant {
-	variants := []*bootImageVariant{}
-	for _, variant := range image.variants {
-		// We also generate boot images for host (for testing), but we don't need those in the apex.
-		// TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
-		if variant.target.Os == android.Android {
-			variants = append(variants, variant)
-		}
-	}
-	return variants
-}
-
-// Returns true if the boot image should be installed in the APEX.
-func (image *bootImageConfig) shouldInstallInApex() bool {
-	return strings.HasPrefix(image.installDirOnDevice, "apex/")
-}
-
-// Return boot image locations (as a list of symbolic paths).
-//
-// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
-// exist on the device. Typically it is /apex/com.android.art/javalib/boot.art and should be the
-// same for all supported architectures on the device. The concrete architecture specific files
-// actually end up in architecture-specific sub-directory such as arm, arm64, x86, or x86_64.
-//
-// For example a physical file /apex/com.android.art/javalib/x86/boot.art has "image location"
-// /apex/com.android.art/javalib/boot.art (which is not an actual file).
-//
-// For a primary boot image the list of locations has a single element.
-//
-// For a boot image extension the list of locations contains a location for all dependency images
-// (including the primary image) and the location of the extension itself. For example, for the
-// Framework boot image extension that depends on the primary ART boot image the list contains two
-// elements.
-//
-// The location is passed as an argument to the ART tools like dex2oat instead of the real path.
-// ART tools will then reconstruct the architecture-specific real path.
-//
-func (image *bootImageVariant) imageLocations() (imageLocationsOnHost []string, imageLocationsOnDevice []string) {
-	if image.extends != nil {
-		imageLocationsOnHost, imageLocationsOnDevice = image.extends.getVariant(image.target).imageLocations()
-	}
-	return append(imageLocationsOnHost, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType)),
-		append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType))
-}
-
-func dexpreoptBootJarsFactory() android.SingletonModule {
-	m := &dexpreoptBootJars{}
-	android.InitAndroidModule(m)
-	return m
-}
-
-func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
-}
-
-func SkipDexpreoptBootJars(ctx android.PathContext) bool {
-	return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
-}
-
-// Singleton module for generating boot image build rules.
-type dexpreoptBootJars struct {
-	android.SingletonModuleBase
-
-	// Default boot image config (currently always the Framework boot image extension). It should be
-	// noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
-	// but the switch is handled not here, but in the makefiles (triggered with
-	// DEXPREOPT_USE_ART_IMAGE=true).
-	defaultBootImage *bootImageConfig
-
-	// Build path to a config file that Soong writes for Make (to be used in makefiles that install
-	// the default boot image).
-	dexpreoptConfigForMake android.WritablePath
-}
-
-// Provide paths to boot images for use by modules that depend upon them.
-//
-// The build rules are created in GenerateSingletonBuildActions().
-func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Placeholder for now.
-}
-
-// Generate build rules for boot images.
-func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	if SkipDexpreoptBootJars(ctx) {
-		return
-	}
-	if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
-		// No module has enabled dexpreopting, so we assume there will be no boot image to make.
-		return
-	}
-
-	d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config")
-	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
-
-	global := dexpreopt.GetGlobalConfig(ctx)
-	if !shouldBuildBootImages(ctx.Config(), global) {
-		return
-	}
-
-	defaultImageConfig := defaultBootImageConfig(ctx)
-	d.defaultBootImage = defaultImageConfig
-}
-
-// shouldBuildBootImages determines whether boot images should be built.
-func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig) bool {
-	// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
-	// and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
-	// Note: this is technically incorrect. Compiled code contains stack checks which may depend
-	//       on ASAN settings.
-	if len(config.SanitizeDevice()) == 1 && config.SanitizeDevice()[0] == "address" && global.SanitizeLite {
-		return false
-	}
-	return true
-}
-
-// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
-// paths in the global config.
-func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
-	// Create the super set of module names.
-	names := []string{}
-	names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
-	names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
-	names = android.SortedUniqueStrings(names)
-	for _, name := range names {
-		src := srcBootDexJarsByModule[name]
-		dst := dstBootJarsByModule[name]
-
-		if src == nil {
-			// A dex boot jar should be provided by the source java module. It needs to be installable or
-			// have compile_dex=true - cf. assignments to java.Module.dexJarFile.
-			//
-			// However, the source java module may be either replaced or overridden (using prefer:true) by
-			// a prebuilt java module with the same name. In that case the dex boot jar needs to be
-			// provided by the corresponding prebuilt APEX module. That APEX is the one that refers
-			// through a exported_(boot|systemserver)classpath_fragments property to a
-			// prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt
-			// java module in the contents property. If that chain is broken then this dependency will
-			// fail.
-			if !ctx.Config().AllowMissingDependencies() {
-				ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name)
-			} else {
-				ctx.AddMissingDependencies([]string{name})
-			}
-		} else if dst == nil {
-			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
-		} else {
-			ctx.Build(pctx, android.BuildParams{
-				Rule:   android.Cp,
-				Input:  src,
-				Output: dst,
-			})
-		}
-	}
-}
-
-// buildBootImageVariantsForAndroidOs generates rules to build the boot image variants for the
-// android.Android OsType and returns a map from the architectures to the paths of the generated
-// boot image files.
-//
-// The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX.
-func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch {
-	return buildBootImageForOsType(ctx, image, profile, android.Android)
-}
-
-// buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the
-// config.BuildOS OsType, i.e. the type of OS on which the build is being running.
-//
-// The files need to be generated into their predefined location because they are used from there
-// both within Soong and outside, e.g. for ART based host side testing and also for use by some
-// cloud based tools. However, they are not needed by callers of this function and so the paths do
-// not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func.
-func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
-	buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
-}
-
-// buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType
-// boot image files are required for and it creates rules to build the boot image
-// files for all the required architectures for them.
-//
-// It returns a map from android.ArchType to the predefined paths of the boot image files.
-func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageFilesByArch {
-	filesByArch := bootImageFilesByArch{}
-	for _, variant := range image.variants {
-		if variant.target.Os == requiredOsType {
-			buildBootImageVariant(ctx, variant, profile)
-			filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
-		}
-	}
-
-	return filesByArch
-}
-
-// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files.
-//
-// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it
-// is a map from android.ArchType to the predefined locations.
-func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) {
-	if filesByArch == nil {
-		return
-	}
-
-	// Compute the list of files from all the architectures.
-	zipFiles := android.Paths{}
-	for _, archType := range android.ArchTypeList() {
-		zipFiles = append(zipFiles, filesByArch[archType]...)
-	}
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		BuiltTool("soong_zip").
-		FlagWithOutput("-o ", image.zip).
-		FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
-		FlagWithInputList("-f ", zipFiles, " -f ")
-
-	rule.Build("zip_"+image.name, "zip "+image.name+" image")
-}
-
-// Generate boot image build rules for a specific target.
-func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) {
-
-	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	arch := image.target.Arch.ArchType
-	os := image.target.Os.String() // We need to distinguish host-x86 and device-x86.
-	symbolsDir := image.symbolsDir.Join(ctx, os, image.installDirOnHost, arch.String())
-	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
-	outputDir := image.dir.Join(ctx, os, image.installDirOnHost, arch.String())
-	outputPath := outputDir.Join(ctx, image.stem+".oat")
-	oatLocation := dexpreopt.PathToLocation(outputPath, arch)
-	imagePath := outputPath.ReplaceExtension(ctx, "art")
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
-	rule.Command().Text("rm").Flag("-f").
-		Flag(symbolsDir.Join(ctx, "*.art").String()).
-		Flag(symbolsDir.Join(ctx, "*.oat").String()).
-		Flag(symbolsDir.Join(ctx, "*.invocation").String())
-	rule.Command().Text("rm").Flag("-f").
-		Flag(outputDir.Join(ctx, "*.art").String()).
-		Flag(outputDir.Join(ctx, "*.oat").String()).
-		Flag(outputDir.Join(ctx, "*.invocation").String())
-
-	cmd := rule.Command()
-
-	extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS")
-	if extraFlags == "" {
-		// Use ANDROID_LOG_TAGS to suppress most logging by default...
-		cmd.Text(`ANDROID_LOG_TAGS="*:e"`)
-	} else {
-		// ...unless the boot image is generated specifically for testing, then allow all logging.
-		cmd.Text(`ANDROID_LOG_TAGS="*:v"`)
-	}
-
-	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
-
-	cmd.Tool(globalSoong.Dex2oat).
-		Flag("--avoid-storing-invocation").
-		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
-		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
-		Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx)
-
-	if profile != nil {
-		cmd.FlagWithInput("--profile-file=", profile)
-	}
-
-	dirtyImageFile := "frameworks/base/config/dirty-image-objects"
-	dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
-	if dirtyImagePath.Valid() {
-		cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path())
-	}
-
-	if image.extends != nil {
-		// It is a boot image extension, so it needs the boot image it depends on (in this case the
-		// primary ART APEX image).
-		artImage := image.primaryImages
-		cmd.
-			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
-			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
-			// Add the path to the first file in the boot image with the arch specific directory removed,
-			// dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
-			// to the file cannot be passed to the command make sure to add the actual path as an Implicit
-			// dependency to ensure that it is built before the command runs.
-			FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage).
-			// Similarly, the dex2oat tool will automatically find the paths to other files in the base
-			// boot image so make sure to add them as implicit dependencies to ensure that they are built
-			// before this command is run.
-			Implicits(image.primaryImagesDeps)
-	} else {
-		// It is a primary image, so it needs a base address.
-		cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
-	}
-
-	// We always expect a preloaded classes file to be available. However, if we cannot find it, it's
-	// OK to not pass the flag to dex2oat.
-	preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
-	if preloadedClassesPath.Valid() {
-		cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
-	}
-
-	cmd.
-		FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
-		FlagForEachArg("--dex-location=", image.dexLocations).
-		Flag("--generate-debug-info").
-		Flag("--generate-build-id").
-		Flag("--image-format=lz4hc").
-		FlagWithArg("--oat-symbols=", symbolsFile.String()).
-		Flag("--strip").
-		FlagWithArg("--oat-file=", outputPath.String()).
-		FlagWithArg("--oat-location=", oatLocation).
-		FlagWithArg("--image=", imagePath.String()).
-		FlagWithArg("--instruction-set=", arch.String()).
-		FlagWithArg("--android-root=", global.EmptyDirectory).
-		FlagWithArg("--no-inline-from=", "core-oj.jar").
-		Flag("--force-determinism").
-		Flag("--abort-on-hard-verifier-error")
-
-	// Use the default variant/features for host builds.
-	// The map below contains only device CPU info (which might be x86 on some devices).
-	if image.target.Os == android.Android {
-		cmd.FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch])
-		cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
-	}
-
-	if global.BootFlags != "" {
-		cmd.Flag(global.BootFlags)
-	}
-
-	if extraFlags != "" {
-		cmd.Flag(extraFlags)
-	}
-
-	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
-
-	installDir := filepath.Join("/", image.installDirOnHost, arch.String())
-
-	var vdexInstalls android.RuleBuilderInstalls
-	var unstrippedInstalls android.RuleBuilderInstalls
-	var deviceInstalls android.RuleBuilderInstalls
-
-	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
-		cmd.ImplicitOutput(artOrOat)
-
-		// Install the .oat and .art files
-		rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base()))
-	}
-
-	for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") {
-		cmd.ImplicitOutput(vdex)
-
-		// Note that the vdex files are identical between architectures.
-		// Make rules will create symlinks to share them between architectures.
-		vdexInstalls = append(vdexInstalls,
-			android.RuleBuilderInstall{vdex, filepath.Join(installDir, vdex.Base())})
-	}
-
-	for _, unstrippedOat := range image.moduleFiles(ctx, symbolsDir, ".oat") {
-		cmd.ImplicitOutput(unstrippedOat)
-
-		// Install the unstripped oat files.  The Make rules will put these in $(TARGET_OUT_UNSTRIPPED)
-		unstrippedInstalls = append(unstrippedInstalls,
-			android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
-	}
-
-	if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
-		installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
-		for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
-			deviceInstalls = append(deviceInstalls,
-				android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
-		}
-	}
-
-	rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
-
-	// save output and installed files for makevars
-	image.installs = rule.Installs()
-	image.vdexInstalls = vdexInstalls
-	image.unstrippedInstalls = unstrippedInstalls
-	image.deviceInstalls = deviceInstalls
-	image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
-}
-
-const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
-It is likely that the boot classpath is inconsistent.
-Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
-
-func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
-	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	if global.DisableGenerateProfile {
-		return nil
-	}
-
-	defaultProfile := "frameworks/base/config/boot-image-profile.txt"
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	var bootImageProfile android.Path
-	if len(global.BootImageProfiles) > 1 {
-		combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
-		rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile)
-		bootImageProfile = combinedBootImageProfile
-	} else if len(global.BootImageProfiles) == 1 {
-		bootImageProfile = global.BootImageProfiles[0]
-	} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
-		bootImageProfile = path.Path()
-	} else {
-		// No profile (not even a default one, which is the case on some branches
-		// like master-art-host that don't have frameworks/base).
-		// Return nil and continue without profile.
-		return nil
-	}
-
-	profile := image.dir.Join(ctx, "boot.prof")
-
-	rule.Command().
-		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(globalSoong.Profman).
-		Flag("--output-profile-type=boot").
-		FlagWithInput("--create-profile-from=", bootImageProfile).
-		FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
-		FlagWithOutput("--reference-profile-file=", profile)
-
-	if image == defaultBootImageConfig(ctx) {
-		rule.Install(profile, "/system/etc/boot-image.prof")
-		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
-		image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
-	}
-
-	rule.Build("bootJarsProfile", "profile boot jars")
-
-	image.profilePathOnHost = profile
-
-	return profile
-}
-
-// bootFrameworkProfileRule generates the rule to create the boot framework profile and
-// returns a path to the generated file.
-func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
-	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
-		return nil
-	}
-
-	defaultProfile := "frameworks/base/config/boot-profile.txt"
-	bootFrameworkProfile := android.PathForSource(ctx, defaultProfile)
-
-	profile := image.dir.Join(ctx, "boot.bprof")
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		Text(`ANDROID_LOG_TAGS="*:e"`).
-		Tool(globalSoong.Profman).
-		Flag("--output-profile-type=bprof").
-		FlagWithInput("--create-profile-from=", bootFrameworkProfile).
-		FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
-		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
-		FlagWithOutput("--reference-profile-file=", profile)
-
-	rule.Install(profile, "/system/etc/boot-image.bprof")
-	rule.Build("bootFrameworkProfile", "profile boot framework jars")
-	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
-	image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
-
-	return profile
-}
-
-func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
-	var allPhonies android.Paths
-	for _, image := range image.variants {
-		arch := image.target.Arch.ArchType
-		suffix := arch.String()
-		// Host and target might both use x86 arch. We need to ensure the names are unique.
-		if image.target.Os.Class == android.Host {
-			suffix = "host-" + suffix
-		}
-		// Create a rule to call oatdump.
-		output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt")
-		rule := android.NewRuleBuilder(pctx, ctx)
-		imageLocationsOnHost, _ := image.imageLocations()
-		rule.Command().
-			BuiltTool("oatdump").
-			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
-			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
-			FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()).
-			FlagWithOutput("--output=", output).
-			FlagWithArg("--instruction-set=", arch.String())
-		rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
-
-		// Create a phony rule that depends on the output file and prints the path.
-		phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix)
-		rule = android.NewRuleBuilder(pctx, ctx)
-		rule.Command().
-			Implicit(output).
-			ImplicitOutput(phony).
-			Text("echo").FlagWithArg("Output in ", output.String())
-		rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
-
-		allPhonies = append(allPhonies, phony)
-	}
-
-	phony := android.PathForPhony(ctx, "dump-oat-boot")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Phony,
-		Output:      phony,
-		Inputs:      allPhonies,
-		Description: "dump-oat-boot",
-	})
-}
-
-func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
-	data := dexpreopt.GetGlobalConfigRawData(ctx)
-
-	android.WriteFileRule(ctx, path, string(data))
-}
-
-// Define Make variables for boot image names, paths, etc. These variables are used in makefiles
-// (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
-// correct output directories.
-func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
-	if d.dexpreoptConfigForMake != nil {
-		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
-		ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
-	}
-
-	image := d.defaultBootImage
-	if image == nil {
-		return
-	}
-
-	ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
-	if image.profileLicenseMetadataFile.Valid() {
-		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
-	}
-
-	global := dexpreopt.GetGlobalConfig(ctx)
-	dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
-	ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
-	ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
-
-	for _, variant := range image.variants {
-		suffix := ""
-		if variant.target.Os.Class == android.Host {
-			suffix = "_host"
-		}
-		sfx := suffix + "_" + variant.target.Arch.ArchType.String()
-		ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, variant.vdexInstalls.String())
-		ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.imagePathOnHost.String())
-		ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
-		ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
-		ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
-		if variant.licenseMetadataFile.Valid() {
-			ctx.Strict("DEXPREOPT_IMAGE_LICENSE_METADATA_"+sfx, variant.licenseMetadataFile.String())
-		}
-	}
-	imageLocationsOnHost, imageLocationsOnDevice := image.getAnyAndroidVariant().imageLocations()
-	ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST", strings.Join(imageLocationsOnHost, ":"))
-	ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE", strings.Join(imageLocationsOnDevice, ":"))
-	ctx.Strict("DEXPREOPT_IMAGE_ZIP", image.zip.String())
-
-	// There used to be multiple images for JIT-Zygote mode, not there's only one.
-	ctx.Strict("DEXPREOPT_IMAGE_NAMES", image.name)
-}
diff --git a/java/dexpreopt_config.go_v1 b/java/dexpreopt_config.go_v1
deleted file mode 100644
index d71e2bb..0000000
--- a/java/dexpreopt_config.go_v1
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2019 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-import (
-	"path/filepath"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/dexpreopt"
-)
-
-// dexpreoptTargets returns the list of targets that are relevant to dexpreopting, which excludes architectures
-// supported through native bridge.
-func dexpreoptTargets(ctx android.PathContext) []android.Target {
-	var targets []android.Target
-	for _, target := range ctx.Config().Targets[android.Android] {
-		if target.NativeBridge == android.NativeBridgeDisabled {
-			targets = append(targets, target)
-		}
-	}
-	// We may also need the images on host in order to run host-based tests.
-	for _, target := range ctx.Config().Targets[ctx.Config().BuildOS] {
-		targets = append(targets, target)
-	}
-
-	return targets
-}
-
-var (
-	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
-	bootImageConfigRawKey  = android.NewOnceKey("bootImageConfigRaw")
-	artBootImageName       = "art"
-	frameworkBootImageName = "boot"
-)
-
-func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
-	return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
-		global := dexpreopt.GetGlobalConfig(ctx)
-
-		artModules := global.ArtApexJars
-		frameworkModules := global.BootJars.RemoveList(artModules)
-
-		// ART config for the primary boot image in the ART apex.
-		// It includes the Core Libraries.
-		artCfg := bootImageConfig{
-			name:                     artBootImageName,
-			stem:                     "boot",
-			installDirOnHost:         "apex/art_boot_images/javalib",
-			installDirOnDevice:       "system/framework",
-			profileInstallPathInApex: "etc/boot-image.prof",
-			modules:                  artModules,
-			preloadedClassesFile:     "art/build/boot/preloaded-classes",
-		}
-
-		// Framework config for the boot image extension.
-		// It includes framework libraries and depends on the ART config.
-		frameworkSubdir := "system/framework"
-		frameworkCfg := bootImageConfig{
-			extends:              &artCfg,
-			name:                 frameworkBootImageName,
-			stem:                 "boot",
-			installDirOnHost:     frameworkSubdir,
-			installDirOnDevice:   frameworkSubdir,
-			modules:              frameworkModules,
-			preloadedClassesFile: "frameworks/base/config/preloaded-classes",
-		}
-
-		return map[string]*bootImageConfig{
-			artBootImageName:       &artCfg,
-			frameworkBootImageName: &frameworkCfg,
-		}
-	}).(map[string]*bootImageConfig)
-}
-
-// Construct the global boot image configs.
-func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
-	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
-		targets := dexpreoptTargets(ctx)
-		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
-
-		configs := genBootImageConfigRaw(ctx)
-		artCfg := configs[artBootImageName]
-		frameworkCfg := configs[frameworkBootImageName]
-
-		// common to all configs
-		for _, c := range configs {
-			c.dir = deviceDir.Join(ctx, "dex_"+c.name+"jars")
-			c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped")
-
-			// expands to <stem>.art for primary image and <stem>-<1st module>.art for extension
-			imageName := c.firstModuleNameOrStem(ctx) + ".art"
-
-			// The path to bootclasspath dex files needs to be known at module
-			// GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled.
-			// Set up known paths for them, the singleton rules will copy them there.
-			// TODO(b/143682396): use module dependencies instead
-			inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
-			c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
-			c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir)
-			c.dexPathsDeps = c.dexPaths
-
-			// Create target-specific variants.
-			for _, target := range targets {
-				arch := target.Arch.ArchType
-				imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String())
-				variant := &bootImageVariant{
-					bootImageConfig:   c,
-					target:            target,
-					imagePathOnHost:   imageDir.Join(ctx, imageName),
-					imagePathOnDevice: filepath.Join("/", c.installDirOnDevice, arch.String(), imageName),
-					imagesDeps:        c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
-					dexLocations:      c.modules.DevicePaths(ctx.Config(), target.Os),
-				}
-				variant.dexLocationsDeps = variant.dexLocations
-				c.variants = append(c.variants, variant)
-			}
-
-			c.zip = c.dir.Join(ctx, c.name+".zip")
-		}
-
-		// specific to the framework config
-		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
-		for i := range targets {
-			frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
-			frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths()
-			frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
-		}
-
-		return configs
-	}).(map[string]*bootImageConfig)
-}
-
-func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig {
-	return genBootImageConfigs(ctx)[frameworkBootImageName]
-}
-
-// Apex boot config allows to access build/install paths of apex boot jars without going
-// through the usual trouble of registering dependencies on those modules and extracting build paths
-// from those dependencies.
-type apexBootConfig struct {
-	// A list of apex boot jars.
-	modules android.ConfiguredJarList
-
-	// A list of predefined build paths to apex boot jars. They are configured very early,
-	// before the modules for these jars are processed and the actual paths are generated, and
-	// later on a singleton adds commands to copy actual jars to the predefined paths.
-	dexPaths android.WritablePaths
-
-	// Map from module name (without prebuilt_ prefix) to the predefined build path.
-	dexPathsByModule map[string]android.WritablePath
-
-	// A list of dex locations (a.k.a. on-device paths) to the boot jars.
-	dexLocations []string
-}
-
-var updatableBootConfigKey = android.NewOnceKey("apexBootConfig")
-
-// Returns apex boot config.
-func GetApexBootConfig(ctx android.PathContext) apexBootConfig {
-	return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
-		apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
-
-		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "apex_bootjars")
-		dexPaths := apexBootJars.BuildPaths(ctx, dir)
-		dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir)
-
-		dexLocations := apexBootJars.DevicePaths(ctx.Config(), android.Android)
-
-		return apexBootConfig{apexBootJars, dexPaths, dexPathsByModuleName, dexLocations}
-	}).(apexBootConfig)
-}
-
-// Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be
-// passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat).
-func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) {
-	// Non-updatable boot jars (they are used both in the boot image and in dexpreopt).
-	bootImage := defaultBootImageConfig(ctx)
-	dexPaths := bootImage.dexPathsDeps
-	// The dex locations for all Android variants are identical.
-	dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
-
-	if withUpdatable {
-		// Apex boot jars (they are used only in dexpreopt, but not in the boot image).
-		apexBootConfig := GetApexBootConfig(ctx)
-		dexPaths = append(dexPaths, apexBootConfig.dexPaths...)
-		dexLocations = append(dexLocations, apexBootConfig.dexLocations...)
-	}
-
-	return dexPaths, dexLocations
-}
-
-var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
-
-var copyOf = android.CopyOf
-
-func init() {
-	android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars)
-}
-
-func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
-	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
-}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 023d619..9663922 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -256,6 +256,10 @@
 	return j.SdkVersion(ctx)
 }
 
+func (j *Javadoc) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
+}
+
 func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return j.SdkVersion(ctx)
 }
diff --git a/java/java.go b/java/java.go
index 2897fd7..dae69dc 100644
--- a/java/java.go
+++ b/java/java.go
@@ -118,6 +118,16 @@
 		copyEverythingToSnapshot,
 	}
 
+	snapshotRequiresImplementationJar = func(ctx android.SdkMemberContext) bool {
+		// In the S build the build will break if updatable-media does not provide a full implementation
+		// jar. That issue was fixed in Tiramisu by b/229932396.
+		if ctx.IsTargetBuildBeforeTiramisu() && ctx.Name() == "updatable-media" {
+			return true
+		}
+
+		return false
+	}
+
 	// Supports adding java boot libraries to module_exports and sdk.
 	//
 	// The build has some implicit dependencies (via the boot jars configuration) on a number of
@@ -135,13 +145,21 @@
 			SupportsSdk:  true,
 		},
 		func(ctx android.SdkMemberContext, j *Library) android.Path {
+			if snapshotRequiresImplementationJar(ctx) {
+				return exportImplementationClassesJar(ctx, j)
+			}
+
 			// Java boot libs are only provided in the SDK to provide access to their dex implementation
 			// jar for use by dexpreopting and boot jars package check. They do not need to provide an
 			// actual implementation jar but the java_import will need a file that exists so just copy an
 			// empty file. Any attempt to use that file as a jar will cause a build error.
 			return ctx.SnapshotBuilder().EmptyFile()
 		},
-		func(osPrefix, name string) string {
+		func(ctx android.SdkMemberContext, osPrefix, name string) string {
+			if snapshotRequiresImplementationJar(ctx) {
+				return sdkSnapshotFilePathForJar(ctx, osPrefix, name)
+			}
+
 			// Create a special name for the implementation jar to try and provide some useful information
 			// to a developer that attempts to compile against this.
 			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
@@ -164,6 +182,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
@@ -172,7 +193,7 @@
 			// file. Any attempt to use that file as a jar will cause a build error.
 			return ctx.SnapshotBuilder().EmptyFile()
 		},
-		func(osPrefix, name string) string {
+		func(_ android.SdkMemberContext, osPrefix, name string) string {
 			// Create a special name for the implementation jar to try and provide some useful information
 			// to a developer that attempts to compile against this.
 			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
@@ -510,6 +531,20 @@
 	}
 }
 
+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
@@ -655,7 +690,7 @@
 )
 
 // path to the jar file of a java library. Relative to <sdk_root>/<api_dir>
-func sdkSnapshotFilePathForJar(osPrefix, name string) string {
+func sdkSnapshotFilePathForJar(_ android.SdkMemberContext, osPrefix, name string) string {
 	return sdkSnapshotFilePathForMember(osPrefix, name, jarFileSuffix)
 }
 
@@ -672,7 +707,7 @@
 
 	// Function to compute the snapshot relative path to which the named library's
 	// jar should be copied.
-	snapshotPathGetter func(osPrefix, name string) string
+	snapshotPathGetter func(ctx android.SdkMemberContext, osPrefix, name string) string
 
 	// True if only the jar should be copied to the snapshot, false if the jar plus any additional
 	// files like aidl files should also be copied.
@@ -730,7 +765,7 @@
 	exportedJar := p.JarToExport
 	if exportedJar != nil {
 		// Delegate the creation of the snapshot relative path to the member type.
-		snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(p.OsPrefix(), ctx.Name())
+		snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(ctx, p.OsPrefix(), ctx.Name())
 
 		// Copy the exported jar to the snapshot.
 		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
@@ -1196,7 +1231,7 @@
 
 	exportedJar := p.JarToExport
 	if exportedJar != nil {
-		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), ctx.Name())
+		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(ctx, p.OsPrefix(), ctx.Name())
 		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
 		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
@@ -1442,6 +1477,10 @@
 	// specified.
 	Min_sdk_version *string
 
+	// The max sdk version placeholder used to replace maxSdkVersion attributes on permission
+	// and uses-permission tags in manifest_fixer.
+	Replace_max_sdk_version_placeholder *string
+
 	Installable *bool
 
 	// If not empty, classes are restricted to the specified packages and their sub-packages.
@@ -1521,6 +1560,13 @@
 	return j.SdkVersion(ctx)
 }
 
+func (j *Import) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	if j.properties.Replace_max_sdk_version_placeholder != nil {
+		return android.SdkSpecFrom(ctx, *j.properties.Replace_max_sdk_version_placeholder)
+	}
+	return android.SdkSpecFrom(ctx, "")
+}
+
 func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return j.SdkVersion(ctx)
 }
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 e276345..c27ca98 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -338,6 +338,14 @@
 			ctx.PropertyErrorf("lint.disabled_checks",
 				"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
 		}
+
+		// TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController
+		if ctx.ModuleName() == "PermissionController" {
+			l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool {
+				return s != "NewApi"
+			})
+			l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi")
+		}
 	}
 
 	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 1eee354..e99cb05 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -6,6 +6,7 @@
 
 --disable_check AnimatorKeep
 --disable_check AppBundleLocaleChanges
+--disable_check AppCompatCustomView
 --disable_check BlockedPrivateApi
 --disable_check CustomSplashScreen
 --disable_check CustomX509TrustManager
@@ -22,6 +23,7 @@
 --disable_check PrivateApi
 --disable_check ProtectedPermissions
 --disable_check QueryPermissionsNeeded
+--disable_check ReservedSystemPermission
 --disable_check ScopedStorage
 --disable_check ServiceCast
 --disable_check SoonBlockedPrivateApi
@@ -99,7 +101,10 @@
 --warning_check WrongViewCast                      # 1 occurences in 1 modules
 
 --warning_check CoarseFineLocation
+--warning_check ExtraText
 --warning_check IntentFilterExportedReceiver
+--warning_check MissingInflatedId
+--warning_check NotificationPermission
 --warning_check QueryAllPackagesPermission
 --warning_check RemoteViewLayout
 --warning_check SupportAnnotationUsage
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index f442ddf..1c42495 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -26,12 +26,14 @@
 func init() {
 	registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&compatConfigMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "compat_configs",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(CompatConfigSdkMemberType)
+}
+
+var CompatConfigSdkMemberType = &compatConfigMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "compat_configs",
+		SupportsSdk:  true,
+	},
 }
 
 func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
diff --git a/java/rro.go b/java/rro.go
index be84aff..7952c2c 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -173,6 +173,10 @@
 	return r.SdkVersion(ctx)
 }
 
+func (r *RuntimeResourceOverlay) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, "")
+}
+
 func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return r.SdkVersion(ctx)
 }
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/rust/bindgen.go b/rust/bindgen.go
index 4d723d6..72cc894 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -239,6 +239,11 @@
 		cflags = append(cflags, "-x c")
 	}
 
+	// LLVM_NEXT may contain flags that bindgen doesn't recognise. Turn off unknown flags warning.
+	if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+		cflags = append(cflags, "-Wno-unknown-warning-option")
+	}
+
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string
diff --git a/rust/config/global.go b/rust/config/global.go
index 3ef0ecb..9acbfb3 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.61.0.p2"
+	RustDefaultVersion = "1.62.0.p1"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 55921ba..586095c 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -153,7 +153,7 @@
 		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, cc.UnstrippedOutputFile, cc.IsValidSharedDependency)
 
 		// Package shared libraries
-		files = append(files, cc.GetSharedLibsToZip(sharedLibraries, rustModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)
+		files = append(files, cc.GetSharedLibsToZip(sharedLibraries, rustModule, &s.FuzzPackager, archString, "lib", &sharedLibraryInstalled)...)
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 4773579..814bd57 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -84,6 +84,16 @@
     ],
 }
 
+python_test_host {
+    name: "jsonmodify_test",
+    main: "jsonmodify_test.py",
+    srcs: [
+        "jsonmodify_test.py",
+        "jsonmodify.py",
+    ],
+    test_suites: ["general-tests"],
+}
+
 python_binary_host {
     name: "test_config_fixer",
     main: "test_config_fixer.py",
@@ -193,3 +203,9 @@
     name: "list_image",
     src: "list_image.sh",
 }
+
+filegroup {
+    name: "rustfmt.toml",
+    srcs: ["rustfmt.toml"],
+    visibility: ["//visibility:public"],
+}
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
index ba1109e..8bd8d45 100755
--- a/scripts/jsonmodify.py
+++ b/scripts/jsonmodify.py
@@ -59,6 +59,13 @@
       cur[key] = val
 
 
+class ReplaceIfEqual(str):
+  def apply(self, obj, old_val, new_val):
+    cur, key = follow_path(obj, self)
+    if cur and cur[key] == int(old_val):
+      cur[key] = new_val
+
+
 class Remove(str):
   def apply(self, obj):
     cur, key = follow_path(obj, self)
@@ -75,6 +82,14 @@
       raise ValueError(self + " should be a array.")
     cur[key].extend(args)
 
+# A JSONDecoder that supports line comments start with //
+class JSONWithCommentsDecoder(json.JSONDecoder):
+  def __init__(self, **kw):
+    super().__init__(**kw)
+
+  def decode(self, s: str):
+    s = '\n'.join(l for l in s.split('\n') if not l.lstrip(' ').startswith('//'))
+    return super().decode(s)
 
 def main():
   parser = argparse.ArgumentParser()
@@ -91,6 +106,11 @@
                       help='replace value of the key specified by path. If path doesn\'t exist, no op.',
                       metavar=('path', 'value'),
                       nargs=2, dest='patch', action='append')
+  parser.add_argument("-se", "--replace-if-equal", type=ReplaceIfEqual,
+                      help='replace value of the key specified by path to new_value if it\'s equal to old_value.' +
+                      'If path doesn\'t exist or the value is not equal to old_value, no op.',
+                      metavar=('path', 'old_value', 'new_value'),
+                      nargs=3, dest='patch', action='append')
   parser.add_argument("-r", "--remove", type=Remove,
                       help='remove the key specified by path. If path doesn\'t exist, no op.',
                       metavar='path',
@@ -103,9 +123,9 @@
 
   if args.input:
     with open(args.input) as f:
-      obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+      obj = json.load(f, object_pairs_hook=collections.OrderedDict, cls=JSONWithCommentsDecoder)
   else:
-    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
+    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict, cls=JSONWithCommentsDecoder)
 
   for p in args.patch:
     p[0].apply(obj, *p[1:])
diff --git a/scripts/jsonmodify_test.py b/scripts/jsonmodify_test.py
new file mode 100644
index 0000000..6f0291d
--- /dev/null
+++ b/scripts/jsonmodify_test.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+"""Tests for jsonmodify."""
+
+import json
+import jsonmodify
+import unittest
+
+
+class JsonmodifyTest(unittest.TestCase):
+
+  def test_set_value(self):
+    obj = json.loads('{"field1": 111}')
+    field1 = jsonmodify.SetValue("field1")
+    field1.apply(obj, 222)
+    field2 = jsonmodify.SetValue("field2")
+    field2.apply(obj, 333)
+    expected = json.loads('{"field1": 222, "field2": 333}')
+    self.assertEqual(obj, expected)
+
+  def test_replace(self):
+    obj = json.loads('{"field1": 111}')
+    field1 = jsonmodify.Replace("field1")
+    field1.apply(obj, 222)
+    field2 = jsonmodify.Replace("field2")
+    field2.apply(obj, 333)
+    expected = json.loads('{"field1": 222}')
+    self.assertEqual(obj, expected)
+
+  def test_replace_if_equal(self):
+    obj = json.loads('{"field1": 111, "field2": 222}')
+    field1 = jsonmodify.ReplaceIfEqual("field1")
+    field1.apply(obj, 111, 333)
+    field2 = jsonmodify.ReplaceIfEqual("field2")
+    field2.apply(obj, 444, 555)
+    field3 = jsonmodify.ReplaceIfEqual("field3")
+    field3.apply(obj, 666, 777)
+    expected = json.loads('{"field1": 333, "field2": 222}')
+    self.assertEqual(obj, expected)
+
+  def test_remove(self):
+    obj = json.loads('{"field1": 111, "field2": 222}')
+    field2 = jsonmodify.Remove("field2")
+    field2.apply(obj)
+    field3 = jsonmodify.Remove("field3")
+    field3.apply(obj)
+    expected = json.loads('{"field1": 111}')
+    self.assertEqual(obj, expected)
+
+  def test_append_list(self):
+    obj = json.loads('{"field1": [111]}')
+    field1 = jsonmodify.AppendList("field1")
+    field1.apply(obj, 222, 333)
+    field2 = jsonmodify.AppendList("field2")
+    field2.apply(obj, 444, 555, 666)
+    expected = json.loads('{"field1": [111, 222, 333], "field2": [444, 555, 666]}')
+    self.assertEqual(obj, expected)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 2d3103b..2da29ee 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -39,6 +39,8 @@
   parser = argparse.ArgumentParser()
   parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
                       help='specify minSdkVersion used by the build system')
+  parser.add_argument('--replaceMaxSdkVersionPlaceholder', default='', dest='max_sdk_version',
+                      help='specify maxSdkVersion used by the build system')
   parser.add_argument('--targetSdkVersion', default='', dest='target_sdk_version',
                       help='specify targetSdkVersion used by the build system')
   parser.add_argument('--raise-min-sdk-version', dest='raise_min_sdk_version', action='store_true',
@@ -342,6 +344,24 @@
   attr.value = 'true'
   application.setAttributeNode(attr)
 
+def set_max_sdk_version(doc, max_sdk_version):
+  """Replace the maxSdkVersion attribute value for permission and
+  uses-permission tags if the value was originally set to 'current'.
+  Used for cts test cases where the maxSdkVersion should equal to
+  Build.SDK_INT.
+
+  Args:
+    doc: The XML document.  May be modified by this function.
+    max_sdk_version: The requested maxSdkVersion attribute.
+  """
+  manifest = parse_manifest(doc)
+  for tag in ['permission', 'uses-permission']:
+    children = get_children_with_tag(manifest, tag)
+    for child in children:
+      max_attr = child.getAttributeNodeNS(android_ns, 'maxSdkVersion')
+      if max_attr and max_attr.value == 'current':
+        max_attr.value = max_sdk_version
+
 def main():
   """Program entry point."""
   try:
@@ -354,6 +374,9 @@
     if args.raise_min_sdk_version:
       raise_min_sdk_version(doc, args.min_sdk_version, args.target_sdk_version, args.library)
 
+    if args.max_sdk_version:
+      set_max_sdk_version(doc, args.max_sdk_version)
+
     if args.uses_libraries:
       add_uses_libraries(doc, args.uses_libraries, True)
 
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 199b279..dad104a 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -571,5 +571,77 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
+
+class SetMaxSdkVersionTest(unittest.TestCase):
+  """Unit tests for set_max_sdk_version function."""
+
+  def assert_xml_equal(self, output, expected):
+    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+  def run_test(self, input_manifest, max_sdk_version):
+    doc = minidom.parseString(input_manifest)
+    manifest_fixer.set_max_sdk_version(doc, max_sdk_version)
+    output = io.StringIO()
+    manifest_fixer.write_xml(output, doc)
+    return output.getvalue()
+
+  manifest_tmpl = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+      '%s'
+      '</manifest>\n')
+
+  def permission(self, max=None):
+    if max is None:
+      return '   <permission/>'
+    return '    <permission android:maxSdkVersion="%s"/>\n' % max
+
+  def uses_permission(self, max=None):
+    if max is None:
+      return '   <uses-permission/>'
+    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max
+
+  def test_permission_no_max_sdk_version(self):
+    """Tests if permission has no maxSdkVersion attribute"""
+    manifest_input = self.manifest_tmpl % self.permission()
+    expected = self.manifest_tmpl % self.permission()
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_permission_max_sdk_version_changed(self):
+    """Tests if permission maxSdkVersion attribute is set to current"""
+    manifest_input = self.manifest_tmpl % self.permission('current')
+    expected = self.manifest_tmpl % self.permission(9000)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_permission_max_sdk_version_not_changed(self):
+    """Tests if permission maxSdkVersion attribute is not set to current"""
+    manifest_input = self.manifest_tmpl % self.permission(30)
+    expected = self.manifest_tmpl % self.permission(30)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_uses_permission_no_max_sdk_version(self):
+    """Tests if uses-permission has no maxSdkVersion attribute"""
+    manifest_input = self.manifest_tmpl % self.uses_permission()
+    expected = self.manifest_tmpl % self.uses_permission()
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_uses_permission_max_sdk_version_changed(self):
+    """Tests if uses-permission maxSdkVersion attribute is set to current"""
+    manifest_input = self.manifest_tmpl % self.uses_permission('current')
+    expected = self.manifest_tmpl % self.uses_permission(9000)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_uses_permission_max_sdk_version_not_changed(self):
+    """Tests if uses-permission maxSdkVersion attribute is not set to current"""
+    manifest_input = self.manifest_tmpl % self.uses_permission(30)
+    expected = self.manifest_tmpl % self.uses_permission(30)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 93ad172..13ddbe7 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -190,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,
@@ -202,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",
@@ -373,24 +361,7 @@
     },
 }
 		`),
-		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")
@@ -427,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) {
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 4c2277e..0494a28 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -24,18 +24,22 @@
 
 // buildRelease represents the version of a build system used to create a specific release.
 //
-// The name of the release, is the same as the code for the dessert release, e.g. S, T, etc.
+// The name of the release, is the same as the code for the dessert release, e.g. S, Tiramisu, etc.
 type buildRelease struct {
-	// The name of the release, e.g. S, T, etc.
+	// The name of the release, e.g. S, Tiramisu, etc.
 	name string
 
 	// The index of this structure within the buildReleases list.
 	ordinal int
 }
 
+func (br *buildRelease) EarlierThan(other *buildRelease) bool {
+	return br.ordinal < other.ordinal
+}
+
 // String returns the name of the build release.
-func (s *buildRelease) String() string {
-	return s.name
+func (br *buildRelease) String() string {
+	return br.name
 }
 
 // buildReleaseSet represents a set of buildRelease objects.
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index d166add..45e8e0e 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -21,16 +21,12 @@
 	"android/soong/java"
 )
 
-func TestSnapshotWithCompatConfig(t *testing.T) {
+func testSnapshotWithCompatConfig(t *testing.T, sdk string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithPlatformCompatConfig,
-	).RunTestWithBp(t, `
-		sdk {
-			name: "mysdk",
-			compat_configs: ["myconfig"],
-		}
-
+		prepareForSdkTestWithApex,
+	).RunTestWithBp(t, sdk+`
 		platform_compat_config {
 			name: "myconfig",
 		}
@@ -73,3 +69,28 @@
 			}),
 	)
 }
+
+func TestSnapshotWithCompatConfig(t *testing.T) {
+	testSnapshotWithCompatConfig(t, `
+		sdk {
+			name: "mysdk",
+			compat_configs: ["myconfig"],
+		}
+`)
+}
+
+func TestSnapshotWithCompatConfig_Apex(t *testing.T) {
+	testSnapshotWithCompatConfig(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			min_sdk_version: "2",
+			compat_configs: ["myconfig"],
+		}
+
+		sdk {
+			name: "mysdk",
+			apexes: ["myapex"],
+		}
+`)
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index d25138f..7ab5285 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -15,6 +15,7 @@
 package sdk
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -257,8 +258,8 @@
 		android.FixtureAddFile("aidl", nil),
 		android.FixtureAddFile("resource.txt", nil),
 	).RunTestWithBp(t, `
-		module_exports {
-			name: "myexports",
+		sdk {
+			name: "mysdk",
 			java_boot_libs: ["myjavalib"],
 		}
 
@@ -278,7 +279,7 @@
 		}
 	`)
 
-	CheckSnapshot(t, result, "myexports", "",
+	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -292,11 +293,65 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
 	)
 }
 
+func TestSnapshotWithJavaBootLibrary_UpdatableMedia(t *testing.T) {
+	runTest := func(t *testing.T, targetBuildRelease, expectedJarPath, expectedCopyRule string) {
+		result := android.GroupFixturePreparers(
+			prepareForSdkTestWithJava,
+			android.FixtureMergeEnv(map[string]string{
+				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
+			}),
+		).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			java_boot_libs: ["updatable-media"],
+		}
+
+		java_library {
+			name: "updatable-media",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			permitted_packages: ["pkg.media"],
+			apex_available: ["com.android.media"],
+		}
+	`)
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(fmt.Sprintf(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "updatable-media",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.media"],
+    jars: ["%s"],
+    permitted_packages: ["pkg.media"],
+}
+`, expectedJarPath)),
+			checkAllCopyRules(expectedCopyRule),
+		)
+	}
+
+	t.Run("updatable-media in S", func(t *testing.T) {
+		runTest(t, "S", "java/updatable-media.jar", `
+.intermediates/updatable-media/android_common/package-check/updatable-media.jar -> java/updatable-media.jar
+`)
+	})
+
+	t.Run("updatable-media in T", func(t *testing.T) {
+		runTest(t, "Tiramisu", "java_boot_libs/snapshot/jars/are/invalid/updatable-media.jar", `
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/updatable-media.jar
+`)
+	})
+}
+
 func TestSnapshotWithJavaSystemserverLibrary(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
@@ -1284,9 +1339,9 @@
 .intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
 `),
 		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
-			".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
 		),
 	)
 }
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 01692a3..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", "",
-		checkAndroidBpContents(`
+		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,6 +133,80 @@
         "mysdklibrary",
     ],
 }
-`),
-	)
+`
+
+	t.Run("target-s", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "S", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    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,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_import {
+    name: "mylib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    permitted_packages: ["mylib"],
+}
+
+prebuilt_systemserverclasspath_fragment {
+    name: "mysystemserverclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    contents: [
+        "mylib",
+        "mysdklibrary",
+    ],
+}
+`)
+	})
+
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
+	})
+
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, `latest`, expectedLatestSnapshot)
+	})
 }
diff --git a/sdk/update.go b/sdk/update.go
index 457828b..c555ddc 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -108,7 +108,7 @@
 
 	mergeZips = pctx.AndroidStaticRule("SnapshotMergeZips",
 		blueprint.RuleParams{
-			Command: `${config.MergeZipsCmd} $out $in`,
+			Command: `${config.MergeZipsCmd} -s $out $in`,
 			CommandDeps: []string{
 				"${config.MergeZipsCmd}",
 			},
@@ -239,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,13 +268,39 @@
 	}
 	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 {
@@ -401,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]
@@ -452,7 +481,7 @@
 	// Copy the build number file into the snapshot.
 	builder.CopyToSnapshot(ctx.Config().BuildNumberFile(ctx), BUILD_NUMBER_FILE)
 
-	filesToZip := builder.filesToZip
+	filesToZip := android.SortedUniquePaths(builder.filesToZip)
 
 	// zip them all
 	zipPath := fmt.Sprintf("%s%s.zip", ctx.ModuleName(), snapshotFileSuffix)
@@ -488,7 +517,7 @@
 			Description: outputDesc,
 			Rule:        mergeZips,
 			Input:       zipFile,
-			Inputs:      builder.zipsToMerge,
+			Inputs:      android.SortedUniquePaths(builder.zipsToMerge),
 			Output:      outputZipFile,
 		})
 	}
@@ -1293,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
 }
@@ -1348,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
@@ -1823,12 +1972,19 @@
 	return m.requiredTraits.Contains(trait)
 }
 
+func (m *memberContext) IsTargetBuildBeforeTiramisu() bool {
+	return m.builder.targetBuildRelease.EarlierThan(buildReleaseT)
+}
+
+var _ android.SdkMemberContext = (*memberContext)(nil)
+
 func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) {
 
 	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.
@@ -1853,9 +2009,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)
@@ -1901,7 +2058,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/ui/build/Android.bp b/ui/build/Android.bp
index 3dc87f5..cfcf804 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -34,15 +34,16 @@
     deps: [
         "blueprint",
         "blueprint-bootstrap",
+        "blueprint-microfactory",
+        "soong-finder",
+        "soong-remoteexec",
+        "soong-shared",
         "soong-ui-build-paths",
         "soong-ui-logger",
         "soong-ui-metrics",
         "soong-ui-status",
         "soong-ui-terminal",
         "soong-ui-tracer",
-        "soong-shared",
-        "soong-finder",
-        "blueprint-microfactory",
     ],
     srcs: [
         "bazel.go",
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 1c80cff..fd60177 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -167,6 +167,7 @@
 		productOut("debug_ramdisk"),
 		productOut("vendor_ramdisk"),
 		productOut("vendor_debug_ramdisk"),
+		productOut("vendor_kernel_ramdisk"),
 		productOut("test_harness_ramdisk"),
 		productOut("recovery"),
 		productOut("root"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 8874209..cbf1986 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -156,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 {
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 82fc15f..6231e52 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -21,6 +21,7 @@
 	"runtime"
 	"strings"
 
+	"android/soong/remoteexec"
 	"android/soong/ui/metrics"
 )
 
@@ -54,11 +55,12 @@
 
 func getRBEVars(ctx Context, config Config) map[string]string {
 	vars := map[string]string{
-		"RBE_log_dir":    config.rbeProxyLogsDir(),
-		"RBE_re_proxy":   config.rbeReproxy(),
-		"RBE_exec_root":  config.rbeExecRoot(),
-		"RBE_output_dir": config.rbeProxyLogsDir(),
+		"RBE_log_dir":       config.rbeProxyLogsDir(),
+		"RBE_re_proxy":      config.rbeReproxy(),
+		"RBE_exec_root":     config.rbeExecRoot(),
+		"RBE_output_dir":    config.rbeProxyLogsDir(),
 		"RBE_proxy_log_dir": config.rbeProxyLogsDir(),
+		"RBE_platform":      "container-image=" + remoteexec.DefaultImage,
 	}
 	if config.StartRBE() {
 		name, err := config.rbeSockAddr(absPath(ctx, config.TempDir()))
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 cef3b5d..9e9ffc0 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -22,30 +22,24 @@
 )
 
 type simpleStatusOutput struct {
-	writer      io.Writer
-	formatter   formatter
-	keepANSI    bool
-	outputLevel status.MsgLevel
+	writer    io.Writer
+	formatter formatter
+	keepANSI  bool
 }
 
 // NewSimpleStatusOutput returns a StatusOutput that represents the
 // current build status similarly to Ninja's built-in terminal
 // output.
-func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool, quietBuild bool) status.StatusOutput {
-	level := status.StatusLvl
-	if quietBuild {
-		level = status.PrintLvl
-	}
+func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput {
 	return &simpleStatusOutput{
-		writer:      w,
-		formatter:   formatter,
-		keepANSI:    keepANSI,
-		outputLevel: level,
+		writer:    w,
+		formatter: formatter,
+		keepANSI:  keepANSI,
 	}
 }
 
 func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
-	if level >= s.outputLevel {
+	if level >= status.StatusLvl {
 		output := s.formatter.message(level, message)
 		if !s.keepANSI {
 			output = string(stripAnsiEscapes([]byte(output)))
@@ -54,13 +48,10 @@
 	}
 }
 
-func (s *simpleStatusOutput) StartAction(_ *status.Action, _ status.Counts) {
+func (s *simpleStatusOutput) StartAction(action *status.Action, counts status.Counts) {
 }
 
 func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
-	if s.outputLevel > status.StatusLvl {
-		return
-	}
 	str := result.Description
 	if str == "" {
 		str = result.Command
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index ff0af47..2ad174f 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -29,9 +29,9 @@
 func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
-	if forceSimpleOutput || quietBuild || !isSmartTerminal(w) {
-		return NewSimpleStatusOutput(w, formatter, forceKeepANSI, quietBuild)
-	} else {
+	if !forceSimpleOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
+	} else {
+		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
 	}
 }