Merge "Support use_version_lib in cc_binary."
diff --git a/android/Android.bp b/android/Android.bp
index c072ac2..87b021f 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -19,6 +19,7 @@
         "soong-shared",
         "soong-starlark-format",
         "soong-ui-metrics_proto",
+        "soong-android-allowlists",
 
         "golang-protobuf-proto",
         "golang-protobuf-encoding-prototext",
@@ -36,6 +37,7 @@
         "bazel_handler.go",
         "bazel_paths.go",
         "config.go",
+        "config_bp2build.go",
         "csuite_config.go",
         "deapexer.go",
         "defaults.go",
@@ -96,6 +98,7 @@
         "bazel_handler_test.go",
         "bazel_test.go",
         "config_test.go",
+        "config_bp2build_test.go",
         "csuite_config_test.go",
         "defaults_test.go",
         "depset_test.go",
diff --git a/android/allowlists/Android.bp b/android/allowlists/Android.bp
new file mode 100644
index 0000000..05cffc1
--- /dev/null
+++ b/android/allowlists/Android.bp
@@ -0,0 +1,25 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-android-allowlists",
+    pkgPath: "android/soong/android/allowlists",
+    srcs: [
+        "allowlists.go",
+    ],
+}
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
new file mode 100644
index 0000000..72961e6
--- /dev/null
+++ b/android/allowlists/allowlists.go
@@ -0,0 +1,414 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package allowlists
+
+// Configuration to decide if modules in a directory should default to true/false for bp2build_available
+type Bp2BuildConfig map[string]BazelConversionConfigEntry
+type BazelConversionConfigEntry int
+
+const (
+	// iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
+	// which can also mean that the key doesn't exist in a lookup.
+
+	// all modules in this package and subpackages default to bp2build_available: true.
+	// allows modules to opt-out.
+	Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
+
+	// all modules in this package (not recursively) default to bp2build_available: true.
+	// allows modules to opt-out.
+	Bp2BuildDefaultTrue
+
+	// all modules in this package (not recursively) default to bp2build_available: false.
+	// allows modules to opt-in.
+	Bp2BuildDefaultFalse
+)
+
+var (
+	Bp2buildDefaultConfig = Bp2BuildConfig{
+		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
+		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
+		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
+		"art/tools":                             Bp2BuildDefaultTrue,
+		"bionic":                                Bp2BuildDefaultTrueRecursively,
+		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
+		"build/bazel/examples/soong_config_variables":        Bp2BuildDefaultTrueRecursively,
+		"build/bazel/examples/apex/minimal":                  Bp2BuildDefaultTrueRecursively,
+		"build/make/tools/signapk":                           Bp2BuildDefaultTrue,
+		"build/make/target/product/security":                 Bp2BuildDefaultTrue,
+		"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/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/error_prone":                               Bp2BuildDefaultTrueRecursively,
+		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
+		"external/google-benchmark":                          Bp2BuildDefaultTrueRecursively,
+		"external/googletest":                                Bp2BuildDefaultTrueRecursively,
+		"external/gwp_asan":                                  Bp2BuildDefaultTrueRecursively,
+		"external/icu":                                       Bp2BuildDefaultTrueRecursively,
+		"external/icu/android_icu4j":                         Bp2BuildDefaultFalse, // java rules incomplete
+		"external/icu/icu4j":                                 Bp2BuildDefaultFalse, // java rules incomplete
+		"external/javapoet":                                  Bp2BuildDefaultTrueRecursively,
+		"external/jemalloc_new":                              Bp2BuildDefaultTrueRecursively,
+		"external/jsoncpp":                                   Bp2BuildDefaultTrueRecursively,
+		"external/libcap":                                    Bp2BuildDefaultTrueRecursively,
+		"external/libcxx":                                    Bp2BuildDefaultTrueRecursively,
+		"external/libcxxabi":                                 Bp2BuildDefaultTrueRecursively,
+		"external/libevent":                                  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/scudo":                                     Bp2BuildDefaultTrueRecursively,
+		"external/selinux/libselinux":                        Bp2BuildDefaultTrueRecursively,
+		"external/selinux/libsepol":                          Bp2BuildDefaultTrueRecursively,
+		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
+		"external/zstd":                                      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/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/proto":                                  Bp2BuildDefaultTrueRecursively,
+		"system/apex/libs":                                   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/libbase":                                     Bp2BuildDefaultTrueRecursively,
+		"system/libprocinfo":                                 Bp2BuildDefaultTrue,
+		"system/libziparchive":                               Bp2BuildDefaultTrueRecursively,
+		"system/logging/liblog":                              Bp2BuildDefaultTrueRecursively,
+		"system/sepolicy/apex":                               Bp2BuildDefaultTrueRecursively,
+		"system/timezone/apex":                               Bp2BuildDefaultTrueRecursively,
+		"system/timezone/output_data":                        Bp2BuildDefaultTrueRecursively,
+		"system/unwinding/libbacktrace":                      Bp2BuildDefaultTrueRecursively,
+		"system/unwinding/libunwindstack":                    Bp2BuildDefaultTrueRecursively,
+		"tools/apksig":                                       Bp2BuildDefaultTrue,
+		"tools/platform-compat/java/android/compat":          Bp2BuildDefaultTrueRecursively,
+	}
+
+	Bp2buildKeepExistingBuildFile = map[string]bool{
+		// This is actually build/bazel/build.BAZEL symlinked to ./BUILD
+		".":/*recursive = */ false,
+
+		// build/bazel/examples/apex/... BUILD files should be generated, so
+		// build/bazel is not recursive. Instead list each subdirectory under
+		// build/bazel explicitly.
+		"build/bazel":/* recursive = */ false,
+		"build/bazel/ci/dist":/* recursive = */ false,
+		"build/bazel/examples/android_app":/* recursive = */ true,
+		"build/bazel/examples/java":/* recursive = */ true,
+		"build/bazel/bazel_skylib":/* recursive = */ true,
+		"build/bazel/rules":/* recursive = */ true,
+		"build/bazel/rules_cc":/* recursive = */ true,
+		"build/bazel/scripts":/* recursive = */ true,
+		"build/bazel/tests":/* recursive = */ true,
+		"build/bazel/platforms":/* recursive = */ true,
+		"build/bazel/product_variables":/* recursive = */ true,
+		"build/bazel/vendor/google":/* recursive = */ true,
+		"build/bazel_common_rules":/* recursive = */ true,
+		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
+		"build/make/tools":/* recursive = */ false,
+		"build/pesto":/* recursive = */ true,
+
+		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
+		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
+		"external/bazelbuild-rules_android":/* recursive = */ true,
+		"external/bazel-skylib":/* recursive = */ true,
+		"external/guava":/* recursive = */ true,
+		"external/jsr305":/* recursive = */ true,
+		"frameworks/ex/common":/* recursive = */ true,
+
+		"packages/apps/Music":/* recursive = */ true,
+		"packages/apps/QuickSearchBox":/* recursive = */ true,
+		"packages/apps/WallpaperPicker":/* recursive = */ false,
+
+		"prebuilts/bundletool":/* recursive = */ true,
+		"prebuilts/gcc":/* recursive = */ true,
+		"prebuilts/build-tools":/* recursive = */ false,
+		"prebuilts/jdk/jdk11":/* recursive = */ false,
+		"prebuilts/sdk":/* recursive = */ false,
+		"prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
+		"prebuilts/sdk/current/support":/* recursive = */ false,
+		"prebuilts/sdk/tools":/* recursive = */ false,
+		"prebuilts/r8":/* recursive = */ false,
+	}
+
+	Bp2buildModuleAlwaysConvertList = []string{
+		//external/avb
+		"avbtool",
+		"libavb",
+		"avb_headers",
+
+		//external/fec
+		"libfec_rs",
+
+		//system/core/libsparse
+		"libsparse",
+
+		//system/extras/ext4_utils
+		"libext4_utils",
+
+		//system/extras/libfec
+		"libfec",
+
+		//system/extras/squashfs_utils
+		"libsquashfs_utils",
+
+		//system/extras/verity/fec
+		"fec",
+
+		//packages/apps/Car/libs/car-ui-lib/car-ui-androidx
+		// genrule dependencies for java_imports
+		"car-ui-androidx-annotation-nodeps",
+		"car-ui-androidx-collection-nodeps",
+		"car-ui-androidx-core-common-nodeps",
+		"car-ui-androidx-lifecycle-common-nodeps",
+		"car-ui-androidx-constraintlayout-solver-nodeps",
+	}
+
+	Bp2buildModuleTypeAlwaysConvertList = []string{
+		"java_import",
+		"java_import_host",
+	}
+
+	Bp2buildModuleDoNotConvertList = []string{
+		// cc bugs
+		"libsepol",                                  // TODO(b/207408632): Unsupported case of .l sources in cc library rules
+		"libactivitymanager_aidl",                   // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
+		"gen-kotlin-build-file.py",                  // TODO(b/198619163) module has same name as source
+		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
+		"linkerconfig", "mdnsd", // TODO(b/202876379): has arch-variant static_executable
+		"linker",       // TODO(b/228316882): cc_binary uses link_crt
+		"libdebuggerd", // TODO(b/228314770): support product variable-specific header_libs
+		"versioner",    // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
+
+		// java bugs
+		"libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
+
+		// python protos
+		"libprotobuf-python",                           // TODO(b/196084681): contains .proto sources
+		"apex_build_info_proto", "apex_manifest_proto", // TODO(b/196084681): a python lib with proto sources
+		"linker_config_proto", // TODO(b/196084681): contains .proto sources
+
+		// genrule incompatibilities
+		"brotli-fuzzer-corpus",                                       // TODO(b/202015218): outputs are in location incompatible with bazel genrule handling.
+		"platform_tools_properties", "build_tools_source_properties", // TODO(b/203369847): multiple genrules in the same package creating the same file
+
+		// aar support
+		"prebuilt_car-ui-androidx-core-common",         // TODO(b/224773339), genrule dependency creates an .aar, not a .jar
+		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
+		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
+
+		// path property for filegroups
+		"conscrypt",                        // TODO(b/210751803), we don't handle path property for filegroups
+		"conscrypt-for-host",               // TODO(b/210751803), we don't handle path property for filegroups
+		"host-libprotobuf-java-full",       // TODO(b/210751803), we don't handle path property for filegroups
+		"libprotobuf-internal-protos",      // TODO(b/210751803), we don't handle path property for filegroups
+		"libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
+		"libprotobuf-java-full",            // TODO(b/210751803), we don't handle path property for filegroups
+		"libprotobuf-java-util-full",       // TODO(b/210751803), we don't handle path property for filegroups
+
+		// go deps:
+		"analyze_bcpf",                                                                               // depends on bpmodify a blueprint_go_binary.
+		"apex-protos",                                                                                // depends on soong_zip, a go binary
+		"generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // depends on unconverted modules: soong_zip
+		"host_bionic_linker_asm",                                                  // depends on extract_linker, a go binary.
+		"host_bionic_linker_script",                                               // depends on extract_linker, a go binary.
+		"libc_musl_sysroot_bionic_arch_headers",                                   // depends on soong_zip
+		"libc_musl_sysroot_bionic_headers",                                        // 218405924, depends on soong_zip and generates duplicate srcs
+		"libc_musl_sysroot_libc++_headers", "libc_musl_sysroot_libc++abi_headers", // depends on soong_zip, zip2zip
+		"robolectric-sqlite4java-native", // depends on soong_zip, a go binary
+		"robolectric_tzdata",             // depends on soong_zip, a go binary
+
+		// rust support
+		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
+
+		// unconverted deps
+		"CarHTMLViewer",                // depends on unconverted modules android.car-stubs, car-ui-lib
+		"abb",                          // depends on unconverted modules: libcmd, libbinder
+		"adb",                          // depends on unconverted modules: AdbWinApi, libandroidfw, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
+		"android_icu4j_srcgen",         // depends on unconverted modules: currysrc
+		"android_icu4j_srcgen_binary",  // depends on unconverted modules: android_icu4j_srcgen, currysrc
+		"apex_manifest_proto_java",     // b/210751803, depends on libprotobuf-java-full
+		"art-script",                   // depends on unconverted modules: dalvikvm, dex2oat
+		"bin2c_fastdeployagent",        // depends on unconverted modules: deployagent
+		"chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
+		"com.android.runtime",                                        // depends on unconverted modules: bionic-linker-config, linkerconfig
+		"conv_linker_config",                                         // depends on unconverted modules: linker_config_proto
+		"currysrc",                                                   // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
+		"dex2oat-script",                                             // depends on unconverted modules: dex2oat
+		"generated_android_icu4j_resources",                          // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+		"generated_android_icu4j_test_resources",                     // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+		"host-libprotobuf-java-nano",                                 // b/220869005, depends on libprotobuf-java-nano
+		"libadb_host",                                                // depends on unconverted modules: AdbWinApi, libopenscreen-discovery, libopenscreen-platform-impl, libusb
+		"libart",                                                     // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
+		"libart-runtime-gtest",                                       // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libartbase-art-gtest
+		"libart_headers",                                             // depends on unconverted modules: art_libartbase_headers
+		"libartd",                                                    // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
+		"libartd-runtime-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest
+		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
+		"libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
+		"libdexfile",                                              // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
+		"libdexfile_static",                                       // depends on unconverted modules: libartbase, libdexfile
+		"libdexfiled",                                             // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
+		"libfastdeploy_host",                                      // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
+		"libgmock_main_ndk",                                       // depends on unconverted modules: libgtest_ndk_c++
+		"libgmock_ndk",                                            // depends on unconverted modules: libgtest_ndk_c++
+		"libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libnativetesthelper_jni, libgmock_ndk
+		"libnativetesthelper_jni",   // depends on unconverted modules: libgtest_ndk_c++
+		"libprotobuf-java-nano",     // b/220869005, depends on non-public_current SDK
+		"libstatslog",               // depends on unconverted modules: libstatspull, statsd-aidl-ndk, libbinder_ndk
+		"libstatslog_art",           // depends on unconverted modules: statslog_art.cpp, statslog_art.h
+		"linker_reloc_bench_main",   // depends on unconverted modules: liblinker_reloc_bench_*
+		"pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
+		"robolectric-sqlite4java-0.282",             // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
+		"static_crasher",                            // depends on unconverted modules: libdebuggerd_handler
+		"stats-log-api-gen",                         // depends on unconverted modules: libstats_proto_host
+		"statslog.cpp", "statslog.h", "statslog.rs", // depends on unconverted modules: stats-log-api-gen
+		"statslog_art.cpp", "statslog_art.h", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
+		"timezone-host",       // depends on unconverted modules: art.module.api.annotations
+		"truth-host-prebuilt", // depends on unconverted modules: truth-prebuilt
+		"truth-prebuilt",      // depends on unconverted modules: asm-7.0, guava
+	}
+
+	Bp2buildCcLibraryStaticOnlyList = []string{}
+
+	MixedBuildsDisabledList = []string{
+		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+
+		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
+		"minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
+
+		"cap_names.h",                                  // TODO(b/204913827) runfiles need to be handled in mixed builds
+		"libcap",                                       // TODO(b/204913827) runfiles need to be handled in mixed builds
+		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
+
+		// Depends on libprotobuf-cpp-*
+		"libadb_pairing_connection",
+		"libadb_pairing_connection_static",
+		"libadb_pairing_server", "libadb_pairing_server_static",
+
+		// TODO(b/204811222) support suffix in cc_binary
+		"acvp_modulewrapper",
+		"android.hardware.media.c2@1.0-service-v4l2",
+		"app_process",
+		"bar_test",
+		"bench_cxa_atexit",
+		"bench_noop",
+		"bench_noop_nostl",
+		"bench_noop_static",
+		"boringssl_self_test",
+		"boringssl_self_test_vendor",
+		"bssl",
+		"cavp",
+		"crash_dump",
+		"crasher",
+		"libcxx_test_template",
+		"linker",
+		"memory_replay",
+		"native_bridge_guest_linker",
+		"native_bridge_stub_library_defaults",
+		"noop",
+		"simpleperf_ndk",
+		"toybox-static",
+		"zlib_bench",
+	}
+)
diff --git a/android/apex.go b/android/apex.go
index cf1bcfe..b127f74 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -113,9 +113,6 @@
 	for _, sdk := range i.RequiredSdks {
 		name += "_" + sdk.Name + "_" + sdk.Version
 	}
-	if i.UsePlatformApis {
-		name += "_private"
-	}
 	return name
 }
 
@@ -546,10 +543,9 @@
 			merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...)
 			merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
 			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
-			if merged[index].UsePlatformApis != apexInfo.UsePlatformApis {
-				panic(fmt.Errorf("variants having different UsePlatformApis can't be merged"))
-			}
-			merged[index].UsePlatformApis = apexInfo.UsePlatformApis
+			// Platform APIs is allowed for this module only when all APEXes containing
+			// the module are with `use_platform_apis: true`.
+			merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis
 		} else {
 			seen[mergedName] = len(merged)
 			apexInfo.ApexVariationName = mergedName
diff --git a/android/apex_test.go b/android/apex_test.go
index 60a639b..1e2f3bd 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -118,17 +118,30 @@
 			},
 		},
 		{
-			name: "don't merge different UsePlatformApis",
+			name: "merge different UsePlatformApis but don't allow using platform api",
 			in: []ApexInfo{
 				{"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
 				{"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_private", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
-				{"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
-				{"bar", "apex10000_private"},
+				{"bar", "apex10000"},
+				{"foo", "apex10000"},
+			},
+		},
+		{
+			name: "merge same UsePlatformApis and allow using platform api",
+			in: []ApexInfo{
+				{"foo", FutureApiLevel, false, true, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000", FutureApiLevel, false, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex10000"},
 				{"foo", "apex10000"},
 			},
 		},
diff --git a/android/api_levels.go b/android/api_levels.go
index 27a3b7f..8163894 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"strconv"
 
+	"android/soong/bazel"
 	"android/soong/starlark_fmt"
 )
 
@@ -393,10 +394,10 @@
 }
 
 func StarlarkApiLevelConfigs(config Config) string {
-	return fmt.Sprintf(`# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+	return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
 _api_levels = %s
 
 api_levels = _api_levels
 `, printApiLevelsStarlarkDict(config),
 	)
-}
\ No newline at end of file
+}
diff --git a/android/arch.go b/android/arch.go
index 6b81022..f732a7d 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1199,14 +1199,6 @@
 				if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
 					mergePropertyStruct(ctx, genProps, bionicProperties)
 				}
-
-				// Special case:  to ease the transition from glibc to musl, apply linux_glibc
-				// properties (which has historically mean host linux) to musl variants.
-				field = "Linux_glibc"
-				prefix = "target.linux_glibc"
-				if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
-					mergePropertyStruct(ctx, genProps, bionicProperties)
-				}
 			}
 
 			// Handle target OS properties in the form:
@@ -1426,14 +1418,6 @@
 			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
 				result = append(result, osArchProperties)
 			}
-
-			// Special case:  to ease the transition from glibc to musl, apply linux_glibc
-			// properties (which has historically mean host linux) to musl variants.
-			field = "Linux_glibc_" + archType.Name
-			userFriendlyField = "target.linux_glibc_" + archType.Name
-			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
-				result = append(result, osArchProperties)
-			}
 		}
 	}
 
diff --git a/android/arch_test.go b/android/arch_test.go
index 68dc7f5..dd0b115 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -606,12 +606,12 @@
 				{
 					module:   "foo",
 					variant:  "linux_musl_x86_64",
-					property: []string{"root", "host", "linux", "host_linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64", "linux_glibc_x86_64"},
+					property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64"},
 				},
 				{
 					module:   "foo",
 					variant:  "linux_musl_x86",
-					property: []string{"root", "host", "linux", "host_linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86", "linux_glibc_x86"},
+					property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86"},
 				},
 			},
 		},
diff --git a/android/bazel.go b/android/bazel.go
index af5de12..4ef8d78 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -24,6 +24,15 @@
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android/allowlists"
+)
+
+const (
+	// A sentinel value to be used as a key in Bp2BuildConfig for modules with
+	// no package path. This is also the module dir for top level Android.bp
+	// modules.
+	Bp2BuildTopLevel = "."
 )
 
 type bazelModuleProperties struct {
@@ -87,7 +96,7 @@
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
 	ShouldConvertWithBp2build(ctx BazelConversionContext) bool
-	shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
+	shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
 	ConvertWithBp2build(ctx TopDownMutatorContext)
 
@@ -161,231 +170,13 @@
 	return "" // no label for unconverted module
 }
 
-// Configuration to decide if modules in a directory should default to true/false for bp2build_available
-type Bp2BuildConfig map[string]BazelConversionConfigEntry
-type BazelConversionConfigEntry int
+type bp2BuildConversionAllowlist struct {
+	// Configure modules in these directories to enable bp2build_available: true or false by default.
+	defaultConfig allowlists.Bp2BuildConfig
 
-const (
-	// A sentinel value to be used as a key in Bp2BuildConfig for modules with
-	// no package path. This is also the module dir for top level Android.bp
-	// modules.
-	BP2BUILD_TOPLEVEL = "."
-
-	// iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
-	// which can also mean that the key doesn't exist in a lookup.
-
-	// all modules in this package and subpackages default to bp2build_available: true.
-	// allows modules to opt-out.
-	Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
-
-	// all modules in this package (not recursively) default to bp2build_available: true.
-	// allows modules to opt-out.
-	Bp2BuildDefaultTrue
-
-	// all modules in this package (not recursively) default to bp2build_available: false.
-	// allows modules to opt-in.
-	Bp2BuildDefaultFalse
-)
-
-var (
 	// Keep any existing BUILD files (and do not generate new BUILD files) for these directories
 	// in the synthetic Bazel workspace.
-	bp2buildKeepExistingBuildFile = map[string]bool{
-		// This is actually build/bazel/build.BAZEL symlinked to ./BUILD
-		".":/*recursive = */ false,
-
-		// build/bazel/examples/apex/... BUILD files should be generated, so
-		// build/bazel is not recursive. Instead list each subdirectory under
-		// build/bazel explicitly.
-		"build/bazel":/* recursive = */ false,
-		"build/bazel/ci/dist":/* recursive = */ false,
-		"build/bazel/examples/android_app":/* recursive = */ true,
-		"build/bazel/examples/java":/* recursive = */ true,
-		"build/bazel/bazel_skylib":/* recursive = */ true,
-		"build/bazel/rules":/* recursive = */ true,
-		"build/bazel/rules_cc":/* recursive = */ true,
-		"build/bazel/scripts":/* recursive = */ true,
-		"build/bazel/tests":/* recursive = */ true,
-		"build/bazel/platforms":/* recursive = */ true,
-		"build/bazel/product_variables":/* recursive = */ true,
-		"build/bazel/vendor/google":/* recursive = */ true,
-		"build/bazel_common_rules":/* recursive = */ true,
-		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
-		"build/make/tools":/* recursive = */ false,
-		"build/pesto":/* recursive = */ true,
-
-		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
-		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
-		"external/bazelbuild-rules_android":/* recursive = */ true,
-		"external/bazel-skylib":/* recursive = */ true,
-		"external/guava":/* recursive = */ true,
-		"external/jsr305":/* recursive = */ true,
-		"frameworks/ex/common":/* recursive = */ true,
-
-		"packages/apps/Music":/* recursive = */ true,
-		"packages/apps/QuickSearchBox":/* recursive = */ true,
-		"packages/apps/WallpaperPicker":/* recursive = */ false,
-
-		"prebuilts/bundletool":/* recursive = */ true,
-		"prebuilts/gcc":/* recursive = */ true,
-		"prebuilts/build-tools":/* recursive = */ false,
-		"prebuilts/jdk/jdk11":/* recursive = */ false,
-		"prebuilts/sdk":/* recursive = */ false,
-		"prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
-		"prebuilts/sdk/current/support":/* recursive = */ false,
-		"prebuilts/sdk/tools":/* recursive = */ false,
-		"prebuilts/r8":/* recursive = */ false,
-	}
-
-	// Configure modules in these directories to enable bp2build_available: true or false by default.
-	bp2buildDefaultConfig = Bp2BuildConfig{
-		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
-		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
-		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
-		"art/tools":                             Bp2BuildDefaultTrue,
-		"bionic":                                Bp2BuildDefaultTrueRecursively,
-		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
-		"build/bazel/examples/soong_config_variables":        Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/apex/minimal":                  Bp2BuildDefaultTrueRecursively,
-		"build/make/tools/signapk":                           Bp2BuildDefaultTrue,
-		"build/make/target/product/security":                 Bp2BuildDefaultTrue,
-		"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/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/error_prone":                               Bp2BuildDefaultTrueRecursively,
-		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
-		"external/google-benchmark":                          Bp2BuildDefaultTrueRecursively,
-		"external/googletest":                                Bp2BuildDefaultTrueRecursively,
-		"external/gwp_asan":                                  Bp2BuildDefaultTrueRecursively,
-		"external/icu":                                       Bp2BuildDefaultTrueRecursively,
-		"external/icu/android_icu4j":                         Bp2BuildDefaultFalse, // java rules incomplete
-		"external/icu/icu4j":                                 Bp2BuildDefaultFalse, // java rules incomplete
-		"external/javapoet":                                  Bp2BuildDefaultTrueRecursively,
-		"external/jemalloc_new":                              Bp2BuildDefaultTrueRecursively,
-		"external/jsoncpp":                                   Bp2BuildDefaultTrueRecursively,
-		"external/libcap":                                    Bp2BuildDefaultTrueRecursively,
-		"external/libcxx":                                    Bp2BuildDefaultTrueRecursively,
-		"external/libcxxabi":                                 Bp2BuildDefaultTrueRecursively,
-		"external/libevent":                                  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/scudo":                                     Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libselinux":                        Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libsepol":                          Bp2BuildDefaultTrueRecursively,
-		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
-		"external/zstd":                                      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/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,
-		"prebuilts/sdk/tools/jetifier/jetifier-standalone":   Bp2BuildDefaultTrue,
-		"system/apex":                                        Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
-		"system/apex/proto":                                  Bp2BuildDefaultTrueRecursively,
-		"system/apex/libs":                                   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/libbase":                                     Bp2BuildDefaultTrueRecursively,
-		"system/libprocinfo":                                 Bp2BuildDefaultTrue,
-		"system/libziparchive":                               Bp2BuildDefaultTrueRecursively,
-		"system/logging/liblog":                              Bp2BuildDefaultTrueRecursively,
-		"system/sepolicy/apex":                               Bp2BuildDefaultTrueRecursively,
-		"system/timezone/apex":                               Bp2BuildDefaultTrueRecursively,
-		"system/timezone/output_data":                        Bp2BuildDefaultTrueRecursively,
-		"system/unwinding/libbacktrace":                      Bp2BuildDefaultTrueRecursively,
-		"system/unwinding/libunwindstack":                    Bp2BuildDefaultTrueRecursively,
-		"tools/apksig":                                       Bp2BuildDefaultTrue,
-		"tools/platform-compat/java/android/compat":          Bp2BuildDefaultTrueRecursively,
-	}
+	keepExistingBuildFile map[string]bool
 
 	// Per-module allowlist to always opt modules in of both bp2build and mixed builds.
 	// These modules are usually in directories with many other modules that are not ready for
@@ -393,278 +184,150 @@
 	//
 	// A module can either be in this list or its directory allowlisted entirely
 	// in bp2buildDefaultConfig, but not both at the same time.
-	bp2buildModuleAlwaysConvertList = []string{
-		//external/avb
-		"avbtool",
-		"libavb",
-		"avb_headers",
-
-		//external/fec
-		"libfec_rs",
-
-		//system/core/libsparse
-		"libsparse",
-
-		//system/extras/ext4_utils
-		"libext4_utils",
-
-		//system/extras/libfec
-		"libfec",
-
-		//system/extras/squashfs_utils
-		"libsquashfs_utils",
-
-		//system/extras/verity/fec
-		"fec",
-
-		//packages/apps/Car/libs/car-ui-lib/car-ui-androidx
-		// genrule dependencies for java_imports
-		"car-ui-androidx-annotation-nodeps",
-		"car-ui-androidx-collection-nodeps",
-		"car-ui-androidx-core-common-nodeps",
-		"car-ui-androidx-lifecycle-common-nodeps",
-		"car-ui-androidx-constraintlayout-solver-nodeps",
-	}
+	moduleAlwaysConvert map[string]bool
 
 	// Per-module-type allowlist to always opt modules in to both bp2build and mixed builds
 	// when they have the same type as one listed.
-	bp2buildModuleTypeAlwaysConvertList = []string{
-		"java_import",
-		"java_import_host",
-	}
+	moduleTypeAlwaysConvert map[string]bool
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
-	bp2buildModuleDoNotConvertList = []string{
-		"libnativehelper_compat_libc", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
-
-		"libart",                             // depends on unconverted modules: art_operator_srcs, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfile, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbase, libprofile, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api
-		"libart-runtime-gtest",               // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libbacktrace, libartbase-art-gtest
-		"libart_headers",                     // depends on unconverted modules: art_libartbase_headers
-		"libartd",                            // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api, art_operator_srcs, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfiled, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbased, libprofiled, cpp-define-generator-asm-support
-		"libartd-runtime-gtest",              // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libbacktrace, libartbased-art-gtest
-		"libstatslog_art",                    // depends on unconverted modules: statslog_art.cpp, statslog_art.h
-		"statslog_art.h", "statslog_art.cpp", // depends on unconverted modules: stats-log-api-gen
-
-		"libandroid_runtime_lazy", // depends on unconverted modules: libbinder_headers
-		"libcmd",                  // depends on unconverted modules: libbinder
-
-		"libdexfile_support_static",                                                       // Depends on unconverted module: libdexfile_external_headers
-		"libunwindstack_local", "libunwindstack_utils", "libc_malloc_debug", "libfdtrack", // Depends on unconverted module: libunwindstack
-
-		"libdexfile_support",          // TODO(b/210546943): Enabled based on product variables.
-		"libdexfile_external_headers", // TODO(b/210546943): Enabled based on product variables.
-
-		"libunwindstack",                // Depends on unconverted module libdexfile_support.
-		"libnativehelper_compat_libc++", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
-
-		"chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
-
-		"libsepol", // TODO(b/207408632): Unsupported case of .l sources in cc library rules
-
-		"gen-kotlin-build-file.py", // module has same name as source
-
-		"libactivitymanager_aidl", // TODO(b/207426160): Depends on activity_manager_procstate_aidl, which is an aidl filegroup.
-
-		"libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libgmock_ndk
-		"libnativetesthelper_jni", "libgmock_main_ndk", "libgmock_ndk", // depends on unconverted module: libgtest_ndk_c++
-
-		"statslog-framework-java-gen", "statslog.cpp", "statslog.h", "statslog.rs", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
-
-		"stats-log-api-gen", // depends on unconverted modules: libstats_proto_host, libprotobuf-cpp-full
-
-		"libstatslog", // depends on unconverted modules: statslog.cpp, statslog.h, ...
-
-		"cmd",                                                        // depends on unconverted module packagemanager_aidl-cpp, of unsupported type aidl_interface
-		"servicedispatcher",                                          // depends on unconverted module android.debug_aidl, of unsupported type aidl_interface
-		"libutilscallstack",                                          // depends on unconverted module libbacktrace
-		"libbacktrace",                                               // depends on unconverted module libunwindstack
-		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
-		"libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
-		"unwind_for_offline", // depends on unconverted module libunwindstack_utils
-		"libdebuggerd",       // depends on unconverted modules libdexfile_support, libunwindstack, gwp_asan_crash_handler, libtombstone_proto, libprotobuf-cpp-lite
-		"libdexfile_static",  // depends on libartpalette, libartbase, libdexfile, which are of unsupported type: art_cc_library.
-
-		"static_crasher", // depends on unconverted modules: libdebuggerd_handler
-
-		"pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
-
-		"libbase_ndk", // http://b/186826477, fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
-
-		"libprotobuf-internal-protos",      // b/210751803, we don't handle path property for filegroups
-		"libprotobuf-internal-python-srcs", // b/210751803, we don't handle path property for filegroups
-		"libprotobuf-java-full",            // b/210751803, we don't handle path property for filegroups
-		"host-libprotobuf-java-full",       // b/210751803, we don't handle path property for filegroups
-		"libprotobuf-java-util-full",       // b/210751803, we don't handle path property for filegroups
-		"apex_manifest_proto_java",         // b/210751803, depends on libprotobuf-java-full
-		"conscrypt",                        // b/210751803, we don't handle path property for filegroups
-		"conscrypt-for-host",               // b/210751803, we don't handle path property for filegroups
-
-		"libprotobuf-java-nano",      // b/220869005, depends on non-public_current SDK
-		"host-libprotobuf-java-nano", // b/220869005, depends on libprotobuf-java-nano
-
-		"libc_musl_sysroot_bionic_arch_headers", // b/218405924, depends on soong_zip
-		"libc_musl_sysroot_bionic_headers",      // b/218405924, depends on soong_zip and generates duplicate srcs
-
-		// python protos
-		"libprotobuf-python",                           // contains .proto sources
-		"conv_linker_config",                           // depends on linker_config_proto, a python lib with proto sources
-		"apex_build_info_proto", "apex_manifest_proto", // a python lib with proto sources
-		"linker_config_proto", // contains .proto sources
-
-		"brotli-fuzzer-corpus", // b/202015218: outputs are in location incompatible with bazel genrule handling.
-
-		// python modules
-		"analyze_bcpf", // depends on bpmodify a blueprint_go_binary.
-
-		// b/203369847: multiple genrules in the same package creating the same file
-		// //development/sdk/...
-		"platform_tools_properties",
-		"build_tools_source_properties",
-
-		// APEX support
-		"com.android.runtime", // depends on unconverted modules: bionic-linker-config, linkerconfig
-
-		"libgtest_ndk_c++",      // b/201816222: Requires sdk_version support.
-		"libgtest_main_ndk_c++", // b/201816222: Requires sdk_version support.
-
-		"abb",                     // depends on unconverted modules: libcmd, libbinder
-		"adb",                     // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
-		"libadb_host",             // depends on unconverted modules: libopenscreen-discovery, libopenscreen-platform-impl, libusb, AdbWinApi
-		"libfastdeploy_host",      // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
-		"linker",                  // depends on unconverted modules: libdebuggerd_handler_fallback
-		"linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
-		"versioner",               // depends on unconverted modules: libclang_cxx_host, libLLVM_host, of unsupported type llvm_host_prebuilt_library_shared
-
-		"linkerconfig", // http://b/202876379 has arch-variant static_executable
-		"mdnsd",        // http://b/202876379 has arch-variant static_executable
-
-		"CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib
-
-		"libdexfile",  // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
-		"libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
-
-		// go deps:
-		"apex-protos",                                                                                // depends on soong_zip, a go binary
-		"generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // depends on unconverted modules: soong_zip
-		"host_bionic_linker_asm",                                                  // depends on extract_linker, a go binary.
-		"host_bionic_linker_script",                                               // depends on extract_linker, a go binary.
-		"robolectric-sqlite4java-native",                                          // depends on soong_zip, a go binary
-		"robolectric_tzdata",                                                      // depends on soong_zip, a go binary
-		"libc_musl_sysroot_libc++_headers", "libc_musl_sysroot_libc++abi_headers", // depends on soong_zip, zip2zip
-
-		"android_icu4j_srcgen_binary", // Bazel build error: deps not allowed without srcs; move to runtime_deps
-		"core-icu4j-for-host",         // Bazel build error: deps not allowed without srcs; move to runtime_deps
-
-		// java deps
-		"android_icu4j_srcgen",          // depends on unconverted modules: currysrc
-		"bin2c_fastdeployagent",         // depends on deployagent, a java binary
-		"currysrc",                      // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
-		"robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
-		"timezone-host",                 // depends on unconverted modules: art.module.api.annotations
-		"truth-host-prebuilt",           // depends on unconverted modules: truth-prebuilt
-		"truth-prebuilt",                // depends on unconverted modules: asm-7.0, guava
-
-		"generated_android_icu4j_resources",      // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
-		"generated_android_icu4j_test_resources", // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
-
-		"art-script",     // depends on unconverted modules: dalvikvm, dex2oat
-		"dex2oat-script", // depends on unconverted modules: dex2oat
-
-		"prebuilt_car-ui-androidx-core-common",         // b/224773339, genrule dependency creates an .aar, not a .jar
-		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
-		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
-
-		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
-	}
+	moduleDoNotConvert map[string]bool
 
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
-	bp2buildCcLibraryStaticOnlyList = []string{}
+	ccLibraryStaticOnly map[string]bool
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
-	mixedBuildsDisabledList = []string{
-		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+	mixedBuildsDisabled map[string]bool
+}
 
-		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
-		"minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
-
-		"cap_names.h",                                  // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libcap",                                       // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
-
-		// Depends on libprotobuf-cpp-*
-		"libadb_pairing_connection",
-		"libadb_pairing_connection_static",
-		"libadb_pairing_server", "libadb_pairing_server_static",
-
-		// TODO(b/204811222) support suffix in cc_binary
-		"acvp_modulewrapper",
-		"android.hardware.media.c2@1.0-service-v4l2",
-		"app_process",
-		"bar_test",
-		"bench_cxa_atexit",
-		"bench_noop",
-		"bench_noop_nostl",
-		"bench_noop_static",
-		"boringssl_self_test",
-		"boringssl_self_test_vendor",
-		"bssl",
-		"cavp",
-		"crash_dump",
-		"crasher",
-		"libcxx_test_template",
-		"linker",
-		"memory_replay",
-		"native_bridge_guest_linker",
-		"native_bridge_stub_library_defaults",
-		"noop",
-		"simpleperf_ndk",
-		"toybox-static",
-		"zlib_bench",
-	}
-
-	// Used for quicker lookups
-	bp2buildModuleDoNotConvert      = map[string]bool{}
-	bp2buildModuleAlwaysConvert     = map[string]bool{}
-	bp2buildModuleTypeAlwaysConvert = map[string]bool{}
-	bp2buildCcLibraryStaticOnly     = map[string]bool{}
-	mixedBuildsDisabled             = map[string]bool{}
-)
-
-func init() {
-	for _, moduleName := range bp2buildModuleAlwaysConvertList {
-		bp2buildModuleAlwaysConvert[moduleName] = true
-	}
-
-	for _, moduleType := range bp2buildModuleTypeAlwaysConvertList {
-		bp2buildModuleTypeAlwaysConvert[moduleType] = true
-	}
-
-	for _, moduleName := range bp2buildModuleDoNotConvertList {
-		bp2buildModuleDoNotConvert[moduleName] = true
-	}
-
-	for _, moduleName := range bp2buildCcLibraryStaticOnlyList {
-		bp2buildCcLibraryStaticOnly[moduleName] = true
-	}
-
-	for _, moduleName := range mixedBuildsDisabledList {
-		mixedBuildsDisabled[moduleName] = true
+// NewBp2BuildAllowlist creates a new, empty bp2BuildConversionAllowlist
+// which can be populated using builder pattern Set* methods
+func NewBp2BuildAllowlist() bp2BuildConversionAllowlist {
+	return bp2BuildConversionAllowlist{
+		allowlists.Bp2BuildConfig{},
+		map[string]bool{},
+		map[string]bool{},
+		map[string]bool{},
+		map[string]bool{},
+		map[string]bool{},
+		map[string]bool{},
 	}
 }
 
+// SetDefaultConfig copies the entries from defaultConfig into the allowlist
+func (a bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) bp2BuildConversionAllowlist {
+	if a.defaultConfig == nil {
+		a.defaultConfig = allowlists.Bp2BuildConfig{}
+	}
+	for k, v := range defaultConfig {
+		a.defaultConfig[k] = v
+	}
+
+	return a
+}
+
+// SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
+func (a bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) bp2BuildConversionAllowlist {
+	if a.keepExistingBuildFile == nil {
+		a.keepExistingBuildFile = map[string]bool{}
+	}
+	for k, v := range keepExistingBuildFile {
+		a.keepExistingBuildFile[k] = v
+	}
+
+	return a
+}
+
+// SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
+func (a bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) bp2BuildConversionAllowlist {
+	if a.moduleAlwaysConvert == nil {
+		a.moduleAlwaysConvert = map[string]bool{}
+	}
+	for _, m := range moduleAlwaysConvert {
+		a.moduleAlwaysConvert[m] = true
+	}
+
+	return a
+}
+
+// SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
+func (a bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) bp2BuildConversionAllowlist {
+	if a.moduleTypeAlwaysConvert == nil {
+		a.moduleTypeAlwaysConvert = map[string]bool{}
+	}
+	for _, m := range moduleTypeAlwaysConvert {
+		a.moduleTypeAlwaysConvert[m] = true
+	}
+
+	return a
+}
+
+// SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
+func (a bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) bp2BuildConversionAllowlist {
+	if a.moduleDoNotConvert == nil {
+		a.moduleDoNotConvert = map[string]bool{}
+	}
+	for _, m := range moduleDoNotConvert {
+		a.moduleDoNotConvert[m] = true
+	}
+
+	return a
+}
+
+// SetCcLibraryStaticOnlyList copies the entries from ccLibraryStaticOnly into the allowlist
+func (a bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) bp2BuildConversionAllowlist {
+	if a.ccLibraryStaticOnly == nil {
+		a.ccLibraryStaticOnly = map[string]bool{}
+	}
+	for _, m := range ccLibraryStaticOnly {
+		a.ccLibraryStaticOnly[m] = true
+	}
+
+	return a
+}
+
+// SetMixedBuildsDisabledList copies the entries from mixedBuildsDisabled into the allowlist
+func (a bp2BuildConversionAllowlist) SetMixedBuildsDisabledList(mixedBuildsDisabled []string) bp2BuildConversionAllowlist {
+	if a.mixedBuildsDisabled == nil {
+		a.mixedBuildsDisabled = map[string]bool{}
+	}
+	for _, m := range mixedBuildsDisabled {
+		a.mixedBuildsDisabled[m] = true
+	}
+
+	return a
+}
+
+var bp2buildAllowlist = NewBp2BuildAllowlist().
+	SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
+	SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
+	SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
+	SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
+	SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
+	SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList).
+	SetMixedBuildsDisabledList(allowlists.MixedBuildsDisabledList)
+
+// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
+// generate a static version of itself based on the current global configuration.
 func GenerateCcLibraryStaticOnly(moduleName string) bool {
-	return bp2buildCcLibraryStaticOnly[moduleName]
+	return bp2buildAllowlist.ccLibraryStaticOnly[moduleName]
 }
 
+// ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
+// added to the build symlink forest based on the current global configuration.
 func ShouldKeepExistingBuildFileForDir(dir string) bool {
-	if _, ok := bp2buildKeepExistingBuildFile[dir]; ok {
+	return shouldKeepExistingBuildFileForDir(bp2buildAllowlist, dir)
+}
+
+func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, dir string) bool {
+	if _, ok := allowlist.keepExistingBuildFile[dir]; ok {
 		// Exact dir match
 		return true
 	}
 	// Check if subtree match
-	for prefix, recursive := range bp2buildKeepExistingBuildFile {
+	for prefix, recursive := range allowlist.keepExistingBuildFile {
 		if recursive {
 			if strings.HasPrefix(dir, prefix+"/") {
 				return true
@@ -698,7 +361,7 @@
 		// variants of a cc_library.
 		return false
 	}
-	return !mixedBuildsDisabled[ctx.Module().Name()]
+	return !bp2buildAllowlist.mixedBuildsDisabled[ctx.Module().Name()]
 }
 
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -710,53 +373,66 @@
 	return b.shouldConvertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
 }
 
-// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
+// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build
 func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx BazelConversionContext) bool {
 	return b.shouldConvertWithBp2build(ctx, ctx.Module())
 }
 
-func (b *BazelModuleBase) shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool {
-	moduleName := module.Name()
-	moduleNameAllowed := bp2buildModuleAlwaysConvert[moduleName]
-	moduleTypeAllowed := bp2buildModuleTypeAlwaysConvert[ctx.OtherModuleType(module)]
-	allowlistConvert := moduleNameAllowed || moduleTypeAllowed
-	if moduleNameAllowed && moduleTypeAllowed {
-		ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in bp2buildModuleAlwaysConvert and also be" +
-			" in bp2buildModuleTypeAlwaysConvert")
-	}
+type bazelOtherModuleContext interface {
+	ModuleErrorf(format string, args ...interface{})
+	Config() Config
+	OtherModuleType(m blueprint.Module) string
+	OtherModuleName(m blueprint.Module) string
+	OtherModuleDir(m blueprint.Module) string
+}
 
-	if bp2buildModuleDoNotConvert[moduleName] {
-		if moduleNameAllowed {
-			ctx.(BaseModuleContext).ModuleErrorf("a module cannot be in bp2buildModuleDoNotConvert" +
-				" and also be in bp2buildModuleAlwaysConvert")
-		}
-		return false
-	}
-
+func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool {
 	if !b.bazelProps().Bazel_module.CanConvertToBazel {
 		return false
 	}
 
 	propValue := b.bazelProperties.Bazel_module.Bp2build_available
 	packagePath := ctx.OtherModuleDir(module)
+
 	// Modules in unit tests which are enabled in the allowlist by type or name
 	// trigger this conditional because unit tests run under the "." package path
-	isTestModule := packagePath == "." && proptools.BoolDefault(propValue, false)
-	if allowlistConvert && !isTestModule && ShouldKeepExistingBuildFileForDir(packagePath) {
+	isTestModule := packagePath == Bp2BuildTopLevel && proptools.BoolDefault(propValue, false)
+	if isTestModule {
+		return true
+	}
+
+	moduleName := module.Name()
+	allowlist := ctx.Config().bp2buildPackageConfig
+	moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
+	moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
+	allowlistConvert := moduleNameAllowed || moduleTypeAllowed
+	if moduleNameAllowed && moduleTypeAllowed {
+		ctx.ModuleErrorf("A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert")
+		return false
+	}
+
+	if allowlist.moduleDoNotConvert[moduleName] {
 		if moduleNameAllowed {
-			ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in a directory listed in bp2buildKeepExistingBuildFile"+
-				" and also be in bp2buildModuleAlwaysConvert. Directory: '%s'", packagePath)
+			ctx.ModuleErrorf("a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert")
 		}
 		return false
 	}
 
-	config := ctx.Config().bp2buildPackageConfig
-	// This is a tristate value: true, false, or unset.
-	if bp2buildDefaultTrueRecursively(packagePath, config) {
+	if allowlistConvert && shouldKeepExistingBuildFileForDir(allowlist, packagePath) {
 		if moduleNameAllowed {
-			ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
-				" or Bp2BuildDefaultTrueRecursively and also be in bp2buildModuleAlwaysConvert. Directory: '%s'",
-				packagePath)
+			ctx.ModuleErrorf("A module cannot be in a directory listed in keepExistingBuildFile"+
+				" and also be in moduleAlwaysConvert. Directory: '%s'", packagePath)
+			return false
+		}
+	}
+
+	// This is a tristate value: true, false, or unset.
+	if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
+		if moduleNameAllowed {
+			ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
+				" or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'",
+				directoryPath)
+			return false
 		}
 
 		// Allow modules to explicitly opt-out.
@@ -777,14 +453,16 @@
 //
 // This function will also return false if the package doesn't match anything in
 // the config.
-func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
-	ret := false
-
+//
+// This function will also return the allowlist entry which caused a particular
+// package to be enabled. Since packages can be enabled via a recursive declaration,
+// the path returned will not always be the same as the one provided.
+func bp2buildDefaultTrueRecursively(packagePath string, config allowlists.Bp2BuildConfig) (bool, string) {
 	// Check if the package path has an exact match in the config.
-	if config[packagePath] == Bp2BuildDefaultTrue || config[packagePath] == Bp2BuildDefaultTrueRecursively {
-		return true
-	} else if config[packagePath] == Bp2BuildDefaultFalse {
-		return false
+	if config[packagePath] == allowlists.Bp2BuildDefaultTrue || config[packagePath] == allowlists.Bp2BuildDefaultTrueRecursively {
+		return true, packagePath
+	} else if config[packagePath] == allowlists.Bp2BuildDefaultFalse {
+		return false, packagePath
 	}
 
 	// If not, check for the config recursively.
@@ -792,15 +470,15 @@
 	// e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
 	for _, part := range strings.Split(packagePath, "/") {
 		packagePrefix += part
-		if config[packagePrefix] == Bp2BuildDefaultTrueRecursively {
+		if config[packagePrefix] == allowlists.Bp2BuildDefaultTrueRecursively {
 			// package contains this prefix and this prefix should convert all modules
-			return true
+			return true, packagePrefix
 		}
 		// Continue to the next part of the package dir.
 		packagePrefix += "/"
 	}
 
-	return ret
+	return false, packagePath
 }
 
 // GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index f353a9d..fa10f62 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -79,6 +79,7 @@
 	OtherModuleType(m blueprint.Module) string
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
+	ModuleErrorf(format string, args ...interface{})
 }
 
 // A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
diff --git a/android/bazel_test.go b/android/bazel_test.go
index e5d8fbb..482df2a 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -13,59 +13,67 @@
 // limitations under the License.
 package android
 
-import "testing"
+import (
+	"android/soong/android/allowlists"
+	"android/soong/bazel"
+	"fmt"
+	"testing"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
 
 func TestConvertAllModulesInPackage(t *testing.T) {
 	testCases := []struct {
-		prefixes   Bp2BuildConfig
+		prefixes   allowlists.Bp2BuildConfig
 		packageDir string
 	}{
 		{
-			prefixes: Bp2BuildConfig{
-				"a": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a/b": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a/b":   Bp2BuildDefaultTrueRecursively,
-				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a/b":   allowlists.Bp2BuildDefaultTrueRecursively,
+				"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":     Bp2BuildDefaultTrueRecursively,
-				"d/e/f": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":     allowlists.Bp2BuildDefaultTrueRecursively,
+				"d/e/f": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":     Bp2BuildDefaultFalse,
-				"a/b":   Bp2BuildDefaultTrueRecursively,
-				"a/b/c": Bp2BuildDefaultFalse,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":     allowlists.Bp2BuildDefaultFalse,
+				"a/b":   allowlists.Bp2BuildDefaultTrueRecursively,
+				"a/b/c": allowlists.Bp2BuildDefaultFalse,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":     Bp2BuildDefaultTrueRecursively,
-				"a/b":   Bp2BuildDefaultFalse,
-				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":     allowlists.Bp2BuildDefaultTrueRecursively,
+				"a/b":   allowlists.Bp2BuildDefaultFalse,
+				"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a",
 		},
 	}
 
 	for _, test := range testCases {
-		if !bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+		if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); !ok {
 			t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes)
 		}
 	}
@@ -73,62 +81,308 @@
 
 func TestModuleOptIn(t *testing.T) {
 	testCases := []struct {
-		prefixes   Bp2BuildConfig
+		prefixes   allowlists.Bp2BuildConfig
 		packageDir string
 	}{
 		{
-			prefixes: Bp2BuildConfig{
-				"a/b": Bp2BuildDefaultFalse,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a/b": allowlists.Bp2BuildDefaultFalse,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":   Bp2BuildDefaultFalse,
-				"a/b": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":   allowlists.Bp2BuildDefaultFalse,
+				"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a/b": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a/b": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a", // opt-in by default
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":     Bp2BuildDefaultTrueRecursively,
-				"d/e/f": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":     allowlists.Bp2BuildDefaultTrueRecursively,
+				"d/e/f": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "foo/bar",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":     Bp2BuildDefaultTrueRecursively,
-				"a/b":   Bp2BuildDefaultFalse,
-				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":     allowlists.Bp2BuildDefaultTrueRecursively,
+				"a/b":   allowlists.Bp2BuildDefaultFalse,
+				"a/b/c": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			packageDir: "a/b",
 		},
 		{
-			prefixes: Bp2BuildConfig{
-				"a":     Bp2BuildDefaultFalse,
-				"a/b":   Bp2BuildDefaultTrueRecursively,
-				"a/b/c": Bp2BuildDefaultFalse,
+			prefixes: allowlists.Bp2BuildConfig{
+				"a":     allowlists.Bp2BuildDefaultFalse,
+				"a/b":   allowlists.Bp2BuildDefaultTrueRecursively,
+				"a/b/c": allowlists.Bp2BuildDefaultFalse,
 			},
 			packageDir: "a",
 		},
 	}
 
 	for _, test := range testCases {
-		if bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+		if ok, _ := bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes); ok {
 			t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
 		}
 	}
 }
+
+type TestBazelModule struct {
+	bazel.TestModuleInfo
+	BazelModuleBase
+}
+
+var _ blueprint.Module = TestBazelModule{}
+
+func (m TestBazelModule) Name() string {
+	return m.TestModuleInfo.ModuleName
+}
+
+func (m TestBazelModule) GenerateBuildActions(blueprint.ModuleContext) {
+}
+
+type TestBazelConversionContext struct {
+	omc       bazel.OtherModuleTestContext
+	allowlist bp2BuildConversionAllowlist
+	errors    []string
+}
+
+var _ bazelOtherModuleContext = &TestBazelConversionContext{}
+
+func (bcc *TestBazelConversionContext) OtherModuleType(m blueprint.Module) string {
+	return bcc.omc.OtherModuleType(m)
+}
+
+func (bcc *TestBazelConversionContext) OtherModuleName(m blueprint.Module) string {
+	return bcc.omc.OtherModuleName(m)
+}
+
+func (bcc *TestBazelConversionContext) OtherModuleDir(m blueprint.Module) string {
+	return bcc.omc.OtherModuleDir(m)
+}
+
+func (bcc *TestBazelConversionContext) ModuleErrorf(format string, args ...interface{}) {
+	bcc.errors = append(bcc.errors, fmt.Sprintf(format, args...))
+}
+
+func (bcc *TestBazelConversionContext) Config() Config {
+	return Config{
+		&config{
+			bp2buildPackageConfig: bcc.allowlist,
+		},
+	}
+}
+
+var bazelableBazelModuleBase = BazelModuleBase{
+	bazelProperties: properties{
+		Bazel_module: bazelModuleProperties{
+			CanConvertToBazel: true,
+		},
+	},
+}
+
+func TestBp2BuildAllowlist(t *testing.T) {
+	testCases := []struct {
+		description    string
+		shouldConvert  bool
+		expectedErrors []string
+		module         TestBazelModule
+		allowlist      bp2BuildConversionAllowlist
+	}{
+		{
+			description:   "allowlist enables module",
+			shouldConvert: true,
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        "dir1",
+				},
+				BazelModuleBase: bazelableBazelModuleBase,
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+			},
+		},
+		{
+			description:    "module in name allowlist and type allowlist fails",
+			shouldConvert:  false,
+			expectedErrors: []string{"A module cannot be in moduleAlwaysConvert and also be in moduleTypeAlwaysConvert"},
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        "dir1",
+				},
+				BazelModuleBase: bazelableBazelModuleBase,
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+				moduleTypeAlwaysConvert: map[string]bool{
+					"rule1": true,
+				},
+			},
+		},
+		{
+			description:    "module in allowlist and denylist fails",
+			shouldConvert:  false,
+			expectedErrors: []string{"a module cannot be in moduleDoNotConvert and also be in moduleAlwaysConvert"},
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        "dir1",
+				},
+				BazelModuleBase: bazelableBazelModuleBase,
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+				moduleDoNotConvert: map[string]bool{
+					"foo": true,
+				},
+			},
+		},
+		{
+			description:    "module in allowlist and existing BUILD file",
+			shouldConvert:  false,
+			expectedErrors: []string{"A module cannot be in a directory listed in keepExistingBuildFile and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        "existing/build/dir",
+				},
+				BazelModuleBase: bazelableBazelModuleBase,
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+				keepExistingBuildFile: map[string]bool{
+					"existing/build/dir": true,
+				},
+			},
+		},
+		{
+			description:    "module allowlist and enabled directory",
+			shouldConvert:  false,
+			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        "existing/build/dir",
+				},
+				BazelModuleBase: bazelableBazelModuleBase,
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+				defaultConfig: allowlists.Bp2BuildConfig{
+					"existing/build/dir": allowlists.Bp2BuildDefaultTrue,
+				},
+			},
+		},
+		{
+			description:    "module allowlist and enabled subdirectory",
+			shouldConvert:  false,
+			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        "existing/build/dir/subdir",
+				},
+				BazelModuleBase: bazelableBazelModuleBase,
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+				defaultConfig: allowlists.Bp2BuildConfig{
+					"existing/build/dir": allowlists.Bp2BuildDefaultTrueRecursively,
+				},
+			},
+		},
+		{
+			description:   "module enabled in unit test short-circuits other allowlists",
+			shouldConvert: true,
+			module: TestBazelModule{
+				TestModuleInfo: bazel.TestModuleInfo{
+					ModuleName: "foo",
+					Typ:        "rule1",
+					Dir:        ".",
+				},
+				BazelModuleBase: BazelModuleBase{
+					bazelProperties: properties{
+						Bazel_module: bazelModuleProperties{
+							CanConvertToBazel:  true,
+							Bp2build_available: proptools.BoolPtr(true),
+						},
+					},
+				},
+			},
+			allowlist: bp2BuildConversionAllowlist{
+				moduleAlwaysConvert: map[string]bool{
+					"foo": true,
+				},
+				moduleDoNotConvert: map[string]bool{
+					"foo": true,
+				},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			bcc := &TestBazelConversionContext{
+				omc: bazel.OtherModuleTestContext{
+					Modules: []bazel.TestModuleInfo{
+						test.module.TestModuleInfo,
+					},
+				},
+				allowlist: test.allowlist,
+			}
+
+			shouldConvert := test.module.shouldConvertWithBp2build(bcc, test.module.TestModuleInfo)
+			if test.shouldConvert != shouldConvert {
+				t.Errorf("Module shouldConvert expected to be: %v, but was: %v", test.shouldConvert, shouldConvert)
+			}
+
+			errorsMatch := true
+			if len(test.expectedErrors) != len(bcc.errors) {
+				errorsMatch = false
+			} else {
+				for i, err := range test.expectedErrors {
+					if err != bcc.errors[i] {
+						errorsMatch = false
+					}
+				}
+			}
+			if !errorsMatch {
+				t.Errorf("Expected errors to be: %v, but were: %v", test.expectedErrors, bcc.errors)
+			}
+		})
+	}
+}
diff --git a/android/config.go b/android/config.go
index 5c41ee8..cb2fc61 100644
--- a/android/config.go
+++ b/android/config.go
@@ -158,7 +158,7 @@
 	mockBpList string
 
 	runningAsBp2Build              bool
-	bp2buildPackageConfig          Bp2BuildConfig
+	bp2buildPackageConfig          bp2BuildConversionAllowlist
 	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
@@ -550,7 +550,7 @@
 	}
 
 	config.BazelContext, err = NewBazelContext(config)
-	config.bp2buildPackageConfig = bp2buildDefaultConfig
+	config.bp2buildPackageConfig = bp2buildAllowlist
 
 	return Config{config}, err
 }
@@ -1360,6 +1360,10 @@
 	return "", false
 }
 
+func (c *deviceConfig) ApexGlobalMinSdkVersionOverride() string {
+	return String(c.config.productVariables.ApexGlobalMinSdkVersionOverride)
+}
+
 func (c *config) IntegerOverflowDisabledForPath(path string) bool {
 	if len(c.productVariables.IntegerOverflowExcludePaths) == 0 {
 		return false
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
new file mode 100644
index 0000000..748be62
--- /dev/null
+++ b/android/config_bp2build.go
@@ -0,0 +1,487 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"fmt"
+	"reflect"
+	"regexp"
+	"sort"
+	"strings"
+
+	"android/soong/bazel"
+	"android/soong/starlark_fmt"
+
+	"github.com/google/blueprint"
+)
+
+// BazelVarExporter is a collection of configuration variables that can be exported for use in Bazel rules
+type BazelVarExporter interface {
+	// asBazel expands strings of configuration variables into their concrete values
+	asBazel(Config, ExportedStringVariables, ExportedStringListVariables, ExportedConfigDependingVariables) []bazelConstant
+}
+
+// ExportedVariables is a collection of interdependent configuration variables
+type ExportedVariables struct {
+	// Maps containing toolchain variables that are independent of the
+	// environment variables of the build.
+	exportedStringVars         ExportedStringVariables
+	exportedStringListVars     ExportedStringListVariables
+	exportedStringListDictVars ExportedStringListDictVariables
+
+	exportedVariableReferenceDictVars ExportedVariableReferenceDictVariables
+
+	/// Maps containing variables that are dependent on the build config.
+	exportedConfigDependingVars ExportedConfigDependingVariables
+
+	pctx PackageContext
+}
+
+// NewExportedVariables creats an empty ExportedVariables struct with non-nil maps
+func NewExportedVariables(pctx PackageContext) ExportedVariables {
+	return ExportedVariables{
+		exportedStringVars:                ExportedStringVariables{},
+		exportedStringListVars:            ExportedStringListVariables{},
+		exportedStringListDictVars:        ExportedStringListDictVariables{},
+		exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{},
+		exportedConfigDependingVars:       ExportedConfigDependingVariables{},
+		pctx:                              pctx,
+	}
+}
+
+func (ev ExportedVariables) asBazel(config Config,
+	stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
+	ret := []bazelConstant{}
+	ret = append(ret, ev.exportedStringVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+	ret = append(ret, ev.exportedStringListVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+	ret = append(ret, ev.exportedStringListDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+	// Note: ExportedVariableReferenceDictVars collections can only contain references to other variables and must be printed last
+	ret = append(ret, ev.exportedVariableReferenceDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
+	return ret
+}
+
+// ExportStringStaticVariable declares a static string variable and exports it to
+// Bazel's toolchain.
+func (ev ExportedVariables) ExportStringStaticVariable(name string, value string) {
+	ev.pctx.StaticVariable(name, value)
+	ev.exportedStringVars.set(name, value)
+}
+
+// ExportStringListStaticVariable declares a static variable and exports it to
+// Bazel's toolchain.
+func (ev ExportedVariables) ExportStringListStaticVariable(name string, value []string) {
+	ev.pctx.StaticVariable(name, strings.Join(value, " "))
+	ev.exportedStringListVars.set(name, value)
+}
+
+// ExportVariableConfigMethod declares a variable whose value is evaluated at
+// runtime via a function with access to the Config and exports it to Bazel's
+// toolchain.
+func (ev ExportedVariables) ExportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
+	ev.exportedConfigDependingVars.set(name, method)
+	return ev.pctx.VariableConfigMethod(name, method)
+}
+
+// ExportSourcePathVariable declares a static "source path" variable and exports
+// it to Bazel's toolchain.
+func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) {
+	ev.pctx.SourcePathVariable(name, value)
+	ev.exportedStringVars.set(name, value)
+}
+
+// ExportVariableFuncVariable declares a variable whose value is evaluated at
+// runtime via a function and exports it to Bazel's toolchain.
+func (ev ExportedVariables) ExportVariableFuncVariable(name string, f func() string) {
+	ev.exportedConfigDependingVars.set(name, func(config Config) string {
+		return f()
+	})
+	ev.pctx.VariableFunc(name, func(PackageVarContext) string {
+		return f()
+	})
+}
+
+// ExportString only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportString(name string, value string) {
+	ev.exportedStringVars.set(name, value)
+}
+
+// ExportStringList only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportStringList(name string, value []string) {
+	ev.exportedStringListVars.set(name, value)
+}
+
+// ExportStringListDict only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportStringListDict(name string, value map[string][]string) {
+	ev.exportedStringListDictVars.set(name, value)
+}
+
+// ExportVariableReferenceDict only exports a variable to Bazel, but does not declare it in Soong
+func (ev ExportedVariables) ExportVariableReferenceDict(name string, value map[string]string) {
+	ev.exportedVariableReferenceDictVars.set(name, value)
+}
+
+// ExportedConfigDependingVariables is a mapping of variable names to functions
+// of type func(config Config) string which return the runtime-evaluated string
+// value of a particular variable
+type ExportedConfigDependingVariables map[string]interface{}
+
+func (m ExportedConfigDependingVariables) set(k string, v interface{}) {
+	m[k] = v
+}
+
+// Ensure that string s has no invalid characters to be generated into the bzl file.
+func validateCharacters(s string) string {
+	for _, c := range []string{`\n`, `"`, `\`} {
+		if strings.Contains(s, c) {
+			panic(fmt.Errorf("%s contains illegal character %s", s, c))
+		}
+	}
+	return s
+}
+
+type bazelConstant struct {
+	variableName       string
+	internalDefinition string
+	sortLast           bool
+}
+
+// ExportedStringVariables is a mapping of variable names to string values
+type ExportedStringVariables map[string]string
+
+func (m ExportedStringVariables) set(k string, v string) {
+	m[k] = v
+}
+
+func (m ExportedStringVariables) asBazel(config Config,
+	stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for k, variableValue := range m {
+		expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
+		if err != nil {
+			panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
+		}
+		if len(expandedVar) > 1 {
+			panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
+		}
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
+		})
+	}
+	return ret
+}
+
+// ExportedStringListVariables is a mapping of variable names to a list of strings
+type ExportedStringListVariables map[string][]string
+
+func (m ExportedStringListVariables) set(k string, v []string) {
+	m[k] = v
+}
+
+func (m ExportedStringListVariables) asBazel(config Config,
+	stringScope ExportedStringVariables, stringListScope ExportedStringListVariables,
+	exportedVars ExportedConfigDependingVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	// For each exported variable, recursively expand elements in the variableValue
+	// list to ensure that interpolated variables are expanded according to their values
+	// in the variable scope.
+	for k, variableValue := range m {
+		var expandedVars []string
+		for _, v := range variableValue {
+			expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
+			if err != nil {
+				panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
+			}
+			expandedVars = append(expandedVars, expandedVar...)
+		}
+		// Assign the list as a bzl-private variable; this variable will be exported
+		// out through a constants struct later.
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
+		})
+	}
+	return ret
+}
+
+// ExportedStringListDictVariables is a mapping from variable names to a
+// dictionary which maps keys to lists of strings
+type ExportedStringListDictVariables map[string]map[string][]string
+
+func (m ExportedStringListDictVariables) set(k string, v map[string][]string) {
+	m[k] = v
+}
+
+// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
+func (m ExportedStringListDictVariables) asBazel(_ Config, _ ExportedStringVariables,
+	_ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for k, dict := range m {
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
+		})
+	}
+	return ret
+}
+
+// ExportedVariableReferenceDictVariables is a mapping from variable names to a
+// dictionary which references previously defined variables. This is used to
+// create a Starlark output such as:
+// 		string_var1 = "string1
+// 		var_ref_dict_var1 = {
+// 			"key1": string_var1
+// 		}
+// This type of variable collection must be expanded last so that it recognizes
+// previously defined variables.
+type ExportedVariableReferenceDictVariables map[string]map[string]string
+
+func (m ExportedVariableReferenceDictVariables) set(k string, v map[string]string) {
+	m[k] = v
+}
+
+func (m ExportedVariableReferenceDictVariables) asBazel(_ Config, _ ExportedStringVariables,
+	_ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for n, dict := range m {
+		for k, v := range dict {
+			matches, err := variableReference(v)
+			if err != nil {
+				panic(err)
+			} else if !matches.matches {
+				panic(fmt.Errorf("Expected a variable reference, got %q", v))
+			} else if len(matches.fullVariableReference) != len(v) {
+				panic(fmt.Errorf("Expected only a variable reference, got %q", v))
+			}
+			dict[k] = "_" + matches.variable
+		}
+		ret = append(ret, bazelConstant{
+			variableName:       n,
+			internalDefinition: starlark_fmt.PrintDict(dict, 0),
+			sortLast:           true,
+		})
+	}
+	return ret
+}
+
+// BazelToolchainVars expands an ExportedVariables collection and returns a string
+// of formatted Starlark variable definitions
+func BazelToolchainVars(config Config, exportedVars ExportedVariables) string {
+	results := exportedVars.asBazel(
+		config,
+		exportedVars.exportedStringVars,
+		exportedVars.exportedStringListVars,
+		exportedVars.exportedConfigDependingVars,
+	)
+
+	sort.Slice(results, func(i, j int) bool {
+		if results[i].sortLast != results[j].sortLast {
+			return !results[i].sortLast
+		}
+		return results[i].variableName < results[j].variableName
+	})
+
+	definitions := make([]string, 0, len(results))
+	constants := make([]string, 0, len(results))
+	for _, b := range results {
+		definitions = append(definitions,
+			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
+		constants = append(constants,
+			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
+	}
+
+	// Build the exported constants struct.
+	ret := bazel.GeneratedBazelFileWarning
+	ret += "\n\n"
+	ret += strings.Join(definitions, "\n\n")
+	ret += "\n\n"
+	ret += "constants = struct(\n"
+	ret += strings.Join(constants, "\n")
+	ret += "\n)"
+
+	return ret
+}
+
+type match struct {
+	matches               bool
+	fullVariableReference string
+	variable              string
+}
+
+func variableReference(input string) (match, error) {
+	// e.g. "${ExternalCflags}"
+	r := regexp.MustCompile(`\${(?:config\.)?([a-zA-Z0-9_]+)}`)
+
+	matches := r.FindStringSubmatch(input)
+	if len(matches) == 0 {
+		return match{}, nil
+	}
+	if len(matches) != 2 {
+		return match{}, fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", input, len(matches)-1)
+	}
+	return match{
+		matches:               true,
+		fullVariableReference: matches[0],
+		// Index 1 of FindStringSubmatch contains the subexpression match
+		// (variable name) of the capture group.
+		variable: matches[1],
+	}, nil
+}
+
+// expandVar recursively expand interpolated variables in the exportedVars scope.
+//
+// We're using a string slice to track the seen variables to avoid
+// stackoverflow errors with infinite recursion. it's simpler to use a
+// string slice than to handle a pass-by-referenced map, which would make it
+// quite complex to track depth-first interpolations. It's also unlikely the
+// interpolation stacks are deep (n > 1).
+func expandVar(config Config, toExpand string, stringScope ExportedStringVariables,
+	stringListScope ExportedStringListVariables, exportedVars ExportedConfigDependingVariables) ([]string, error) {
+
+	// Internal recursive function.
+	var expandVarInternal func(string, map[string]bool) (string, error)
+	expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
+		var ret string
+		remainingString := toExpand
+		for len(remainingString) > 0 {
+			matches, err := variableReference(remainingString)
+			if err != nil {
+				panic(err)
+			}
+			if !matches.matches {
+				return ret + remainingString, nil
+			}
+			matchIndex := strings.Index(remainingString, matches.fullVariableReference)
+			ret += remainingString[:matchIndex]
+			remainingString = remainingString[matchIndex+len(matches.fullVariableReference):]
+
+			variable := matches.variable
+			// toExpand contains a variable.
+			if _, ok := seenVars[variable]; ok {
+				return ret, fmt.Errorf(
+					"Unbounded recursive interpolation of variable: %s", variable)
+			}
+			// A map is passed-by-reference. Create a new map for
+			// this scope to prevent variables seen in one depth-first expansion
+			// to be also treated as "seen" in other depth-first traversals.
+			newSeenVars := map[string]bool{}
+			for k := range seenVars {
+				newSeenVars[k] = true
+			}
+			newSeenVars[variable] = true
+			if unexpandedVars, ok := stringListScope[variable]; ok {
+				expandedVars := []string{}
+				for _, unexpandedVar := range unexpandedVars {
+					expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+					if err != nil {
+						return ret, err
+					}
+					expandedVars = append(expandedVars, expandedVar)
+				}
+				ret += strings.Join(expandedVars, " ")
+			} else if unexpandedVar, ok := stringScope[variable]; ok {
+				expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+				if err != nil {
+					return ret, err
+				}
+				ret += expandedVar
+			} else if unevaluatedVar, ok := exportedVars[variable]; ok {
+				evalFunc := reflect.ValueOf(unevaluatedVar)
+				validateVariableMethod(variable, evalFunc)
+				evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
+				evaluatedValue := evaluatedResult[0].Interface().(string)
+				expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
+				if err != nil {
+					return ret, err
+				}
+				ret += expandedVar
+			} else {
+				return "", fmt.Errorf("Unbound config variable %s", variable)
+			}
+		}
+		return ret, nil
+	}
+	var ret []string
+	stringFields := splitStringKeepingQuotedSubstring(toExpand, ' ')
+	for _, v := range stringFields {
+		val, err := expandVarInternal(v, map[string]bool{})
+		if err != nil {
+			return ret, err
+		}
+		ret = append(ret, val)
+	}
+
+	return ret, nil
+}
+
+// splitStringKeepingQuotedSubstring splits a string on a provided separator,
+// but it will not split substrings inside unescaped double quotes. If the double
+// quotes are escaped, then the returned string will only include the quote, and
+// not the escape.
+func splitStringKeepingQuotedSubstring(s string, delimiter byte) []string {
+	var ret []string
+	quote := byte('"')
+
+	var substring []byte
+	quoted := false
+	escaped := false
+
+	for i := range s {
+		if !quoted && s[i] == delimiter {
+			ret = append(ret, string(substring))
+			substring = []byte{}
+			continue
+		}
+
+		characterIsEscape := i < len(s)-1 && s[i] == '\\' && s[i+1] == quote
+		if characterIsEscape {
+			escaped = true
+			continue
+		}
+
+		if s[i] == quote {
+			if !escaped {
+				quoted = !quoted
+			}
+			escaped = false
+		}
+
+		substring = append(substring, s[i])
+	}
+
+	ret = append(ret, string(substring))
+
+	return ret
+}
+
+func validateVariableMethod(name string, methodValue reflect.Value) {
+	methodType := methodValue.Type()
+	if methodType.Kind() != reflect.Func {
+		panic(fmt.Errorf("method given for variable %s is not a function",
+			name))
+	}
+	if n := methodType.NumIn(); n != 1 {
+		panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
+			name, n))
+	}
+	if n := methodType.NumOut(); n != 1 {
+		panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
+			name, n))
+	}
+	if kind := methodType.Out(0).Kind(); kind != reflect.String {
+		panic(fmt.Errorf("method for variable %s does not return a string",
+			name))
+	}
+}
diff --git a/android/config_bp2build_test.go b/android/config_bp2build_test.go
new file mode 100644
index 0000000..1a0ba7b
--- /dev/null
+++ b/android/config_bp2build_test.go
@@ -0,0 +1,454 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"android/soong/bazel"
+	"testing"
+)
+
+func TestExpandVars(t *testing.T) {
+	android_arm64_config := TestConfig("out", nil, "", nil)
+	android_arm64_config.BuildOS = Android
+	android_arm64_config.BuildArch = Arm64
+
+	testCases := []struct {
+		description     string
+		config          Config
+		stringScope     ExportedStringVariables
+		stringListScope ExportedStringListVariables
+		configVars      ExportedConfigDependingVariables
+		toExpand        string
+		expectedValues  []string
+	}{
+		{
+			description:    "no expansion for non-interpolated value",
+			toExpand:       "foo",
+			expectedValues: []string{"foo"},
+		},
+		{
+			description: "single level expansion for string var",
+			stringScope: ExportedStringVariables{
+				"foo": "bar",
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"bar"},
+		},
+		{
+			description: "single level expansion with short-name for string var",
+			stringScope: ExportedStringVariables{
+				"foo": "bar",
+			},
+			toExpand:       "${config.foo}",
+			expectedValues: []string{"bar"},
+		},
+		{
+			description: "single level expansion string list var",
+			stringListScope: ExportedStringListVariables{
+				"foo": []string{"bar"},
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"bar"},
+		},
+		{
+			description: "mixed level expansion for string list var",
+			stringScope: ExportedStringVariables{
+				"foo": "${bar}",
+				"qux": "hello",
+			},
+			stringListScope: ExportedStringListVariables{
+				"bar": []string{"baz", "${qux}"},
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"baz hello"},
+		},
+		{
+			description: "double level expansion",
+			stringListScope: ExportedStringListVariables{
+				"foo": []string{"${bar}"},
+				"bar": []string{"baz"},
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"baz"},
+		},
+		{
+			description: "double level expansion with a literal",
+			stringListScope: ExportedStringListVariables{
+				"a": []string{"${b}", "c"},
+				"b": []string{"d"},
+			},
+			toExpand:       "${a}",
+			expectedValues: []string{"d c"},
+		},
+		{
+			description: "double level expansion, with two variables in a string",
+			stringListScope: ExportedStringListVariables{
+				"a": []string{"${b} ${c}"},
+				"b": []string{"d"},
+				"c": []string{"e"},
+			},
+			toExpand:       "${a}",
+			expectedValues: []string{"d e"},
+		},
+		{
+			description: "triple level expansion with two variables in a string",
+			stringListScope: ExportedStringListVariables{
+				"a": []string{"${b} ${c}"},
+				"b": []string{"${c}", "${d}"},
+				"c": []string{"${d}"},
+				"d": []string{"foo"},
+			},
+			toExpand:       "${a}",
+			expectedValues: []string{"foo foo foo"},
+		},
+		{
+			description: "expansion with config depending vars",
+			configVars: ExportedConfigDependingVariables{
+				"a": func(c Config) string { return c.BuildOS.String() },
+				"b": func(c Config) string { return c.BuildArch.String() },
+			},
+			config:         android_arm64_config,
+			toExpand:       "${a}-${b}",
+			expectedValues: []string{"android-arm64"},
+		},
+		{
+			description: "double level multi type expansion",
+			stringListScope: ExportedStringListVariables{
+				"platform": []string{"${os}-${arch}"},
+				"const":    []string{"const"},
+			},
+			configVars: ExportedConfigDependingVariables{
+				"os":   func(c Config) string { return c.BuildOS.String() },
+				"arch": func(c Config) string { return c.BuildArch.String() },
+				"foo":  func(c Config) string { return "foo" },
+			},
+			config:         android_arm64_config,
+			toExpand:       "${const}/${platform}/${foo}",
+			expectedValues: []string{"const/android-arm64/foo"},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
+			if len(output) != len(testCase.expectedValues) {
+				t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
+			}
+			for i, actual := range output {
+				expectedValue := testCase.expectedValues[i]
+				if actual != expectedValue {
+					t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
+				}
+			}
+		})
+	}
+}
+
+func TestBazelToolchainVars(t *testing.T) {
+	testCases := []struct {
+		name        string
+		config      Config
+		vars        ExportedVariables
+		expectedOut string
+	}{
+		{
+			name: "exports strings",
+			vars: ExportedVariables{
+				exportedStringVars: ExportedStringVariables{
+					"a": "b",
+					"c": "d",
+				},
+			},
+			expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = "b"
+
+_c = "d"
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports string lists",
+			vars: ExportedVariables{
+				exportedStringListVars: ExportedStringListVariables{
+					"a": []string{"b1", "b2"},
+					"c": []string{"d1", "d2"},
+				},
+			},
+			expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = [
+    "b1",
+    "b2",
+]
+
+_c = [
+    "d1",
+    "d2",
+]
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports string lists dicts",
+			vars: ExportedVariables{
+				exportedStringListDictVars: ExportedStringListDictVariables{
+					"a": map[string][]string{"b1": {"b2"}},
+					"c": map[string][]string{"d1": {"d2"}},
+				},
+			},
+			expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = {
+    "b1": ["b2"],
+}
+
+_c = {
+    "d1": ["d2"],
+}
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports dict with var refs",
+			vars: ExportedVariables{
+				exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
+					"a": map[string]string{"b1": "${b2}"},
+					"c": map[string]string{"d1": "${config.d2}"},
+				},
+			},
+			expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = {
+    "b1": _b2,
+}
+
+_c = {
+    "d1": _d2,
+}
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "sorts across types with variable references last",
+			vars: ExportedVariables{
+				exportedStringVars: ExportedStringVariables{
+					"b": "b-val",
+					"d": "d-val",
+				},
+				exportedStringListVars: ExportedStringListVariables{
+					"c": []string{"c-val"},
+					"e": []string{"e-val"},
+				},
+				exportedStringListDictVars: ExportedStringListDictVariables{
+					"a": map[string][]string{"a1": {"a2"}},
+					"f": map[string][]string{"f1": {"f2"}},
+				},
+				exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
+					"aa": map[string]string{"b1": "${b}"},
+					"cc": map[string]string{"d1": "${config.d}"},
+				},
+			},
+			expectedOut: bazel.GeneratedBazelFileWarning + `
+
+_a = {
+    "a1": ["a2"],
+}
+
+_b = "b-val"
+
+_c = ["c-val"]
+
+_d = "d-val"
+
+_e = ["e-val"]
+
+_f = {
+    "f1": ["f2"],
+}
+
+_aa = {
+    "b1": _b,
+}
+
+_cc = {
+    "d1": _d,
+}
+
+constants = struct(
+    a = _a,
+    b = _b,
+    c = _c,
+    d = _d,
+    e = _e,
+    f = _f,
+    aa = _aa,
+    cc = _cc,
+)`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			out := BazelToolchainVars(tc.config, tc.vars)
+			if out != tc.expectedOut {
+				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
+			}
+		})
+	}
+}
+
+func TestSplitStringKeepingQuotedSubstring(t *testing.T) {
+	testCases := []struct {
+		description string
+		s           string
+		delimiter   byte
+		split       []string
+	}{
+		{
+			description: "empty string returns single empty string",
+			s:           "",
+			delimiter:   ' ',
+			split: []string{
+				"",
+			},
+		},
+		{
+			description: "string with single space returns two empty strings",
+			s:           " ",
+			delimiter:   ' ',
+			split: []string{
+				"",
+				"",
+			},
+		},
+		{
+			description: "string with two spaces returns three empty strings",
+			s:           "  ",
+			delimiter:   ' ',
+			split: []string{
+				"",
+				"",
+				"",
+			},
+		},
+		{
+			description: "string with four words returns four word string",
+			s:           "hello world with words",
+			delimiter:   ' ',
+			split: []string{
+				"hello",
+				"world",
+				"with",
+				"words",
+			},
+		},
+		{
+			description: "string with words and nested quote returns word strings and quote string",
+			s:           `hello "world with" words`,
+			delimiter:   ' ',
+			split: []string{
+				"hello",
+				`"world with"`,
+				"words",
+			},
+		},
+		{
+			description: "string with escaped quote inside real quotes",
+			s:           `hello \"world "with\" words"`,
+			delimiter:   ' ',
+			split: []string{
+				"hello",
+				`"world`,
+				`"with" words"`,
+			},
+		},
+		{
+			description: "string with words and escaped quotes returns word strings",
+			s:           `hello \"world with\" words`,
+			delimiter:   ' ',
+			split: []string{
+				"hello",
+				`"world`,
+				`with"`,
+				"words",
+			},
+		},
+		{
+			description: "string which is single quoted substring returns only substring",
+			s:           `"hello world with words"`,
+			delimiter:   ' ',
+			split: []string{
+				`"hello world with words"`,
+			},
+		},
+		{
+			description: "string starting with quote returns quoted string",
+			s:           `"hello world with" words`,
+			delimiter:   ' ',
+			split: []string{
+				`"hello world with"`,
+				"words",
+			},
+		},
+		{
+			description: "string with starting quote and no ending quote returns quote to end of string",
+			s:           `hello "world with words`,
+			delimiter:   ' ',
+			split: []string{
+				"hello",
+				`"world with words`,
+			},
+		},
+		{
+			description: "quoted string is treated as a single \"word\" unless separated by delimiter",
+			s:           `hello "world"with words`,
+			delimiter:   ' ',
+			split: []string{
+				"hello",
+				`"world"with`,
+				"words",
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			split := splitStringKeepingQuotedSubstring(tc.s, tc.delimiter)
+			if len(split) != len(tc.split) {
+				t.Fatalf("number of split string elements (%d) differs from expected (%d): split string (%v), expected (%v)",
+					len(split), len(tc.split), split, tc.split,
+				)
+			}
+			for i := range split {
+				if split[i] != tc.split[i] {
+					t.Errorf("split string element (%d), %v, differs from expected, %v", i, split[i], tc.split[i])
+				}
+			}
+		})
+	}
+}
diff --git a/android/licenses.go b/android/licenses.go
index e60c7a2..bd14b26 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -335,4 +335,6 @@
 	ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
 	ctx.Strict("COMPLIANCENOTICE_BOM", ctx.Config().HostToolPath(ctx, "compliancenotice_bom").String())
 	ctx.Strict("COMPLIANCENOTICE_SHIPPEDLIBS", ctx.Config().HostToolPath(ctx, "compliancenotice_shippedlibs").String())
+	ctx.Strict("COMPLIANCE_LISTSHARE", ctx.Config().HostToolPath(ctx, "compliance_listshare").String())
+	ctx.Strict("COMPLIANCE_CHECKSHARE", ctx.Config().HostToolPath(ctx, "compliance_checkshare").String())
 }
diff --git a/android/module.go b/android/module.go
index 66a5f60..ab68e24 100644
--- a/android/module.go
+++ b/android/module.go
@@ -3734,6 +3734,8 @@
 	Installed_paths   []string `json:"installed,omitempty"`
 	SrcJars           []string `json:"srcjars,omitempty"`
 	Paths             []string `json:"path,omitempty"`
+	Static_libs       []string `json:"static_libs,omitempty"`
+	Libs              []string `json:"libs,omitempty"`
 }
 
 func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents string) []error {
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 8dd9b89..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -639,13 +639,9 @@
 // Extracts an interface from values containing the properties to apply based on config.
 // If config does not match a value with a non-nil property set, the default value will be returned.
 func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
-	configValue := config.String(s.variable)
-	if configValue != "" && !InList(configValue, s.values) {
-		return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
-	}
 	for j, v := range s.values {
 		f := values.Field(j)
-		if configValue == v && !f.Elem().IsNil() {
+		if config.String(s.variable) == v && !f.Elem().IsNil() {
 			return f.Interface(), nil
 		}
 	}
@@ -862,13 +858,3 @@
 }
 
 var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
-
-// InList checks if the string belongs to the list
-func InList(s string, list []string) bool {
-	for _, s2 := range list {
-		if s2 == s {
-			return true
-		}
-	}
-	return false
-}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index d5d87ef..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -303,10 +303,6 @@
 	Bool_var interface{}
 }
 
-type stringSoongConfigVars struct {
-	String_var interface{}
-}
-
 func Test_PropertiesToApply(t *testing.T) {
 	mt, _ := newModuleType(&ModuleTypeProperties{
 		Module_type:      "foo",
@@ -369,51 +365,6 @@
 	}
 }
 
-func Test_PropertiesToApply_String_Error(t *testing.T) {
-	mt, _ := newModuleType(&ModuleTypeProperties{
-		Module_type:      "foo",
-		Config_namespace: "bar",
-		Variables:        []string{"string_var"},
-		Properties:       []string{"a", "b"},
-	})
-	mt.Variables = append(mt.Variables, &stringVariable{
-		baseVariable: baseVariable{
-			variable: "string_var",
-		},
-		values: []string{"a", "b", "c"},
-	})
-	stringVarPositive := &properties{
-		A: proptools.StringPtr("A"),
-		B: true,
-	}
-	conditionsDefault := &properties{
-		A: proptools.StringPtr("default"),
-		B: false,
-	}
-	actualProps := &struct {
-		Soong_config_variables stringSoongConfigVars
-	}{
-		Soong_config_variables: stringSoongConfigVars{
-			String_var: &boolVarProps{
-				A:                  stringVarPositive.A,
-				B:                  stringVarPositive.B,
-				Conditions_default: conditionsDefault,
-			},
-		},
-	}
-	props := reflect.ValueOf(actualProps)
-
-	_, err := PropertiesToApply(mt, props, Config(map[string]string{
-		"string_var": "x",
-	}))
-	expected := `Soong config property "string_var" must be one of [a b c], found "x"`
-	if err == nil {
-		t.Fatalf("Expected an error, got nil")
-	} else if err.Error() != expected {
-		t.Fatalf("Error message was not correct, expected %q, got %q", expected, err.Error())
-	}
-}
-
 func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
 	testCases := []struct {
 		desc     string
diff --git a/android/testing.go b/android/testing.go
index a9632e9..ac02db9 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -211,7 +211,7 @@
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
 
-func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConfig) {
+func (ctx *TestContext) RegisterBp2BuildConfig(config bp2BuildConversionAllowlist) {
 	ctx.config.bp2buildPackageConfig = config
 }
 
diff --git a/android/variable.go b/android/variable.go
index 4ed0507..077b810 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -389,6 +389,8 @@
 	CertificateOverrides         []string `json:",omitempty"`
 	PackageNameOverrides         []string `json:",omitempty"`
 
+	ApexGlobalMinSdkVersionOverride *string `json:",omitempty"`
+
 	EnforceSystemCertificate          *bool    `json:",omitempty"`
 	EnforceSystemCertificateAllowList []string `json:",omitempty"`
 
diff --git a/apex/apex.go b/apex/apex.go
index 2fe17da..a7b0a4f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2450,20 +2450,43 @@
 	android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
 }
 
+// Returns apex's min_sdk_version string value, honoring overrides
+func (a *apexBundle) minSdkVersionValue(ctx android.EarlyModuleContext) string {
+	// Only override the minSdkVersion value on Apexes which already specify
+	// a min_sdk_version (it's optional for non-updatable apexes), and that its
+	// min_sdk_version value is lower than the one to override with.
+	overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
+	overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
+	originalMinApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	isMinSdkSet := a.properties.Min_sdk_version != nil
+	isOverrideValueHigher := overrideApiLevel.CompareTo(originalMinApiLevel) > 0
+	if overrideMinSdkValue != "" && isMinSdkSet && isOverrideValueHigher {
+		return overrideMinSdkValue
+	}
+
+	return proptools.String(a.properties.Min_sdk_version)
+}
+
+// Returns apex's min_sdk_version SdkSpec, honoring overrides
 func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpec{
 		Kind:     android.SdkNone,
 		ApiLevel: a.minSdkVersion(ctx),
-		Raw:      String(a.properties.Min_sdk_version),
+		Raw:      a.minSdkVersionValue(ctx),
 	}
 }
 
+// Returns apex's min_sdk_version ApiLevel, honoring overrides
 func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	ver := proptools.String(a.properties.Min_sdk_version)
-	if ver == "" {
+	return minSdkVersionFromValue(ctx, a.minSdkVersionValue(ctx))
+}
+
+// Construct ApiLevel object from min_sdk_version string value
+func minSdkVersionFromValue(ctx android.EarlyModuleContext, value string) android.ApiLevel {
+	if value == "" {
 		return android.NoneApiLevel
 	}
-	apiLevel, err := android.ApiLevelFromUser(ctx, ver)
+	apiLevel, err := android.ApiLevelFromUser(ctx, value)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
 		return android.NoneApiLevel
@@ -2518,7 +2541,7 @@
 // checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
 func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
 	if a.Updatable() {
-		if String(a.properties.Min_sdk_version) == "" {
+		if a.minSdkVersionValue(ctx) == "" {
 			ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
 		}
 		if a.UsePlatformApis() {
@@ -3398,6 +3421,8 @@
 		fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.properties.File_contexts))
 	}
 
+	// TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
+	// given it's coming via config, we probably don't want to put it in here.
 	var minSdkVersion *string
 	if a.properties.Min_sdk_version != nil {
 		minSdkVersion = a.properties.Min_sdk_version
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 3e01f26..77cbb58 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -113,6 +113,12 @@
 	})
 }
 
+func withApexGlobalMinSdkVersionOverride(minSdkOverride *string) android.FixturePreparer {
+	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.ApexGlobalMinSdkVersionOverride = minSdkOverride
+	})
+}
+
 var withBinder32bit = android.FixtureModifyProductVariables(
 	func(variables android.FixtureProductVariables) {
 		variables.Binder32bit = proptools.BoolPtr(true)
@@ -168,44 +174,42 @@
 		"system/sepolicy/apex/otherapex-file_contexts":                nil,
 		"system/sepolicy/apex/com.android.vndk-file_contexts":         nil,
 		"system/sepolicy/apex/com.android.vndk.current-file_contexts": nil,
-		"mylib.cpp":                                  nil,
-		"mytest.cpp":                                 nil,
-		"mytest1.cpp":                                nil,
-		"mytest2.cpp":                                nil,
-		"mytest3.cpp":                                nil,
-		"myprebuilt":                                 nil,
-		"my_include":                                 nil,
-		"foo/bar/MyClass.java":                       nil,
-		"prebuilt.jar":                               nil,
-		"prebuilt.so":                                nil,
-		"vendor/foo/devkeys/test.x509.pem":           nil,
-		"vendor/foo/devkeys/test.pk8":                nil,
-		"testkey.x509.pem":                           nil,
-		"testkey.pk8":                                nil,
-		"testkey.override.x509.pem":                  nil,
-		"testkey.override.pk8":                       nil,
-		"vendor/foo/devkeys/testkey.avbpubkey":       nil,
-		"vendor/foo/devkeys/testkey.pem":             nil,
-		"NOTICE":                                     nil,
-		"custom_notice":                              nil,
-		"custom_notice_for_static_lib":               nil,
-		"testkey2.avbpubkey":                         nil,
-		"testkey2.pem":                               nil,
-		"myapex-arm64.apex":                          nil,
-		"myapex-arm.apex":                            nil,
-		"myapex.apks":                                nil,
-		"frameworks/base/api/current.txt":            nil,
-		"framework/aidl/a.aidl":                      nil,
-		"build/make/core/proguard.flags":             nil,
-		"build/make/core/proguard_basic_keeps.flags": nil,
-		"dummy.txt":                                  nil,
-		"baz":                                        nil,
-		"bar/baz":                                    nil,
-		"testdata/baz":                               nil,
-		"AppSet.apks":                                nil,
-		"foo.rs":                                     nil,
-		"libfoo.jar":                                 nil,
-		"libbar.jar":                                 nil,
+		"mylib.cpp":                            nil,
+		"mytest.cpp":                           nil,
+		"mytest1.cpp":                          nil,
+		"mytest2.cpp":                          nil,
+		"mytest3.cpp":                          nil,
+		"myprebuilt":                           nil,
+		"my_include":                           nil,
+		"foo/bar/MyClass.java":                 nil,
+		"prebuilt.jar":                         nil,
+		"prebuilt.so":                          nil,
+		"vendor/foo/devkeys/test.x509.pem":     nil,
+		"vendor/foo/devkeys/test.pk8":          nil,
+		"testkey.x509.pem":                     nil,
+		"testkey.pk8":                          nil,
+		"testkey.override.x509.pem":            nil,
+		"testkey.override.pk8":                 nil,
+		"vendor/foo/devkeys/testkey.avbpubkey": nil,
+		"vendor/foo/devkeys/testkey.pem":       nil,
+		"NOTICE":                               nil,
+		"custom_notice":                        nil,
+		"custom_notice_for_static_lib":         nil,
+		"testkey2.avbpubkey":                   nil,
+		"testkey2.pem":                         nil,
+		"myapex-arm64.apex":                    nil,
+		"myapex-arm.apex":                      nil,
+		"myapex.apks":                          nil,
+		"frameworks/base/api/current.txt":      nil,
+		"framework/aidl/a.aidl":                nil,
+		"dummy.txt":                            nil,
+		"baz":                                  nil,
+		"bar/baz":                              nil,
+		"testdata/baz":                         nil,
+		"AppSet.apks":                          nil,
+		"foo.rs":                               nil,
+		"libfoo.jar":                           nil,
+		"libbar.jar":                           nil,
 	},
 	),
 
@@ -1032,10 +1036,10 @@
 
 	// Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because
 	// of the platform_apis: true)
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000_private").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
 	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
-	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000_private").Rule("rustc").Args["linkFlags"]
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
 	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
 	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
 }
@@ -6314,6 +6318,124 @@
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
 
+func TestMinSdkVersionOverride(t *testing.T) {
+	// Override from 29 to 31
+	minSdkOverride31 := "31"
+	ctx := testApex(t, `
+			apex {
+					name: "myapex",
+					key: "myapex.key",
+					native_shared_libs: ["mylib"],
+					updatable: true,
+					min_sdk_version: "29"
+			}
+
+			override_apex {
+					name: "override_myapex",
+					base: "myapex",
+					logging_parent: "com.foo.bar",
+					package_name: "test.overridden.package"
+			}
+
+			apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+			}
+
+			cc_library {
+					name: "mylib",
+					srcs: ["mylib.cpp"],
+					runtime_libs: ["libbar"],
+					system_shared_libs: [],
+					stl: "none",
+					apex_available: [ "myapex" ],
+					min_sdk_version: "apex_inherit"
+			}
+
+			cc_library {
+					name: "libbar",
+					srcs: ["mylib.cpp"],
+					system_shared_libs: [],
+					stl: "none",
+					apex_available: [ "myapex" ],
+					min_sdk_version: "apex_inherit"
+			}
+
+	`, withApexGlobalMinSdkVersionOverride(&minSdkOverride31))
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that direct non-stubs dep is always included
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+	// Ensure that runtime_libs dep in included
+	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+	// Ensure libraries target overridden min_sdk_version value
+	ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_apex31")
+}
+
+func TestMinSdkVersionOverrideToLowerVersionNoOp(t *testing.T) {
+	// Attempt to override from 31 to 29, should be a NOOP
+	minSdkOverride29 := "29"
+	ctx := testApex(t, `
+			apex {
+					name: "myapex",
+					key: "myapex.key",
+					native_shared_libs: ["mylib"],
+					updatable: true,
+					min_sdk_version: "31"
+			}
+
+			override_apex {
+					name: "override_myapex",
+					base: "myapex",
+					logging_parent: "com.foo.bar",
+					package_name: "test.overridden.package"
+			}
+
+			apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+			}
+
+			cc_library {
+					name: "mylib",
+					srcs: ["mylib.cpp"],
+					runtime_libs: ["libbar"],
+					system_shared_libs: [],
+					stl: "none",
+					apex_available: [ "myapex" ],
+					min_sdk_version: "apex_inherit"
+			}
+
+			cc_library {
+					name: "libbar",
+					srcs: ["mylib.cpp"],
+					system_shared_libs: [],
+					stl: "none",
+					apex_available: [ "myapex" ],
+					min_sdk_version: "apex_inherit"
+			}
+
+	`, withApexGlobalMinSdkVersionOverride(&minSdkOverride29))
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that direct non-stubs dep is always included
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+	// Ensure that runtime_libs dep in included
+	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+	// Ensure libraries target the original min_sdk_version value rather than the overridden
+	ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_apex31")
+}
+
 func TestLegacyAndroid10Support(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
diff --git a/apex/builder.go b/apex/builder.go
index ea61e1a..293f388 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -994,7 +994,7 @@
 		return !externalDep
 	})
 
-	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, proptools.String(a.properties.Min_sdk_version), depInfos)
+	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).Raw, depInfos)
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Phony,
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 80af2bd..9e7edc7 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -10,11 +10,11 @@
         "configurability.go",
         "constants.go",
         "properties.go",
+        "testing.go",
     ],
     testSrcs: [
         "aquery_test.go",
         "properties_test.go",
-        "testing.go",
     ],
     pluginFor: [
         "soong_build",
diff --git a/bazel/constants.go b/bazel/constants.go
index 6beb496..b10f256 100644
--- a/bazel/constants.go
+++ b/bazel/constants.go
@@ -21,7 +21,7 @@
 
 	SoongInjectionDirName = "soong_injection"
 
-	GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT"
+	GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT."
 )
 
 // String returns the name of the run.
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index c7f9776..7b76b74 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -329,7 +329,7 @@
 func TestPartitionLabelListAttribute(t *testing.T) {
 	testCases := []struct {
 		name           string
-		ctx            *otherModuleTestContext
+		ctx            *OtherModuleTestContext
 		labelList      LabelListAttribute
 		filters        LabelPartitions
 		expected       PartitionToLabelListAttribute
@@ -337,7 +337,7 @@
 	}{
 		{
 			name: "no configurable values",
-			ctx:  &otherModuleTestContext{},
+			ctx:  &OtherModuleTestContext{},
 			labelList: LabelListAttribute{
 				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
 			},
@@ -354,7 +354,7 @@
 		},
 		{
 			name: "no configurable values, remainder partition",
-			ctx:  &otherModuleTestContext{},
+			ctx:  &OtherModuleTestContext{},
 			labelList: LabelListAttribute{
 				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
 			},
@@ -371,7 +371,7 @@
 		},
 		{
 			name: "no configurable values, empty partition",
-			ctx:  &otherModuleTestContext{},
+			ctx:  &OtherModuleTestContext{},
 			labelList: LabelListAttribute{
 				Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
 			},
@@ -387,8 +387,8 @@
 		},
 		{
 			name: "no configurable values, has map",
-			ctx: &otherModuleTestContext{
-				modules: []testModuleInfo{testModuleInfo{name: "srcs", typ: "fg", dir: "dir"}},
+			ctx: &OtherModuleTestContext{
+				Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}},
 			},
 			labelList: LabelListAttribute{
 				Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
@@ -406,7 +406,7 @@
 		},
 		{
 			name: "configurable values, keeps empty if excludes",
-			ctx:  &otherModuleTestContext{},
+			ctx:  &OtherModuleTestContext{},
 			labelList: LabelListAttribute{
 				ConfigurableValues: configurableLabelLists{
 					ArchConfigurationAxis: labelListSelectValues{
@@ -450,7 +450,7 @@
 		},
 		{
 			name: "error for multiple partitions same value",
-			ctx:  &otherModuleTestContext{},
+			ctx:  &OtherModuleTestContext{},
 			labelList: LabelListAttribute{
 				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
 			},
diff --git a/bazel/testing.go b/bazel/testing.go
index 23c8350..9a43b61 100644
--- a/bazel/testing.go
+++ b/bazel/testing.go
@@ -20,86 +20,86 @@
 	"github.com/google/blueprint"
 )
 
-// testModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
+// TestModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
 // a blueprint ModuleContext
-type testModuleInfo struct {
-	name string
-	typ  string
-	dir  string
+type TestModuleInfo struct {
+	ModuleName string
+	Typ        string
+	Dir        string
 }
 
 // Name returns name for testModuleInfo -- required to implement blueprint.Module
-func (mi testModuleInfo) Name() string {
-	return mi.name
+func (mi TestModuleInfo) Name() string {
+	return mi.ModuleName
 }
 
 // GenerateBuildActions unused, but required to implmeent blueprint.Module
-func (mi testModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
+func (mi TestModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
 
-func (mi testModuleInfo) equals(other testModuleInfo) bool {
-	return mi.name == other.name && mi.typ == other.typ && mi.dir == other.dir
+func (mi TestModuleInfo) equals(other TestModuleInfo) bool {
+	return mi.ModuleName == other.ModuleName && mi.Typ == other.Typ && mi.Dir == other.Dir
 }
 
 // ensure testModuleInfo implements blueprint.Module
-var _ blueprint.Module = testModuleInfo{}
+var _ blueprint.Module = TestModuleInfo{}
 
-// otherModuleTestContext is a mock context that implements OtherModuleContext
-type otherModuleTestContext struct {
-	modules []testModuleInfo
+// OtherModuleTestContext is a mock context that implements OtherModuleContext
+type OtherModuleTestContext struct {
+	Modules []TestModuleInfo
 	errors  []string
 }
 
 // ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists
-func (omc *otherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
-	for _, m := range omc.modules {
-		if m.name == name {
+func (omc *OtherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
+	for _, m := range omc.Modules {
+		if m.ModuleName == name {
 			return m, true
 		}
 	}
-	return testModuleInfo{}, false
+	return TestModuleInfo{}, false
 }
 
 // testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc
-func (omc *otherModuleTestContext) testModuleInfo(m blueprint.Module) (testModuleInfo, bool) {
-	mi, ok := m.(testModuleInfo)
+func (omc *OtherModuleTestContext) testModuleInfo(m blueprint.Module) (TestModuleInfo, bool) {
+	mi, ok := m.(TestModuleInfo)
 	if !ok {
-		return testModuleInfo{}, false
+		return TestModuleInfo{}, false
 	}
-	for _, other := range omc.modules {
+	for _, other := range omc.Modules {
 		if other.equals(mi) {
 			return mi, true
 		}
 	}
-	return testModuleInfo{}, false
+	return TestModuleInfo{}, false
 }
 
 // OtherModuleType returns type of m if it exists in omc
-func (omc *otherModuleTestContext) OtherModuleType(m blueprint.Module) string {
+func (omc *OtherModuleTestContext) OtherModuleType(m blueprint.Module) string {
 	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.typ
+		return mi.Typ
 	}
 	return ""
 }
 
 // OtherModuleName returns name of m if it exists in omc
-func (omc *otherModuleTestContext) OtherModuleName(m blueprint.Module) string {
+func (omc *OtherModuleTestContext) OtherModuleName(m blueprint.Module) string {
 	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.name
+		return mi.ModuleName
 	}
 	return ""
 }
 
 // OtherModuleDir returns dir of m if it exists in omc
-func (omc *otherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
+func (omc *OtherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
 	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.dir
+		return mi.Dir
 	}
 	return ""
 }
 
-func (omc *otherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
+func (omc *OtherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
 	omc.errors = append(omc.errors, fmt.Sprintf(format, args...))
 }
 
 // Ensure otherModuleTestContext implements OtherModuleContext
-var _ OtherModuleContext = &otherModuleTestContext{}
+var _ OtherModuleContext = &OtherModuleTestContext{}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 8a171d4..8f78968 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -18,6 +18,7 @@
     ],
     deps: [
         "soong-android",
+        "soong-android-allowlists",
         "soong-android-soongconfig",
         "soong-shared",
         "soong-apex",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 1d3b105..a96a3fc 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -580,7 +580,9 @@
 				elements = append(elements, val)
 			}
 		}
-		return starlark_fmt.PrintList(elements, indent, "%s"), nil
+		return starlark_fmt.PrintList(elements, indent, func(s string) string {
+			return "%s"
+		}), nil
 
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index b21a477..0f3ca79 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -20,6 +20,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/android/allowlists"
 	"android/soong/python"
 )
 
@@ -922,7 +923,7 @@
 		moduleTypeUnderTestFactory android.ModuleFactory
 		expectedCount              map[string]int
 		description                string
-		bp2buildConfig             android.Bp2BuildConfig
+		bp2buildConfig             allowlists.Bp2BuildConfig
 		checkDir                   string
 		fs                         map[string]string
 	}{
@@ -937,10 +938,10 @@
 				"not_migrated":                       0,
 				"also_not_migrated":                  0,
 			},
-			bp2buildConfig: android.Bp2BuildConfig{
-				"migrated":                android.Bp2BuildDefaultTrueRecursively,
-				"migrated/but_not_really": android.Bp2BuildDefaultFalse,
-				"not_migrated":            android.Bp2BuildDefaultFalse,
+			bp2buildConfig: allowlists.Bp2BuildConfig{
+				"migrated":                allowlists.Bp2BuildDefaultTrueRecursively,
+				"migrated/but_not_really": allowlists.Bp2BuildDefaultFalse,
+				"not_migrated":            allowlists.Bp2BuildDefaultFalse,
 			},
 			fs: map[string]string{
 				"migrated/Android.bp":                           `filegroup { name: "a" }`,
@@ -960,9 +961,9 @@
 				"package-opt-out":            1,
 				"package-opt-out/subpackage": 0,
 			},
-			bp2buildConfig: android.Bp2BuildConfig{
-				"package-opt-in":  android.Bp2BuildDefaultFalse,
-				"package-opt-out": android.Bp2BuildDefaultTrueRecursively,
+			bp2buildConfig: allowlists.Bp2BuildConfig{
+				"package-opt-in":  allowlists.Bp2BuildDefaultFalse,
+				"package-opt-out": allowlists.Bp2BuildDefaultTrueRecursively,
 			},
 			fs: map[string]string{
 				"package-opt-in/Android.bp": `
@@ -1004,7 +1005,8 @@
 		config := android.TestConfig(buildDir, nil, "", fs)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
+		allowlist := android.NewBp2BuildAllowlist().SetDefaultConfig(testCase.bp2buildConfig)
+		ctx.RegisterBp2BuildConfig(allowlist)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
new file mode 100644
index 0000000..8183316
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -0,0 +1,250 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package bp2build
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/cc"
+)
+
+func TestPrebuiltLibraryStaticAndSharedSimple(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library static and shared simple",
+			moduleTypeUnderTest:        "cc_prebuilt_library",
+			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	srcs: ["libf.so"],
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+					"static_library": `"libf.so"`,
+				}),
+				makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+					"shared_library": `"libf.so"`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibraryWithArchVariance(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library with arch variance",
+			moduleTypeUnderTest:        "cc_prebuilt_library",
+			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	arch: {
+		arm64: { srcs: ["libf.so"], },
+		arm: { srcs: ["libg.so"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+					"static_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`,
+				}),
+				makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+					"shared_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibraryAdditionalAttrs(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library additional attributes",
+			moduleTypeUnderTest:        "cc_prebuilt_library",
+			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so":    "",
+				"testdir/1/": "",
+				"testdir/2/": "",
+			},
+			blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	srcs: ["libf.so"],
+	export_include_dirs: ["testdir/1/"],
+	export_system_include_dirs: ["testdir/2/"],
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+					"static_library":         `"libf.so"`,
+					"export_includes":        `["testdir/1/"]`,
+					"export_system_includes": `["testdir/2/"]`,
+				}),
+				// TODO(b/229374533): When fixed, update this test
+				makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+					"shared_library": `"libf.so"`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibrarySharedStanzaFails(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library with shared stanza fails because multiple sources",
+			moduleTypeUnderTest:        "cc_prebuilt_library",
+			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	srcs: ["libf.so"],
+	shared: {
+		srcs: ["libg.so"],
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedErr: fmt.Errorf("Expected at most once source file"),
+		})
+}
+
+func TestPrebuiltLibraryStaticStanzaFails(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library with static stanza fails because multiple sources",
+			moduleTypeUnderTest:        "cc_prebuilt_library",
+			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	srcs: ["libf.so"],
+	static: {
+		srcs: ["libg.so"],
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedErr: fmt.Errorf("Expected at most once source file"),
+		})
+}
+
+func TestPrebuiltLibrarySharedAndStaticStanzas(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library with both shared and static stanzas",
+			moduleTypeUnderTest:        "cc_prebuilt_library",
+			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	static: {
+		srcs: ["libf.so"],
+	},
+	shared: {
+		srcs: ["libg.so"],
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+					"static_library": `"libf.so"`,
+				}),
+				makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+					"shared_library": `"libg.so"`,
+				}),
+			},
+		})
+}
+
+// TODO(b/228623543): When this bug is fixed, enable this test
+//func TestPrebuiltLibraryOnlyShared(t *testing.T) {
+//	runBp2BuildTestCaseSimple(t,
+//		bp2buildTestCase{
+//			description:                "prebuilt library shared only",
+//			moduleTypeUnderTest:        "cc_prebuilt_library",
+//			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+//			filesystem: map[string]string{
+//				"libf.so": "",
+//			},
+//			blueprint: `
+//cc_prebuilt_library {
+//	name: "libtest",
+//	srcs: ["libf.so"],
+//	static: {
+//		enabled: false,
+//	},
+//	bazel_module: { bp2build_available: true },
+//}`,
+//			expectedBazelTargets: []string{
+//				makeBazelTarget("prebuilt_library_shared", "libtest", attrNameToString{
+//					"shared_library": `"libf.so"`,
+//				}),
+//			},
+//		})
+//}
+
+// TODO(b/228623543): When this bug is fixed, enable this test
+//func TestPrebuiltLibraryOnlyStatic(t *testing.T) {
+//	runBp2BuildTestCaseSimple(t,
+//		bp2buildTestCase{
+//			description:                "prebuilt library static only",
+//			moduleTypeUnderTest:        "cc_prebuilt_library",
+//			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+//			filesystem: map[string]string{
+//				"libf.so": "",
+//			},
+//			blueprint: `
+//cc_prebuilt_library {
+//	name: "libtest",
+//	srcs: ["libf.so"],
+//	shared: {
+//		enabled: false,
+//	},
+//	bazel_module: { bp2build_available: true },
+//}`,
+//			expectedBazelTargets: []string{
+//				makeBazelTarget("prebuilt_library_static", "libtest_bp2build_cc_library_static", attrNameToString{
+//					"static_library": `"libf.so"`,
+//				}),
+//			},
+//		})
+//}
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
index ef2fddc..57905e5 100644
--- a/bp2build/cc_prebuilt_library_shared_test.go
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -1,6 +1,7 @@
 package bp2build
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/cc"
@@ -59,3 +60,26 @@
 			},
 		})
 }
+
+func TestSharedPrebuiltLibrarySharedStanzaFails(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library shared with shared stanza fails because multiple sources",
+			moduleTypeUnderTest:        "cc_prebuilt_library_shared",
+			moduleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	srcs: ["libf.so"],
+	shared: {
+		srcs: ["libg.so"],
+	},
+	bazel_module: { bp2build_available: true},
+}`,
+			expectedErr: fmt.Errorf("Expected at most one source file"),
+		})
+}
diff --git a/bp2build/cc_prebuilt_library_static_test.go b/bp2build/cc_prebuilt_library_static_test.go
new file mode 100644
index 0000000..3feb1f1
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_static_test.go
@@ -0,0 +1,98 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package bp2build
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/cc"
+)
+
+func TestStaticPrebuiltLibrary(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library static simple",
+			moduleTypeUnderTest:        "cc_prebuilt_library_static",
+			moduleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	srcs: ["libf.so"],
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("prebuilt_library_static", "libtest", attrNameToString{
+					"static_library": `"libf.so"`,
+				}),
+			},
+		})
+}
+
+func TestStaticPrebuiltLibraryWithArchVariance(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library static with arch variance",
+			moduleTypeUnderTest:        "cc_prebuilt_library_static",
+			moduleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	arch: {
+		arm64: { srcs: ["libf.so"], },
+		arm: { srcs: ["libg.so"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("prebuilt_library_static", "libtest", attrNameToString{
+					"static_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`,
+				}),
+			},
+		})
+}
+
+func TestStaticPrebuiltLibraryStaticStanzaFails(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                "prebuilt library with static stanza fails because multiple sources",
+			moduleTypeUnderTest:        "cc_prebuilt_library_static",
+			moduleTypeUnderTestFactory: cc.PrebuiltStaticLibraryFactory,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	srcs: ["libf.so"],
+	static: {
+		srcs: ["libg.so"],
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedErr: fmt.Errorf("Expected at most one source file"),
+		})
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 91e614d..1790dd7 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -7,7 +7,8 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/cc/config"
+	cc_config "android/soong/cc/config"
+	java_config "android/soong/java/config"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -22,7 +23,10 @@
 	var files []BazelFile
 
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
-	files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars(cfg)))
+	files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+
+	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
+	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
 
 	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index d65ece8..e49d855 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -95,6 +95,14 @@
 			basename: "constants.bzl",
 		},
 		{
+			dir:      "java_toolchain",
+			basename: GeneratedBuildFileName,
+		},
+		{
+			dir:      "java_toolchain",
+			basename: "constants.bzl",
+		},
+		{
 			dir:      "metrics",
 			basename: "converted_modules.txt",
 		},
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 4b75e3b..ccc52ef 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -219,3 +219,35 @@
 		},
 	})
 }
+
+func TestJavaLibraryLogTags(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		description:                "Java library - logtags creates separate dependency",
+		moduleTypeUnderTest:        "java_library",
+		moduleTypeUnderTestFactory: java.LibraryFactory,
+		blueprint: `java_library {
+        name: "example_lib",
+        srcs: [
+			"a.java",
+			"b.java",
+			"a.logtag",
+			"b.logtag",
+		],
+        bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("event_log_tags", "example_lib_logtags", attrNameToString{
+				"srcs": `[
+        "a.logtag",
+        "b.logtag",
+    ]`,
+			}),
+			makeBazelTarget("java_library", "example_lib", attrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.java",
+        ":example_lib_logtags",
+    ]`,
+			}),
+		}})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 53b60fa..029ba49 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -25,14 +25,17 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/android/allowlists"
 	"android/soong/bazel"
 )
 
 var (
 	// A default configuration for tests to not have to specify bp2build_available on top level targets.
-	bp2buildConfig = android.Bp2BuildConfig{
-		android.BP2BUILD_TOPLEVEL: android.Bp2BuildDefaultTrueRecursively,
-	}
+	bp2buildConfig = android.NewBp2BuildAllowlist().SetDefaultConfig(
+		allowlists.Bp2BuildConfig{
+			android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
+		},
+	)
 
 	buildDir string
 )
@@ -81,8 +84,9 @@
 	expectedBazelTargets       []string
 	filesystem                 map[string]string
 	dir                        string
-	expectedErr                error
-	unconvertedDepsMode        unconvertedDepsMode
+	// An error with a string contained within the string of the expected error
+	expectedErr         error
+	unconvertedDepsMode unconvertedDepsMode
 }
 
 func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
diff --git a/cc/afdo.go b/cc/afdo.go
index c888213..66e9732 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -45,6 +45,8 @@
 }
 
 type AfdoProperties struct {
+	// Afdo allows developers self-service enroll for
+	// automatic feedback-directed optimization using profile data.
 	Afdo bool
 
 	AfdoTarget *string  `blueprint:"mutated"`
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 811e228..cc378b3 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -141,6 +141,7 @@
 	}
 }
 
+// Parses properties common to static and shared libraries. Also used for prebuilt libraries.
 func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
 	attrs := staticOrSharedAttributes{}
 
@@ -198,30 +199,72 @@
 
 // Convenience struct to hold all attributes parsed from prebuilt properties.
 type prebuiltAttributes struct {
-	Src bazel.LabelAttribute
+	Src     bazel.LabelAttribute
+	Enabled bazel.BoolAttribute
 }
 
 // NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
-func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
+func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) prebuiltAttributes {
+	manySourceFileError := func(axis bazel.ConfigurationAxis, config string) {
+		ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most one source file for %s %s\n", axis, config)
+	}
 	var srcLabelAttribute bazel.LabelAttribute
 
-	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
-		for config, props := range configToProps {
-			if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
-				if len(prebuiltLinkerProperties.Srcs) > 1 {
-					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for %s %s\n", axis, config)
-					continue
-				} else if len(prebuiltLinkerProperties.Srcs) == 0 {
-					continue
-				}
-				src := android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0])
-				srcLabelAttribute.SetSelectValue(axis, config, src)
-			}
+	parseSrcs := func(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, srcs []string) {
+		if len(srcs) > 1 {
+			manySourceFileError(axis, config)
+			return
+		} else if len(srcs) == 0 {
+			return
 		}
+		if srcLabelAttribute.SelectValue(axis, config) != nil {
+			manySourceFileError(axis, config)
+			return
+		}
+
+		src := android.BazelLabelForModuleSrcSingle(ctx, srcs[0])
+		srcLabelAttribute.SetSelectValue(axis, config, src)
+	}
+
+	bp2BuildPropParseHelper(ctx, module, &prebuiltLinkerProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+		if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
+			parseSrcs(ctx, axis, config, prebuiltLinkerProperties.Srcs)
+		}
+	})
+
+	var enabledLabelAttribute bazel.BoolAttribute
+	parseAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
+		if props.Enabled != nil {
+			enabledLabelAttribute.SetSelectValue(axis, config, props.Enabled)
+		}
+		parseSrcs(ctx, axis, config, props.Srcs)
+	}
+
+	if isStatic {
+		bp2BuildPropParseHelper(ctx, module, &StaticProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+			if staticProperties, ok := props.(*StaticProperties); ok {
+				parseAttrs(axis, config, staticProperties.Static)
+			}
+		})
+	} else {
+		bp2BuildPropParseHelper(ctx, module, &SharedProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
+			if sharedProperties, ok := props.(*SharedProperties); ok {
+				parseAttrs(axis, config, sharedProperties.Shared)
+			}
+		})
 	}
 
 	return prebuiltAttributes{
-		Src: srcLabelAttribute,
+		Src:     srcLabelAttribute,
+		Enabled: enabledLabelAttribute,
+	}
+}
+
+func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
+		for config, props := range configToProps {
+			parseFunc(axis, config, props)
+		}
 	}
 }
 
@@ -542,8 +585,8 @@
 }
 
 func bp2BuildParseSdkAttributes(module *Module) sdkAttributes {
-	return sdkAttributes {
-		Sdk_version: module.Properties.Sdk_version,
+	return sdkAttributes{
+		Sdk_version:     module.Properties.Sdk_version,
 		Min_sdk_version: module.Properties.Min_sdk_version,
 	}
 }
diff --git a/cc/cc.go b/cc/cc.go
index ac6da05..456b736 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1218,6 +1218,17 @@
 	return c.VendorProperties.IsVendorPublicLibrary
 }
 
+func (c *Module) IsVndkPrebuiltLibrary() bool {
+	if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) SdkAndPlatformVariantVisibleToMake() bool {
+	return c.Properties.SdkAndPlatformVariantVisibleToMake
+}
+
 func (c *Module) HasLlndkStubs() bool {
 	lib := moduleLibraryInterface(c)
 	return lib != nil && lib.hasLLNDKStubs()
@@ -1699,7 +1710,7 @@
 	return nil
 }
 
-func (c *Module) getNameSuffixWithVndkVersion(ctx android.ModuleContext) string {
+func getNameSuffixWithVndkVersion(ctx android.ModuleContext, c LinkableInterface) string {
 	// Returns the name suffix for product and vendor variants. If the VNDK version is not
 	// "current", it will append the VNDK version to the name suffix.
 	var vndkVersion string
@@ -1719,19 +1730,19 @@
 	if vndkVersion == "current" {
 		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
 	}
-	if c.Properties.VndkVersion != vndkVersion && c.Properties.VndkVersion != "" {
+	if c.VndkVersion() != vndkVersion && c.VndkVersion() != "" {
 		// add version suffix only if the module is using different vndk version than the
 		// version in product or vendor partition.
-		nameSuffix += "." + c.Properties.VndkVersion
+		nameSuffix += "." + c.VndkVersion()
 	}
 	return nameSuffix
 }
 
-func (c *Module) setSubnameProperty(actx android.ModuleContext) {
-	c.Properties.SubName = ""
+func GetSubnameProperty(actx android.ModuleContext, c LinkableInterface) string {
+	var subName = ""
 
 	if c.Target().NativeBridge == android.NativeBridgeEnabled {
-		c.Properties.SubName += NativeBridgeSuffix
+		subName += NativeBridgeSuffix
 	}
 
 	llndk := c.IsLlndk()
@@ -1739,25 +1750,27 @@
 		// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
 		// added for product variant only when we have vendor and product variants with core
 		// variant. The suffix is not added for vendor-only or product-only module.
-		c.Properties.SubName += c.getNameSuffixWithVndkVersion(actx)
+		subName += getNameSuffixWithVndkVersion(actx, c)
 	} else if c.IsVendorPublicLibrary() {
-		c.Properties.SubName += vendorPublicLibrarySuffix
-	} else if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
+		subName += vendorPublicLibrarySuffix
+	} else if c.IsVndkPrebuiltLibrary() {
 		// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
 		// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
-		c.Properties.SubName += VendorSuffix
+		subName += VendorSuffix
 	} else if c.InRamdisk() && !c.OnlyInRamdisk() {
-		c.Properties.SubName += RamdiskSuffix
+		subName += RamdiskSuffix
 	} else if c.InVendorRamdisk() && !c.OnlyInVendorRamdisk() {
-		c.Properties.SubName += VendorRamdiskSuffix
+		subName += VendorRamdiskSuffix
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
-		c.Properties.SubName += RecoverySuffix
-	} else if c.IsSdkVariant() && (c.Properties.SdkAndPlatformVariantVisibleToMake || c.SplitPerApiLevel()) {
-		c.Properties.SubName += sdkSuffix
+		subName += RecoverySuffix
+	} else if c.IsSdkVariant() && (c.SdkAndPlatformVariantVisibleToMake() || c.SplitPerApiLevel()) {
+		subName += sdkSuffix
 		if c.SplitPerApiLevel() {
-			c.Properties.SubName += "." + c.SdkVersion()
+			subName += "." + c.SdkVersion()
 		}
 	}
+
+	return subName
 }
 
 // Returns true if Bazel was successfully used for the analysis of this module.
@@ -1795,7 +1808,7 @@
 		return
 	}
 
-	c.setSubnameProperty(actx)
+	c.Properties.SubName = GetSubnameProperty(actx, c)
 	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
 		c.hideApexVariantFromMake = true
@@ -3543,13 +3556,14 @@
 	case fullLibrary:
 		if !prebuilt {
 			libraryBp2Build(ctx, c)
+		} else {
+			prebuiltLibraryBp2Build(ctx, c)
 		}
 	case headerLibrary:
 		libraryHeadersBp2Build(ctx, c)
 	case staticLibrary:
-
 		if prebuilt {
-			prebuiltLibraryStaticBp2Build(ctx, c)
+			prebuiltLibraryStaticBp2Build(ctx, c, false)
 		} else {
 			sharedOrStaticLibraryBp2Build(ctx, c, true)
 		}
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index e1b0605..1a21c13 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -11,7 +11,6 @@
         "soong-starlark-format",
     ],
     srcs: [
-        "bp2build.go",
         "clang.go",
         "global.go",
         "tidy.go",
@@ -33,7 +32,6 @@
         "arm64_linux_host.go",
     ],
     testSrcs: [
-        "bp2build_test.go",
         "tidy_test.go",
     ],
 }
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 4d0ae1a..dfe143f 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -98,28 +98,28 @@
 	pctx.SourcePathVariable("Arm64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
 
-	exportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
-	exportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
+	exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
+	exportedVars.ExportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
 
-	exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
-	exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
+	exportedVars.ExportStringListStaticVariable("Arm64Cflags", arm64Cflags)
+	exportedVars.ExportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
 
-	exportedVariableReferenceDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar)
-	exportedVariableReferenceDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar)
-	exportedVariableReferenceDictVars.Set("Arm64CpuVariantLdflags", arm64CpuVariantLdflags)
+	exportedVars.ExportVariableReferenceDict("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar)
+	exportedVars.ExportVariableReferenceDict("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar)
+	exportedVars.ExportVariableReferenceDict("Arm64CpuVariantLdflags", arm64CpuVariantLdflags)
 
-	exportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"])
-	exportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
-	exportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
-	exportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
+	exportedVars.ExportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"])
+	exportedVars.ExportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
+	exportedVars.ExportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
+	exportedVars.ExportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
 
-	exportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
-	exportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
-	exportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"])
-	exportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"])
-	exportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"])
+	exportedVars.ExportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
+	exportedVars.ExportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
+	exportedVars.ExportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"])
+	exportedVars.ExportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"])
+	exportedVars.ExportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"])
 
-	exportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"})
+	exportedVars.ExportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"})
 }
 
 var (
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 4466632..d702c61 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -178,41 +178,41 @@
 	pctx.SourcePathVariable("ArmGccRoot", "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
 
 	// Just exported. Not created as a Ninja static variable.
-	exportedStringVars.Set("ArmClangTriple", clangTriple)
+	exportedVars.ExportString("ArmClangTriple", clangTriple)
 
-	exportStringListStaticVariable("ArmLdflags", armLdflags)
-	exportStringListStaticVariable("ArmLldflags", armLldflags)
+	exportedVars.ExportStringListStaticVariable("ArmLdflags", armLdflags)
+	exportedVars.ExportStringListStaticVariable("ArmLldflags", armLldflags)
 
-	exportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
-	exportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
+	exportedVars.ExportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
+	exportedVars.ExportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
 
 	// Clang cflags
-	exportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
-	exportStringListStaticVariable("ArmCflags", armCflags)
-	exportStringListStaticVariable("ArmCppflags", armCppflags)
+	exportedVars.ExportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
+	exportedVars.ExportStringListStaticVariable("ArmCflags", armCflags)
+	exportedVars.ExportStringListStaticVariable("ArmCppflags", armCppflags)
 
 	// Clang ARM vs. Thumb instruction set cflags
-	exportStringListStaticVariable("ArmArmCflags", armArmCflags)
-	exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
+	exportedVars.ExportStringListStaticVariable("ArmArmCflags", armArmCflags)
+	exportedVars.ExportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
 
-	exportedVariableReferenceDictVars.Set("ArmArchVariantCflags", armArchVariantCflagsVar)
-	exportedVariableReferenceDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflagsVar)
+	exportedVars.ExportVariableReferenceDict("ArmArchVariantCflags", armArchVariantCflagsVar)
+	exportedVars.ExportVariableReferenceDict("ArmCpuVariantCflags", armCpuVariantCflagsVar)
 
 	// Clang arch variant cflags
-	exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
-	exportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
-	exportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"])
-	exportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"])
+	exportedVars.ExportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
+	exportedVars.ExportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
+	exportedVars.ExportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"])
+	exportedVars.ExportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"])
 
 	// Clang cpu variant cflags
-	exportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""])
-	exportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
-	exportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
-	exportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
-	exportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
-	exportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
-	exportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
-	exportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"])
+	exportedVars.ExportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""])
+	exportedVars.ExportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
+	exportedVars.ExportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
+	exportedVars.ExportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
+	exportedVars.ExportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
+	exportedVars.ExportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
+	exportedVars.ExportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
+	exportedVars.ExportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"])
 }
 
 var (
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
deleted file mode 100644
index 73f65f5..0000000
--- a/cc/config/bp2build.go
+++ /dev/null
@@ -1,378 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
-	"fmt"
-	"reflect"
-	"regexp"
-	"sort"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/starlark_fmt"
-
-	"github.com/google/blueprint"
-)
-
-type bazelVarExporter interface {
-	asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
-}
-
-// Helpers for exporting cc configuration information to Bazel.
-var (
-	// Maps containing toolchain variables that are independent of the
-	// environment variables of the build.
-	exportedStringListVars     = exportedStringListVariables{}
-	exportedStringVars         = exportedStringVariables{}
-	exportedStringListDictVars = exportedStringListDictVariables{}
-	// Note: these can only contain references to other variables and must be printed last
-	exportedVariableReferenceDictVars = exportedVariableReferenceDictVariables{}
-
-	/// Maps containing variables that are dependent on the build config.
-	exportedConfigDependingVars = exportedConfigDependingVariables{}
-)
-
-type exportedConfigDependingVariables map[string]interface{}
-
-func (m exportedConfigDependingVariables) Set(k string, v interface{}) {
-	m[k] = v
-}
-
-// Ensure that string s has no invalid characters to be generated into the bzl file.
-func validateCharacters(s string) string {
-	for _, c := range []string{`\n`, `"`, `\`} {
-		if strings.Contains(s, c) {
-			panic(fmt.Errorf("%s contains illegal character %s", s, c))
-		}
-	}
-	return s
-}
-
-type bazelConstant struct {
-	variableName       string
-	internalDefinition string
-	sortLast           bool
-}
-
-type exportedStringVariables map[string]string
-
-func (m exportedStringVariables) Set(k string, v string) {
-	m[k] = v
-}
-
-func (m exportedStringVariables) asBazel(config android.Config,
-	stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for k, variableValue := range m {
-		expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
-		if err != nil {
-			panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
-		}
-		if len(expandedVar) > 1 {
-			panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
-		}
-		ret = append(ret, bazelConstant{
-			variableName:       k,
-			internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
-		})
-	}
-	return ret
-}
-
-// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
-func exportStringStaticVariable(name string, value string) {
-	pctx.StaticVariable(name, value)
-	exportedStringVars.Set(name, value)
-}
-
-type exportedStringListVariables map[string][]string
-
-func (m exportedStringListVariables) Set(k string, v []string) {
-	m[k] = v
-}
-
-func (m exportedStringListVariables) asBazel(config android.Config,
-	stringScope exportedStringVariables, stringListScope exportedStringListVariables,
-	exportedVars exportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	// For each exported variable, recursively expand elements in the variableValue
-	// list to ensure that interpolated variables are expanded according to their values
-	// in the variable scope.
-	for k, variableValue := range m {
-		var expandedVars []string
-		for _, v := range variableValue {
-			expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
-			if err != nil {
-				panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
-			}
-			expandedVars = append(expandedVars, expandedVar...)
-		}
-		// Assign the list as a bzl-private variable; this variable will be exported
-		// out through a constants struct later.
-		ret = append(ret, bazelConstant{
-			variableName:       k,
-			internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
-		})
-	}
-	return ret
-}
-
-// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
-func exportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
-	exportedConfigDependingVars.Set(name, method)
-	return pctx.VariableConfigMethod(name, method)
-}
-
-// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
-func exportSourcePathVariable(name string, value string) {
-	pctx.SourcePathVariable(name, value)
-	exportedStringVars.Set(name, value)
-}
-
-// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
-func exportStringListStaticVariable(name string, value []string) {
-	pctx.StaticVariable(name, strings.Join(value, " "))
-	exportedStringListVars.Set(name, value)
-}
-
-func ExportStringList(name string, value []string) {
-	exportedStringListVars.Set(name, value)
-}
-
-type exportedStringListDictVariables map[string]map[string][]string
-
-func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
-	m[k] = v
-}
-
-// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
-func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
-	_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for k, dict := range m {
-		ret = append(ret, bazelConstant{
-			variableName:       k,
-			internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
-		})
-	}
-	return ret
-}
-
-type exportedVariableReferenceDictVariables map[string]map[string]string
-
-func (m exportedVariableReferenceDictVariables) Set(k string, v map[string]string) {
-	m[k] = v
-}
-
-func (m exportedVariableReferenceDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
-	_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for n, dict := range m {
-		for k, v := range dict {
-			matches, err := variableReference(v)
-			if err != nil {
-				panic(err)
-			} else if !matches.matches {
-				panic(fmt.Errorf("Expected a variable reference, got %q", v))
-			} else if len(matches.fullVariableReference) != len(v) {
-				panic(fmt.Errorf("Expected only a variable reference, got %q", v))
-			}
-			dict[k] = "_" + matches.variable
-		}
-		ret = append(ret, bazelConstant{
-			variableName:       n,
-			internalDefinition: starlark_fmt.PrintDict(dict, 0),
-			sortLast:           true,
-		})
-	}
-	return ret
-}
-
-// BazelCcToolchainVars generates bzl file content containing variables for
-// Bazel's cc_toolchain configuration.
-func BazelCcToolchainVars(config android.Config) string {
-	return bazelToolchainVars(
-		config,
-		exportedStringListDictVars,
-		exportedStringListVars,
-		exportedStringVars,
-		exportedVariableReferenceDictVars)
-}
-
-func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
-	ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
-
-	results := []bazelConstant{}
-	for _, v := range vars {
-		results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
-	}
-
-	sort.Slice(results, func(i, j int) bool {
-		if results[i].sortLast != results[j].sortLast {
-			return !results[i].sortLast
-		}
-		return results[i].variableName < results[j].variableName
-	})
-
-	definitions := make([]string, 0, len(results))
-	constants := make([]string, 0, len(results))
-	for _, b := range results {
-		definitions = append(definitions,
-			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
-		constants = append(constants,
-			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
-	}
-
-	// Build the exported constants struct.
-	ret += strings.Join(definitions, "\n\n")
-	ret += "\n\n"
-	ret += "constants = struct(\n"
-	ret += strings.Join(constants, "\n")
-	ret += "\n)"
-
-	return ret
-}
-
-type match struct {
-	matches               bool
-	fullVariableReference string
-	variable              string
-}
-
-func variableReference(input string) (match, error) {
-	// e.g. "${ExternalCflags}"
-	r := regexp.MustCompile(`\${(?:config\.)?([a-zA-Z0-9_]+)}`)
-
-	matches := r.FindStringSubmatch(input)
-	if len(matches) == 0 {
-		return match{}, nil
-	}
-	if len(matches) != 2 {
-		return match{}, fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", input, len(matches)-1)
-	}
-	return match{
-		matches:               true,
-		fullVariableReference: matches[0],
-		// Index 1 of FindStringSubmatch contains the subexpression match
-		// (variable name) of the capture group.
-		variable: matches[1],
-	}, nil
-}
-
-// expandVar recursively expand interpolated variables in the exportedVars scope.
-//
-// We're using a string slice to track the seen variables to avoid
-// stackoverflow errors with infinite recursion. it's simpler to use a
-// string slice than to handle a pass-by-referenced map, which would make it
-// quite complex to track depth-first interpolations. It's also unlikely the
-// interpolation stacks are deep (n > 1).
-func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
-	stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
-
-	// Internal recursive function.
-	var expandVarInternal func(string, map[string]bool) (string, error)
-	expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
-		var ret string
-		remainingString := toExpand
-		for len(remainingString) > 0 {
-			matches, err := variableReference(remainingString)
-			if err != nil {
-				panic(err)
-			}
-			if !matches.matches {
-				return ret + remainingString, nil
-			}
-			matchIndex := strings.Index(remainingString, matches.fullVariableReference)
-			ret += remainingString[:matchIndex]
-			remainingString = remainingString[matchIndex+len(matches.fullVariableReference):]
-
-			variable := matches.variable
-			// toExpand contains a variable.
-			if _, ok := seenVars[variable]; ok {
-				return ret, fmt.Errorf(
-					"Unbounded recursive interpolation of variable: %s", variable)
-			}
-			// A map is passed-by-reference. Create a new map for
-			// this scope to prevent variables seen in one depth-first expansion
-			// to be also treated as "seen" in other depth-first traversals.
-			newSeenVars := map[string]bool{}
-			for k := range seenVars {
-				newSeenVars[k] = true
-			}
-			newSeenVars[variable] = true
-			if unexpandedVars, ok := stringListScope[variable]; ok {
-				expandedVars := []string{}
-				for _, unexpandedVar := range unexpandedVars {
-					expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
-					if err != nil {
-						return ret, err
-					}
-					expandedVars = append(expandedVars, expandedVar)
-				}
-				ret += strings.Join(expandedVars, " ")
-			} else if unexpandedVar, ok := stringScope[variable]; ok {
-				expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
-				if err != nil {
-					return ret, err
-				}
-				ret += expandedVar
-			} else if unevaluatedVar, ok := exportedVars[variable]; ok {
-				evalFunc := reflect.ValueOf(unevaluatedVar)
-				validateVariableMethod(variable, evalFunc)
-				evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
-				evaluatedValue := evaluatedResult[0].Interface().(string)
-				expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
-				if err != nil {
-					return ret, err
-				}
-				ret += expandedVar
-			} else {
-				return "", fmt.Errorf("Unbound config variable %s", variable)
-			}
-		}
-		return ret, nil
-	}
-	var ret []string
-	for _, v := range strings.Split(toExpand, " ") {
-		val, err := expandVarInternal(v, map[string]bool{})
-		if err != nil {
-			return ret, err
-		}
-		ret = append(ret, val)
-	}
-
-	return ret, nil
-}
-
-func validateVariableMethod(name string, methodValue reflect.Value) {
-	methodType := methodValue.Type()
-	if methodType.Kind() != reflect.Func {
-		panic(fmt.Errorf("method given for variable %s is not a function",
-			name))
-	}
-	if n := methodType.NumIn(); n != 1 {
-		panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
-			name, n))
-	}
-	if n := methodType.NumOut(); n != 1 {
-		panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
-			name, n))
-	}
-	if kind := methodType.Out(0).Kind(); kind != reflect.String {
-		panic(fmt.Errorf("method for variable %s does not return a string",
-			name))
-	}
-}
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
deleted file mode 100644
index 9a8178a..0000000
--- a/cc/config/bp2build_test.go
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
-	"testing"
-
-	"android/soong/android"
-)
-
-func TestExpandVars(t *testing.T) {
-	android_arm64_config := android.TestConfig("out", nil, "", nil)
-	android_arm64_config.BuildOS = android.Android
-	android_arm64_config.BuildArch = android.Arm64
-
-	testCases := []struct {
-		description     string
-		config          android.Config
-		stringScope     exportedStringVariables
-		stringListScope exportedStringListVariables
-		configVars      exportedConfigDependingVariables
-		toExpand        string
-		expectedValues  []string
-	}{
-		{
-			description:    "no expansion for non-interpolated value",
-			toExpand:       "foo",
-			expectedValues: []string{"foo"},
-		},
-		{
-			description: "single level expansion for string var",
-			stringScope: exportedStringVariables{
-				"foo": "bar",
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"bar"},
-		},
-		{
-			description: "single level expansion with short-name for string var",
-			stringScope: exportedStringVariables{
-				"foo": "bar",
-			},
-			toExpand:       "${config.foo}",
-			expectedValues: []string{"bar"},
-		},
-		{
-			description: "single level expansion string list var",
-			stringListScope: exportedStringListVariables{
-				"foo": []string{"bar"},
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"bar"},
-		},
-		{
-			description: "mixed level expansion for string list var",
-			stringScope: exportedStringVariables{
-				"foo": "${bar}",
-				"qux": "hello",
-			},
-			stringListScope: exportedStringListVariables{
-				"bar": []string{"baz", "${qux}"},
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"baz hello"},
-		},
-		{
-			description: "double level expansion",
-			stringListScope: exportedStringListVariables{
-				"foo": []string{"${bar}"},
-				"bar": []string{"baz"},
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"baz"},
-		},
-		{
-			description: "double level expansion with a literal",
-			stringListScope: exportedStringListVariables{
-				"a": []string{"${b}", "c"},
-				"b": []string{"d"},
-			},
-			toExpand:       "${a}",
-			expectedValues: []string{"d c"},
-		},
-		{
-			description: "double level expansion, with two variables in a string",
-			stringListScope: exportedStringListVariables{
-				"a": []string{"${b} ${c}"},
-				"b": []string{"d"},
-				"c": []string{"e"},
-			},
-			toExpand:       "${a}",
-			expectedValues: []string{"d e"},
-		},
-		{
-			description: "triple level expansion with two variables in a string",
-			stringListScope: exportedStringListVariables{
-				"a": []string{"${b} ${c}"},
-				"b": []string{"${c}", "${d}"},
-				"c": []string{"${d}"},
-				"d": []string{"foo"},
-			},
-			toExpand:       "${a}",
-			expectedValues: []string{"foo foo foo"},
-		},
-		{
-			description: "expansion with config depending vars",
-			configVars: exportedConfigDependingVariables{
-				"a": func(c android.Config) string { return c.BuildOS.String() },
-				"b": func(c android.Config) string { return c.BuildArch.String() },
-			},
-			config:         android_arm64_config,
-			toExpand:       "${a}-${b}",
-			expectedValues: []string{"android-arm64"},
-		},
-		{
-			description: "double level multi type expansion",
-			stringListScope: exportedStringListVariables{
-				"platform": []string{"${os}-${arch}"},
-				"const":    []string{"const"},
-			},
-			configVars: exportedConfigDependingVariables{
-				"os":   func(c android.Config) string { return c.BuildOS.String() },
-				"arch": func(c android.Config) string { return c.BuildArch.String() },
-				"foo":  func(c android.Config) string { return "foo" },
-			},
-			config:         android_arm64_config,
-			toExpand:       "${const}/${platform}/${foo}",
-			expectedValues: []string{"const/android-arm64/foo"},
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.description, func(t *testing.T) {
-			output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
-			if len(output) != len(testCase.expectedValues) {
-				t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
-			}
-			for i, actual := range output {
-				expectedValue := testCase.expectedValues[i]
-				if actual != expectedValue {
-					t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
-				}
-			}
-		})
-	}
-}
-
-func TestBazelToolchainVars(t *testing.T) {
-	testCases := []struct {
-		name        string
-		config      android.Config
-		vars        []bazelVarExporter
-		expectedOut string
-	}{
-		{
-			name: "exports strings",
-			vars: []bazelVarExporter{
-				exportedStringVariables{
-					"a": "b",
-					"c": "d",
-				},
-			},
-			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = "b"
-
-_c = "d"
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "exports string lists",
-			vars: []bazelVarExporter{
-				exportedStringListVariables{
-					"a": []string{"b1", "b2"},
-					"c": []string{"d1", "d2"},
-				},
-			},
-			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = [
-    "b1",
-    "b2",
-]
-
-_c = [
-    "d1",
-    "d2",
-]
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "exports string lists dicts",
-			vars: []bazelVarExporter{
-				exportedStringListDictVariables{
-					"a": map[string][]string{"b1": []string{"b2"}},
-					"c": map[string][]string{"d1": []string{"d2"}},
-				},
-			},
-			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = {
-    "b1": ["b2"],
-}
-
-_c = {
-    "d1": ["d2"],
-}
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "exports dict with var refs",
-			vars: []bazelVarExporter{
-				exportedVariableReferenceDictVariables{
-					"a": map[string]string{"b1": "${b2}"},
-					"c": map[string]string{"d1": "${config.d2}"},
-				},
-			},
-			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = {
-    "b1": _b2,
-}
-
-_c = {
-    "d1": _d2,
-}
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "sorts across types with variable references last",
-			vars: []bazelVarExporter{
-				exportedStringVariables{
-					"b": "b-val",
-					"d": "d-val",
-				},
-				exportedStringListVariables{
-					"c": []string{"c-val"},
-					"e": []string{"e-val"},
-				},
-				exportedStringListDictVariables{
-					"a": map[string][]string{"a1": []string{"a2"}},
-					"f": map[string][]string{"f1": []string{"f2"}},
-				},
-				exportedVariableReferenceDictVariables{
-					"aa": map[string]string{"b1": "${b}"},
-					"cc": map[string]string{"d1": "${config.d}"},
-				},
-			},
-			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
-
-_a = {
-    "a1": ["a2"],
-}
-
-_b = "b-val"
-
-_c = ["c-val"]
-
-_d = "d-val"
-
-_e = ["e-val"]
-
-_f = {
-    "f1": ["f2"],
-}
-
-_aa = {
-    "b1": _b,
-}
-
-_cc = {
-    "d1": _d,
-}
-
-constants = struct(
-    a = _a,
-    b = _b,
-    c = _c,
-    d = _d,
-    e = _e,
-    f = _f,
-    aa = _aa,
-    cc = _cc,
-)`,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			out := bazelToolchainVars(tc.config, tc.vars...)
-			if out != tc.expectedOut {
-				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
-			}
-		})
-	}
-}
diff --git a/cc/config/global.go b/cc/config/global.go
index dfb9a66..65bfbf0 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -23,6 +23,9 @@
 )
 
 var (
+	pctx         = android.NewPackageContext("android/soong/cc/config")
+	exportedVars = android.NewExportedVariables(pctx)
+
 	// Flags used by lots of devices.  Putting them in package static variables
 	// will save bytes in build.ninja so they aren't repeated for every file
 	commonGlobalCflags = []string{
@@ -283,8 +286,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r450784b"
-	ClangDefaultShortVersion = "14.0.4"
+	ClangDefaultVersion      = "clang-r450784c"
+	ClangDefaultShortVersion = "14.0.5"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -296,20 +299,28 @@
 	WarningAllowedOldProjects = []string{}
 )
 
-var pctx = android.NewPackageContext("android/soong/cc/config")
+// BazelCcToolchainVars generates bzl file content containing variables for
+// Bazel's cc_toolchain configuration.
+func BazelCcToolchainVars(config android.Config) string {
+	return android.BazelToolchainVars(config, exportedVars)
+}
+
+func ExportStringList(name string, value []string) {
+	exportedVars.ExportStringList(name, value)
+}
 
 func init() {
 	if runtime.GOOS == "linux" {
 		commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
 	}
 
-	exportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
-	exportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
-	exportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
-	exportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
-	exportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags)
-	exportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags)
-	exportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
+	exportedVars.ExportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
+	exportedVars.ExportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
+	exportedVars.ExportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
+	exportedVars.ExportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
+	exportedVars.ExportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags)
+	exportedVars.ExportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags)
+	exportedVars.ExportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
 
 	// Export the static default CommonGlobalCflags to Bazel.
 	// TODO(187086342): handle cflags that are set in VariableFuncs.
@@ -320,7 +331,7 @@
 			"-ftrivial-auto-var-init=zero",
 			"-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang",
 		}...)
-	exportedStringListVars.Set("CommonGlobalCflags", bazelCommonGlobalCflags)
+	exportedVars.ExportStringList("CommonGlobalCflags", bazelCommonGlobalCflags)
 
 	pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string {
 		flags := commonGlobalCflags
@@ -349,17 +360,17 @@
 
 	// Export the static default DeviceGlobalCflags to Bazel.
 	// TODO(187086342): handle cflags that are set in VariableFuncs.
-	exportedStringListVars.Set("DeviceGlobalCflags", deviceGlobalCflags)
+	exportedVars.ExportStringList("DeviceGlobalCflags", deviceGlobalCflags)
 
 	pctx.VariableFunc("DeviceGlobalCflags", func(ctx android.PackageVarContext) string {
 		return strings.Join(deviceGlobalCflags, " ")
 	})
 
-	exportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
-	exportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
-	exportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
-	exportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
-	exportStringListStaticVariable("ExternalCflags", extraExternalCflags)
+	exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
+	exportedVars.ExportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+	exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
+	exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
+	exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
 
 	// Everything in these lists is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
@@ -374,11 +385,11 @@
 		"frameworks/native/opengl/include",
 		"frameworks/av/include",
 	}
-	exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes)
+	exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes)
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
 
-	exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
-	exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
+	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
 
 	pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase)
 	pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
@@ -418,7 +429,7 @@
 	pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
 }
 
-var HostPrebuiltTag = exportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+var HostPrebuiltTag = exportedVars.ExportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
 
 func ClangPath(ctx android.PathContext, file string) android.SourcePath {
 	type clangToolKey string
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 164e7a6..aebda0b 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -91,26 +91,26 @@
 	pctx.SourcePathVariable("X86_64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
 
-	exportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
-	exportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
+	exportedVars.ExportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
+	exportedVars.ExportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
 
-	exportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
-	exportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
+	exportedVars.ExportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
+	exportedVars.ExportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
 
 	// Clang cflags
-	exportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
-	exportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
+	exportedVars.ExportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
+	exportedVars.ExportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
 
 	// Yasm flags
-	exportStringListStaticVariable("X86_64YasmFlags", []string{
+	exportedVars.ExportStringListStaticVariable("X86_64YasmFlags", []string{
 		"-f elf64",
 		"-m amd64",
 	})
 
 	// Extended cflags
 
-	exportedStringListDictVars.Set("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
-	exportedStringListDictVars.Set("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
+	exportedVars.ExportStringListDict("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
+	exportedVars.ExportStringListDict("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
 
 	// Architecture variant cflags
 	for variant, cflags := range x86_64ArchVariantCflags {
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index e32e1bd..421b083 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -98,25 +98,25 @@
 	pctx.SourcePathVariable("X86GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
 
-	exportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
-	exportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
+	exportedVars.ExportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
+	exportedVars.ExportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
 
-	exportStringListStaticVariable("X86Ldflags", x86Ldflags)
-	exportStringListStaticVariable("X86Lldflags", x86Ldflags)
+	exportedVars.ExportStringListStaticVariable("X86Ldflags", x86Ldflags)
+	exportedVars.ExportStringListStaticVariable("X86Lldflags", x86Ldflags)
 
 	// Clang cflags
-	exportStringListStaticVariable("X86Cflags", x86Cflags)
-	exportStringListStaticVariable("X86Cppflags", x86Cppflags)
+	exportedVars.ExportStringListStaticVariable("X86Cflags", x86Cflags)
+	exportedVars.ExportStringListStaticVariable("X86Cppflags", x86Cppflags)
 
 	// Yasm flags
-	exportStringListStaticVariable("X86YasmFlags", []string{
+	exportedVars.ExportStringListStaticVariable("X86YasmFlags", []string{
 		"-f elf32",
 		"-m x86",
 	})
 
 	// Extended cflags
-	exportedStringListDictVars.Set("X86ArchVariantCflags", x86ArchVariantCflags)
-	exportedStringListDictVars.Set("X86ArchFeatureCflags", x86ArchFeatureCflags)
+	exportedVars.ExportStringListDict("X86ArchVariantCflags", x86ArchVariantCflags)
+	exportedVars.ExportStringListDict("X86ArchFeatureCflags", x86ArchFeatureCflags)
 
 	// Architecture variant cflags
 	for variant, cflags := range x86ArchVariantCflags {
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index e1659d3..4e8fd77 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -121,40 +121,40 @@
 )
 
 func init() {
-	exportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
-	exportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+	exportedVars.ExportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
+	exportedVars.ExportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
 
 	// Most places use the full GCC version. A few only use up to the first two numbers.
 	if p := strings.Split(linuxGccVersion, "."); len(p) > 2 {
-		exportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
+		exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
 	} else {
-		exportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
+		exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
 	}
 
-	exportSourcePathVariable("LinuxGccRoot",
+	exportedVars.ExportSourcePathVariable("LinuxGccRoot",
 		"prebuilts/gcc/linux-x86/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
 
-	exportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
+	exportedVars.ExportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
 
-	exportStringListStaticVariable("LinuxCflags", linuxCflags)
-	exportStringListStaticVariable("LinuxLdflags", linuxLdflags)
-	exportStringListStaticVariable("LinuxLldflags", linuxLdflags)
-	exportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
-	exportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
-	exportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
-	exportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
-	exportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
-	exportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxCflags", linuxCflags)
+	exportedVars.ExportStringListStaticVariable("LinuxLdflags", linuxLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxLldflags", linuxLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
+	exportedVars.ExportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
+	exportedVars.ExportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
+	exportedVars.ExportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
 
-	exportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
-	exportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
-	exportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
-	exportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
-	exportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
-	exportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags)
+	exportedVars.ExportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
+	exportedVars.ExportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
+	exportedVars.ExportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
+	exportedVars.ExportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
+	exportedVars.ExportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
+	exportedVars.ExportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags)
 	// Yasm flags
-	exportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
-	exportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
+	exportedVars.ExportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
+	exportedVars.ExportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
 }
 
 type toolchainLinux struct {
diff --git a/cc/linkable.go b/cc/linkable.go
index d4b4770..6bec30c 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -176,10 +176,14 @@
 	IsVndk() bool
 	IsVndkExt() bool
 	IsVndkPrivate() bool
+	IsVendorPublicLibrary() bool
+	IsVndkPrebuiltLibrary() bool
 	HasVendorVariant() bool
 	HasProductVariant() bool
 	HasNonSystemVariants() bool
+	ProductSpecific() bool
 	InProduct() bool
+	SdkAndPlatformVariantVisibleToMake() bool
 
 	// SubName returns the modules SubName, used for image and NDK/SDK variations.
 	SubName() string
diff --git a/cc/pgo.go b/cc/pgo.go
index aa0feae..0632c15 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -32,8 +32,8 @@
 	}
 
 	globalPgoProfileProjects = []string{
-		"toolchain/pgo-profiles",
-		"vendor/google_data/pgo_profile",
+		"toolchain/pgo-profiles/pgo",
+		"vendor/google_data/pgo_profile/pgo",
 	}
 )
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 5980319..4ad8a90 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -253,6 +253,7 @@
 func NewPrebuiltLibrary(hod android.HostOrDeviceSupported, srcsProperty string) (*Module, *libraryDecorator) {
 	module, library := NewLibrary(hod)
 	module.compiler = nil
+	module.bazelable = true
 
 	prebuilt := &prebuiltLibraryLinker{
 		libraryDecorator: library,
@@ -338,8 +339,21 @@
 	Export_system_includes bazel.StringListAttribute
 }
 
-func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module) {
-	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+// TODO(b/228623543): The below is not entirely true until the bug is fixed. For now, both targets are always generated
+// Implements bp2build for cc_prebuilt_library modules. This will generate:
+// * Only a prebuilt_library_static if the shared.enabled property is set to false across all variants.
+// * Only a prebuilt_library_shared if the static.enabled property is set to false across all variants
+// * Both a prebuilt_library_static and prebuilt_library_shared if the aforementioned properties are not false across
+//   all variants
+//
+// In all cases, prebuilt_library_static target names will be appended with "_bp2build_cc_library_static".
+func prebuiltLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	prebuiltLibraryStaticBp2Build(ctx, module, true)
+	prebuiltLibrarySharedBp2Build(ctx, module)
+}
+
+func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module, fullBuild bool) {
+	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, true)
 	exportedIncludes := Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx, module)
 
 	attrs := &bazelPrebuiltLibraryStaticAttributes{
@@ -354,7 +368,10 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+	if fullBuild {
+		name += "_bp2build_cc_library_static"
+	}
+	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
 }
 
 type bazelPrebuiltLibrarySharedAttributes struct {
@@ -362,7 +379,7 @@
 }
 
 func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
-	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, false)
 
 	attrs := &bazelPrebuiltLibrarySharedAttributes{
 		Shared_library: prebuiltAttrs.Src,
@@ -374,7 +391,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
 }
 
 type prebuiltObjectProperties struct {
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 03ce2d5..4f7451d 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -750,7 +750,7 @@
 		return false
 	}
 	defer fileA.Close()
-	fileB, err := os.Open(a)
+	fileB, err := os.Open(b)
 	if err != nil {
 		return false
 	}
diff --git a/cmd/sbox/sbox_test.go b/cmd/sbox/sbox_test.go
new file mode 100644
index 0000000..3f13d2f
--- /dev/null
+++ b/cmd/sbox/sbox_test.go
@@ -0,0 +1,127 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+)
+
+func Test_filesHaveSameContents(t *testing.T) {
+
+	tests := []struct {
+		name     string
+		a        string
+		b        string
+		missingA bool
+		missingB bool
+
+		equal bool
+	}{
+		{
+			name:  "empty",
+			a:     "",
+			b:     "",
+			equal: true,
+		},
+		{
+			name:  "equal",
+			a:     "foo",
+			b:     "foo",
+			equal: true,
+		},
+		{
+			name:  "unequal",
+			a:     "foo",
+			b:     "bar",
+			equal: false,
+		},
+		{
+			name:  "unequal different sizes",
+			a:     "foo",
+			b:     "foobar",
+			equal: false,
+		},
+		{
+			name:  "equal large",
+			a:     strings.Repeat("a", 2*1024*1024),
+			b:     strings.Repeat("a", 2*1024*1024),
+			equal: true,
+		},
+		{
+			name:  "equal large unaligned",
+			a:     strings.Repeat("a", 2*1024*1024+10),
+			b:     strings.Repeat("a", 2*1024*1024+10),
+			equal: true,
+		},
+		{
+			name:  "unequal large",
+			a:     strings.Repeat("a", 2*1024*1024),
+			b:     strings.Repeat("a", 2*1024*1024-1) + "b",
+			equal: false,
+		},
+		{
+			name:  "unequal large unaligned",
+			a:     strings.Repeat("a", 2*1024*1024+10),
+			b:     strings.Repeat("a", 2*1024*1024+9) + "b",
+			equal: false,
+		},
+		{
+			name:     "missing a",
+			missingA: true,
+			b:        "foo",
+			equal:    false,
+		},
+		{
+			name:     "missing b",
+			a:        "foo",
+			missingB: true,
+			equal:    false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tempDir, err := os.MkdirTemp("", "testFilesHaveSameContents")
+			if err != nil {
+				t.Fatalf("failed to create temp dir: %s", err)
+			}
+			defer os.RemoveAll(tempDir)
+
+			fileA := filepath.Join(tempDir, "a")
+			fileB := filepath.Join(tempDir, "b")
+
+			if !tt.missingA {
+				err := ioutil.WriteFile(fileA, []byte(tt.a), 0666)
+				if err != nil {
+					t.Fatalf("failed to write %s: %s", fileA, err)
+				}
+			}
+
+			if !tt.missingB {
+				err := ioutil.WriteFile(fileB, []byte(tt.b), 0666)
+				if err != nil {
+					t.Fatalf("failed to write %s: %s", fileB, err)
+				}
+			}
+
+			if got := filesHaveSameContents(fileA, fileB); got != tt.equal {
+				t.Errorf("filesHaveSameContents() = %v, want %v", got, tt.equal)
+			}
+		})
+	}
+}
diff --git a/cmd/symbols_map/elf.go b/cmd/symbols_map/elf.go
index 3c8b1e4..950e3b2 100644
--- a/cmd/symbols_map/elf.go
+++ b/cmd/symbols_map/elf.go
@@ -38,13 +38,13 @@
 	return elfIdentifierFromReaderAt(f, filename, allowMissing)
 }
 
-// elfIdentifier extracts the elf build ID from a ReaderAt.  If allowMissing is true it returns
-// an empty identifier if the file exists but the build ID note does not.
+// elfIdentifierFromReaderAt extracts the elf build ID from a ReaderAt.  If allowMissing is true it
+// returns an empty identifier if the file exists but the build ID note does not.
 func elfIdentifierFromReaderAt(r io.ReaderAt, filename string, allowMissing bool) (string, error) {
 	f, err := elf.NewFile(r)
 	if err != nil {
 		if allowMissing {
-			if errors.Is(err, io.EOF) {
+			if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
 				return "", nil
 			}
 			if _, ok := err.(*elf.FormatError); ok {
diff --git a/cmd/symbols_map/elf_test.go b/cmd/symbols_map/elf_test.go
index b96ea59..a94c87f 100644
--- a/cmd/symbols_map/elf_test.go
+++ b/cmd/symbols_map/elf_test.go
@@ -39,6 +39,10 @@
 			name:     "empty elf",
 			contents: emptyElfFile(),
 		},
+		{
+			name:     "short section header",
+			contents: shortSectionHeaderElfFile(),
+		},
 	}
 
 	for _, tt := range tests {
@@ -111,3 +115,38 @@
 	binary.Write(buf, binary.LittleEndian, header)
 	return buf.String()
 }
+
+// shortSectionHeader returns an elf file header with a section header that extends past the end of
+// the file.
+func shortSectionHeaderElfFile() string {
+	ident := [elf.EI_NIDENT]byte{}
+	identBuf := bytes.NewBuffer(ident[0:0:elf.EI_NIDENT])
+	binary.Write(identBuf, binary.LittleEndian, []byte("\x7fELF"))
+	binary.Write(identBuf, binary.LittleEndian, elf.ELFCLASS64)
+	binary.Write(identBuf, binary.LittleEndian, elf.ELFDATA2LSB)
+	binary.Write(identBuf, binary.LittleEndian, elf.EV_CURRENT)
+	binary.Write(identBuf, binary.LittleEndian, elf.ELFOSABI_LINUX)
+	binary.Write(identBuf, binary.LittleEndian, make([]byte, 8))
+
+	header := elf.Header64{
+		Ident:     ident,
+		Type:      uint16(elf.ET_EXEC),
+		Machine:   uint16(elf.EM_X86_64),
+		Version:   uint32(elf.EV_CURRENT),
+		Entry:     0,
+		Phoff:     uint64(binary.Size(elf.Header64{})),
+		Shoff:     uint64(binary.Size(elf.Header64{})),
+		Flags:     0,
+		Ehsize:    uint16(binary.Size(elf.Header64{})),
+		Phentsize: 0x38,
+		Phnum:     0,
+		Shentsize: 0x40,
+		Shnum:     1,
+		Shstrndx:  0,
+	}
+
+	buf := &bytes.Buffer{}
+	binary.Write(buf, binary.LittleEndian, header)
+	binary.Write(buf, binary.LittleEndian, []byte{0})
+	return buf.String()
+}
diff --git a/java/aar.go b/java/aar.go
index 8e10253..00ff7e7 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -598,16 +598,26 @@
 // AAR (android library) prebuilts
 //
 
+// Properties for android_library_import
 type AARImportProperties struct {
+	// ARR (android library prebuilt) filepath. Exactly one ARR is required.
 	Aars []string `android:"path"`
-
-	Sdk_version     *string
+	// If not blank, set to the version of the sdk to compile against.
+	// Defaults to private.
+	// Values are of one of the following forms:
+	// 1) numerical API level, "current", "none", or "core_platform"
+	// 2) An SDK kind with an API level: "<sdk kind>_<API level>"
+	// See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
+	// If the SDK kind is empty, it will be set to public
+	Sdk_version *string
+	// If not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+	// Defaults to sdk_version if not set. See sdk_version for possible values.
 	Min_sdk_version *string
-
+	// List of java static libraries that the included ARR (android library prebuilts) has dependencies to.
 	Static_libs []string
-	Libs        []string
-
-	// if set to true, run Jetifier against .aar file. Defaults to false.
+	// List of java libraries that the included ARR (android library prebuilts) has dependencies to.
+	Libs []string
+	// If set to true, run Jetifier against .aar file. Defaults to false.
 	Jetifier *bool
 }
 
diff --git a/java/app.go b/java/app.go
index 21ee34e..2b52eab 100755
--- a/java/app.go
+++ b/java/app.go
@@ -638,7 +638,21 @@
 	}
 
 	certificates := processMainCert(a.ModuleBase, a.getCertString(ctx), certificateDeps, ctx)
-	a.certificate = certificates[0]
+
+	// This can be reached with an empty certificate list if AllowMissingDependencies is set
+	// and the certificate property for this module is a module reference to a missing module.
+	if len(certificates) > 0 {
+		a.certificate = certificates[0]
+	} else {
+		if !ctx.Config().AllowMissingDependencies() && len(ctx.GetMissingDependencies()) > 0 {
+			panic("Should only get here if AllowMissingDependencies set and there are missing dependencies")
+		}
+		// Set a certificate to avoid panics later when accessing it.
+		a.certificate = Certificate{
+			Key: android.PathForModuleOut(ctx, "missing.pk8"),
+			Pem: android.PathForModuleOut(ctx, "missing.pem"),
+		}
+	}
 
 	// Build a final signed app package.
 	packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk")
diff --git a/java/app_import.go b/java/app_import.go
index a1c4d58..b017eca 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -466,7 +466,7 @@
 //                 apk: "prebuilts/example_xhdpi.apk",
 //             },
 //         },
-//         certificate: "PRESIGNED",
+//         presigned: true,
 //     }
 func AndroidAppImportFactory() android.Module {
 	module := &AndroidAppImport{}
diff --git a/java/app_test.go b/java/app_test.go
index 08baf54..6a4508c 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2948,3 +2948,24 @@
 		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
 	}
 }
+
+func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.PrepareForTestWithAllowMissingDependencies,
+		android.PrepareForTestWithAndroidMk,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			certificate: ":missing_certificate",
+			sdk_version: "current",
+		}`)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	fooApk := foo.Output("foo.apk")
+	if fooApk.Rule != android.ErrorRule {
+		t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
+	}
+	android.AssertStringDoesContain(t, "expected error rule message", fooApk.Args["error"], "missing dependencies: missing_certificate\n")
+}
diff --git a/java/base.go b/java/base.go
index 5802099..6f12f6c 100644
--- a/java/base.go
+++ b/java/base.go
@@ -185,12 +185,12 @@
 // constructing a new module.
 type DeviceProperties struct {
 	// If not blank, set to the version of the sdk to compile against.
-	// Defaults to compiling against the current platform.
+	// Defaults to private.
 	// Values are of one of the following forms:
-	// 1) numerical API level or "current"
-	// 2) An SDK kind with an API level: "<sdk kind>_<API level>". See
-	// build/soong/android/sdk_version.go for the complete and up to date list of
-	// SDK kinds. If the SDK kind value is empty, it will be set to public.
+	// 1) numerical API level, "current", "none", or "core_platform"
+	// 2) An SDK kind with an API level: "<sdk kind>_<API level>"
+	// See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
+	// If the SDK kind is empty, it will be set to public.
 	Sdk_version *string
 
 	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
@@ -1694,6 +1694,8 @@
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
 	}
 	dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
+	dpInfo.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...)
+	dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...)
 }
 
 func (j *Module) CompilerDeps() []string {
diff --git a/java/config/config.go b/java/config/config.go
index 262c531..95b841f 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -26,7 +26,8 @@
 )
 
 var (
-	pctx = android.NewPackageContext("android/soong/java/config")
+	pctx         = android.NewPackageContext("android/soong/java/config")
+	exportedVars = android.NewExportedVariables(pctx)
 
 	LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
 	LegacyCorePlatformSystemModules          = "legacy-core-platform-api-stubs-system-modules"
@@ -53,25 +54,40 @@
 	}
 )
 
-const (
-	JavaVmFlags  = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
-	JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1`
+var (
+	JavacVmFlags    = strings.Join(javacVmFlagsList, " ")
+	javaVmFlagsList = []string{
+		`-XX:OnError="cat hs_err_pid%p.log"`,
+		"-XX:CICompilerCount=6",
+		"-XX:+UseDynamicNumberOfGCThreads",
+	}
+	javacVmFlagsList = []string{
+		`-J-XX:OnError="cat hs_err_pid%p.log"`,
+		"-J-XX:CICompilerCount=6",
+		"-J-XX:+UseDynamicNumberOfGCThreads",
+		"-J-XX:+TieredCompilation",
+		"-J-XX:TieredStopAtLevel=1",
+	}
 )
 
 func init() {
 	pctx.Import("github.com/google/blueprint/bootstrap")
 
-	pctx.StaticVariable("JavacHeapSize", "2048M")
-	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
+	exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M")
+	exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
 
 	// ErrorProne can use significantly more memory than javac alone, give it a higher heap
 	// size (b/221480398).
-	pctx.StaticVariable("ErrorProneHeapSize", "4096M")
-	pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
+	exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M")
+	exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
 
-	pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
+	exportedVars.ExportStringListStaticVariable("DexFlags", []string{
+		`-JXX:OnError="cat hs_err_pid%p.log"`,
+		"-JXX:CICompilerCount=6",
+		"-JXX:+UseDynamicNumberOfGCThreads",
+	})
 
-	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
+	exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
 		`-Xmaxerrs 9999999`,
 		`-encoding UTF-8`,
 		`-sourcepath ""`,
@@ -85,10 +101,10 @@
 
 		// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
 		`-XDstringConcat=inline`,
-	}, " "))
+	})
 
-	pctx.StaticVariable("JavaVmFlags", JavaVmFlags)
-	pctx.StaticVariable("JavacVmFlags", JavacVmFlags)
+	exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList)
+	exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList)
 
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 
@@ -184,6 +200,10 @@
 	hostJNIToolVariableWithSdkToolsPrebuilt("SignapkJniLibrary", "libconscrypt_openjdk_jni")
 }
 
+func BazelJavaToolchainVars(config android.Config) string {
+	return android.BazelToolchainVars(config, exportedVars)
+}
+
 func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) {
 	pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
 		if ctx.Config().AlwaysUsePrebuiltSdks() {
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index 48681b5..5f853c8 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -16,8 +16,6 @@
 
 import (
 	"strings"
-
-	"android/soong/android"
 )
 
 var (
@@ -31,23 +29,23 @@
 )
 
 // Wrapper that grabs value of val late so it can be initialized by a later module's init function
-func errorProneVar(name string, val *[]string, sep string) {
-	pctx.VariableFunc(name, func(android.PackageVarContext) string {
+func errorProneVar(val *[]string, sep string) func() string {
+	return func() string {
 		return strings.Join(*val, sep)
-	})
+	}
 }
 
 func init() {
-	errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":")
-	errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ")
-	errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ")
-	errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ")
-	errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ")
-	errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ")
-	pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{
+	exportedVars.ExportVariableFuncVariable("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":"))
+	exportedVars.ExportVariableFuncVariable("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " "))
+	exportedVars.ExportVariableFuncVariable("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " "))
+	exportedVars.ExportVariableFuncVariable("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " "))
+	exportedVars.ExportVariableFuncVariable("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " "))
+	exportedVars.ExportVariableFuncVariable("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " "))
+	exportedVars.ExportStringListStaticVariable("ErrorProneChecks", []string{
 		"${ErrorProneChecksOff}",
 		"${ErrorProneChecksError}",
 		"${ErrorProneChecksWarning}",
 		"${ErrorProneChecksDefaultDisabled}",
-	}, " "))
+	})
 }
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 95ded34..534a814 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -295,6 +295,12 @@
 	return dexJar.Path()
 }
 
+// HIDDENAPI_STUB_FLAGS_IMPL_FLAGS is the set of flags that identify implementation only signatures,
+// i.e. those signatures that are not part of any API (including the hidden API).
+var HIDDENAPI_STUB_FLAGS_IMPL_FLAGS = []string{}
+
+var HIDDENAPI_FLAGS_CSV_IMPL_FLAGS = []string{"blocked"}
+
 // buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
 //
 // The rule is initialized but not built so that the caller can modify it and select an appropriate
@@ -345,7 +351,8 @@
 	// If there are stub flag files that have been generated by fragments on which this depends then
 	// use them to validate the stub flag file generated by the rules created by this method.
 	if len(stubFlagSubsets) > 0 {
-		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets)
+		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets,
+			HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)
 
 		// Add the file that indicates that the file generated by this is valid.
 		//
@@ -904,7 +911,8 @@
 	// If there are flag files that have been generated by fragments on which this depends then use
 	// them to validate the flag file generated by the rules created by this method.
 	if len(flagSubsets) > 0 {
-		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets)
+		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets,
+			HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)
 
 		// Add the file that indicates that the file generated by this is valid.
 		//
@@ -968,13 +976,29 @@
 	return patternsFile
 }
 
-// buildRuleRemoveBlockedFlag creates a rule that will remove entries from the input path which
-// only have blocked flags. It will not remove entries that have blocked as well as other flags,
-// e.g. blocked,core-platform-api.
-func buildRuleRemoveBlockedFlag(ctx android.BuilderContext, name string, desc string, inputPath android.Path, filteredPath android.WritablePath) {
+// buildRuleRemoveSignaturesWithImplementationFlags creates a rule that will remove signatures from
+// the input flags file which have only the implementation flags, i.e. are not part of an API.
+//
+// The implementationFlags specifies the set of default flags that identifies the signature of a
+// private, implementation only, member. Signatures that match those flags are removed from the
+// flags as they are implementation only.
+//
+// This is used to remove implementation only signatures from the signature files that are persisted
+// in the sdk snapshot as the sdk snapshots should not include implementation details. The
+// signatures generated by this method will be compared by the buildRuleValidateOverlappingCsvFiles
+// method which treats any missing signatures as if they were implementation only signatures.
+func buildRuleRemoveSignaturesWithImplementationFlags(ctx android.BuilderContext,
+	name string, desc string, inputPath android.Path, filteredPath android.WritablePath,
+	implementationFlags []string) {
+
 	rule := android.NewRuleBuilder(pctx, ctx)
+	implementationFlagPattern := ""
+	for _, implementationFlag := range implementationFlags {
+		implementationFlagPattern = implementationFlagPattern + "," + implementationFlag
+	}
 	rule.Command().
-		Text(`grep -vE "^[^,]+,blocked$"`).Input(inputPath).Text(">").Output(filteredPath).
+		Text(`grep -vE "^[^,]+` + implementationFlagPattern + `$"`).Input(inputPath).
+		Text(">").Output(filteredPath).
 		// Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
 		// something and 1 (build failure) when it does not and 2 (when it encounters an error).
 		// However, while it is unlikely it is not an error if this does not find any matches. The
@@ -986,7 +1010,14 @@
 
 // buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
 // by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
-func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets) android.WritablePath {
+//
+// The implementationFlags specifies the set of default flags that identifies the signature of a
+// private, implementation only, member. A signature which is present in a monolithic flags subset
+// defined by SignatureCsvSubset but which is not present in the flags file from the corresponding
+// module is assumed to be an implementation only member and so must have these flags.
+func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string,
+	monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets,
+	implementationFlags []string) android.WritablePath {
 	// The file which is used to record that the flags file is valid.
 	validFile := pathForValidation(ctx, monolithicFilePath)
 
@@ -994,14 +1025,19 @@
 	rule := android.NewRuleBuilder(pctx, ctx)
 	command := rule.Command().
 		BuiltTool("verify_overlaps").
-		Input(monolithicFilePath)
+		FlagWithInput("--monolithic-flags ", monolithicFilePath)
 
 	for _, subset := range csvSubsets {
 		command.
+			Flag("--module-flags ").
 			Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile).
 			Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile)
 	}
 
+	for _, implementationFlag := range implementationFlags {
+		command.FlagWithArg("--implementation-flag ", implementationFlag)
+	}
+
 	// If validation passes then update the file that records that.
 	command.Text("&& touch").Output(validFile)
 	rule.Build(name+"Validation", desc+" validation")
@@ -1075,12 +1111,16 @@
 	// Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
 	// compared against the monolithic stub flags.
 	filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
-	buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredStubFlags", "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV)
+	buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredStubFlags",
+		"modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV,
+		HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)
 
 	// Generate the filtered-flags.csv file which contains the filtered flags that will be compared
 	// against the monolithic flags.
 	filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
-	buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredFlags", "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV)
+	buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredFlags",
+		"modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV,
+		HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)
 
 	// Store the paths in the info for use by other modules and sdk snapshot generation.
 	output := HiddenAPIOutput{
diff --git a/java/java.go b/java/java.go
index 713fe94..b34d6de 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2041,6 +2041,10 @@
 // and also separates dependencies into dynamic dependencies and static dependencies.
 // Each corresponding Bazel target type, can have a different method for handling
 // dynamic vs. static dependencies, and so these are returned to the calling function.
+type eventLogTagsAttributes struct {
+	Srcs bazel.LabelListAttribute
+}
+
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) {
 	var srcs bazel.LabelListAttribute
 	archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
@@ -2055,11 +2059,32 @@
 
 	javaSrcPartition := "java"
 	protoSrcPartition := "proto"
+	logtagSrcPartition := "logtag"
 	srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
-		javaSrcPartition:  bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
-		protoSrcPartition: android.ProtoSrcLabelPartition,
+		javaSrcPartition:   bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
+		logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}},
+		protoSrcPartition:  android.ProtoSrcLabelPartition,
 	})
 
+	javaSrcs := srcPartitions[javaSrcPartition]
+
+	var logtagsSrcs bazel.LabelList
+	if !srcPartitions[logtagSrcPartition].IsEmpty() {
+		logtagsLibName := m.Name() + "_logtags"
+		logtagsSrcs = bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}})
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "event_log_tags",
+				Bzl_load_location: "//build/make/tools:event_log_tags.bzl",
+			},
+			android.CommonAttributes{Name: logtagsLibName},
+			&eventLogTagsAttributes{
+				Srcs: srcPartitions[logtagSrcPartition],
+			},
+		)
+	}
+	javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs))
+
 	var javacopts []string
 	if m.properties.Javacflags != nil {
 		javacopts = append(javacopts, m.properties.Javacflags...)
@@ -2071,7 +2096,7 @@
 	}
 
 	commonAttrs := &javaCommonAttributes{
-		Srcs: srcPartitions[javaSrcPartition],
+		Srcs: javaSrcs,
 		Plugins: bazel.MakeLabelListAttribute(
 			android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
 		),
diff --git a/java/jdeps.go b/java/jdeps.go
index eff9a31..3734335 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -71,6 +71,8 @@
 		dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
 		dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars)
 		dpInfo.Paths = android.FirstUniqueStrings(dpInfo.Paths)
+		dpInfo.Static_libs = android.FirstUniqueStrings(dpInfo.Static_libs)
+		dpInfo.Libs = android.FirstUniqueStrings(dpInfo.Libs)
 		moduleInfos[name] = dpInfo
 
 		mkProvider, ok := module.(android.AndroidMkDataProvider)
diff --git a/java/testing.go b/java/testing.go
index 82aa29b..4000334 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -70,6 +70,10 @@
 		defaultJavaDir + "/framework/aidl": nil,
 		// Needed for various deps defined in GatherRequiredDepsForTest()
 		defaultJavaDir + "/a.java": nil,
+
+		// Needed for R8 rules on apps
+		"build/make/core/proguard.flags":             nil,
+		"build/make/core/proguard_basic_keeps.flags": nil,
 	}.AddToFixture(),
 	// The java default module definitions.
 	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 54bb6d1..9266520 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -232,19 +232,18 @@
 }
 
 type variableRefExpr struct {
-	ref       variable
-	isDefined bool
+	ref variable
 }
 
-func NewVariableRefExpr(ref variable, isDefined bool) starlarkExpr {
+func NewVariableRefExpr(ref variable) starlarkExpr {
 	if predefined, ok := ref.(*predefinedVariable); ok {
 		return predefined.value
 	}
-	return &variableRefExpr{ref, isDefined}
+	return &variableRefExpr{ref}
 }
 
 func (v *variableRefExpr) emit(gctx *generationContext) {
-	v.ref.emitGet(gctx, v.isDefined)
+	v.ref.emitGet(gctx)
 }
 
 func (v *variableRefExpr) typ() starlarkType {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 0942c28..8f4fea4 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -80,7 +80,7 @@
 	"copy-files":                           &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList},
 	"dir":                                  &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeString},
 	"dist-for-goals":                       &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
-	"enforce-product-packages-exist":       &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid},
+	"enforce-product-packages-exist":       &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addHandle: true},
 	"error":                                &makeControlFuncParser{name: baseName + ".mkerror"},
 	"findstring":                           &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt},
 	"find-copy-subdir-files":               &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList},
@@ -107,8 +107,8 @@
 	"my-dir":                               &myDirCallParser{},
 	"patsubst":                             &substCallParser{fname: "patsubst"},
 	"product-copy-files-by-pattern":        &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList},
-	"require-artifacts-in-path":            &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid},
-	"require-artifacts-in-path-relaxed":    &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid},
+	"require-artifacts-in-path":            &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addHandle: true},
+	"require-artifacts-in-path-relaxed":    &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addHandle: true},
 	// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
 	"shell":    &shellCallParser{},
 	"sort":     &simpleCallParser{name: baseName + ".mksort", returnType: starlarkTypeList},
@@ -197,17 +197,57 @@
 	return s == "error" || s == "warning" || s == "info"
 }
 
+// varAssignmentScope points to the last assignment for each variable
+// in the current block. It is used during the parsing to chain
+// the assignments to a variable together.
+type varAssignmentScope struct {
+	outer *varAssignmentScope
+	vars  map[string]bool
+}
+
 // Starlark output generation context
 type generationContext struct {
-	buf          strings.Builder
-	starScript   *StarlarkScript
-	indentLevel  int
-	inAssignment bool
-	tracedCount  int
+	buf            strings.Builder
+	starScript     *StarlarkScript
+	indentLevel    int
+	inAssignment   bool
+	tracedCount    int
+	varAssignments *varAssignmentScope
 }
 
 func NewGenerateContext(ss *StarlarkScript) *generationContext {
-	return &generationContext{starScript: ss}
+	return &generationContext{
+		starScript: ss,
+		varAssignments: &varAssignmentScope{
+			outer: nil,
+			vars:  make(map[string]bool),
+		},
+	}
+}
+
+func (gctx *generationContext) pushVariableAssignments() {
+	va := &varAssignmentScope{
+		outer: gctx.varAssignments,
+		vars:  make(map[string]bool),
+	}
+	gctx.varAssignments = va
+}
+
+func (gctx *generationContext) popVariableAssignments() {
+	gctx.varAssignments = gctx.varAssignments.outer
+}
+
+func (gctx *generationContext) hasBeenAssigned(v variable) bool {
+	for va := gctx.varAssignments; va != nil; va = va.outer {
+		if _, ok := va.vars[v.name()]; ok {
+			return true
+		}
+	}
+	return false
+}
+
+func (gctx *generationContext) setHasBeenAssigned(v variable) {
+	gctx.varAssignments.vars[v.name()] = true
 }
 
 // emit returns generated script
@@ -394,14 +434,6 @@
 	nodeLocator    func(pos mkparser.Pos) int
 }
 
-// varAssignmentScope points to the last assignment for each variable
-// in the current block. It is used during the parsing to chain
-// the assignments to a variable together.
-type varAssignmentScope struct {
-	outer *varAssignmentScope
-	vars  map[string]*assignmentNode
-}
-
 // parseContext holds the script we are generating and all the ephemeral data
 // needed during the parsing.
 type parseContext struct {
@@ -415,7 +447,6 @@
 	errorLogger      ErrorLogger
 	tracedVariables  map[string]bool // variables to be traced in the generated script
 	variables        map[string]variable
-	varAssignments   *varAssignmentScope
 	outputDir        string
 	dependentModules map[string]*moduleInfo
 	soongNamespaces  map[string]map[string]bool
@@ -468,7 +499,6 @@
 		typeHints:        make(map[string]starlarkType),
 		atTopOfMakefile:  true,
 	}
-	ctx.pushVarAssignments()
 	for _, item := range predefined {
 		ctx.variables[item.name] = &predefinedVariable{
 			baseVariable: baseVariable{nam: item.name, typ: starlarkTypeString},
@@ -479,31 +509,6 @@
 	return ctx
 }
 
-func (ctx *parseContext) lastAssignment(v variable) *assignmentNode {
-	for va := ctx.varAssignments; va != nil; va = va.outer {
-		if v, ok := va.vars[v.name()]; ok {
-			return v
-		}
-	}
-	return nil
-}
-
-func (ctx *parseContext) setLastAssignment(v variable, asgn *assignmentNode) {
-	ctx.varAssignments.vars[v.name()] = asgn
-}
-
-func (ctx *parseContext) pushVarAssignments() {
-	va := &varAssignmentScope{
-		outer: ctx.varAssignments,
-		vars:  make(map[string]*assignmentNode),
-	}
-	ctx.varAssignments = va
-}
-
-func (ctx *parseContext) popVarAssignments() {
-	ctx.varAssignments = ctx.varAssignments.outer
-}
-
 func (ctx *parseContext) hasNodes() bool {
 	return ctx.currentNodeIndex < len(ctx.nodes)
 }
@@ -585,8 +590,6 @@
 		asgn.value = &toStringExpr{expr: asgn.value}
 	}
 
-	asgn.previous = ctx.lastAssignment(lhs)
-	ctx.setLastAssignment(lhs, asgn)
 	switch a.Type {
 	case "=", ":=":
 		asgn.flavor = asgnSet
@@ -952,11 +955,8 @@
 func (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase {
 	block := &switchCase{gate: ctx.parseCondition(check)}
 	defer func() {
-		ctx.popVarAssignments()
 		ctx.ifNestLevel--
-
 	}()
-	ctx.pushVarAssignments()
 	ctx.ifNestLevel++
 
 	for ctx.hasNodes() {
@@ -980,7 +980,7 @@
 		if !check.Args.Const() {
 			return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump())
 		}
-		v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]), false)
+		v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]))
 		if strings.HasSuffix(check.Name, "ndef") {
 			v = &notExpr{v}
 		}
@@ -1241,7 +1241,7 @@
 		}
 		name = words[0].Dump()
 		if len(words) < 2 {
-			args = &mkparser.MakeString{}
+			args = mkparser.SimpleMakeString("", words[0].Pos())
 		} else {
 			args = words[1]
 		}
@@ -1293,12 +1293,12 @@
 				args: []starlarkExpr{
 					&stringLiteralExpr{literal: substParts[0]},
 					&stringLiteralExpr{literal: substParts[1]},
-					NewVariableRefExpr(v, ctx.lastAssignment(v) != nil),
+					NewVariableRefExpr(v),
 				},
 			}
 		}
 		if v := ctx.addVariable(refDump); v != nil {
-			return NewVariableRefExpr(v, ctx.lastAssignment(v) != nil)
+			return NewVariableRefExpr(v)
 		}
 		return ctx.newBadExpr(node, "unknown variable %s", refDump)
 	}
@@ -1394,7 +1394,7 @@
 		return ctx.newBadExpr(node, "is-product-in-list requires an argument")
 	}
 	return &inExpr{
-		expr:  &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
+		expr:  NewVariableRefExpr(ctx.addVariable("TARGET_PRODUCT")),
 		list:  maybeConvertToStringList(ctx.parseMakeString(node, args)),
 		isNot: false,
 	}
@@ -1407,8 +1407,8 @@
 		return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform")
 	}
 	return &inExpr{
-		expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
-		list:  &variableRefExpr{ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS"), true},
+		expr:  NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")),
+		list:  NewVariableRefExpr(ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS")),
 		isNot: false,
 	}
 }
@@ -1420,8 +1420,8 @@
 		return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments")
 	}
 	return &inExpr{
-		expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
-		list:  &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
+		expr:  NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")),
+		list:  NewVariableRefExpr(ctx.addVariable("QCOM_BOARD_PLATFORMS")),
 		isNot: false,
 	}
 }
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 9c2b392..de75129 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -642,7 +642,7 @@
     pass
   elif not rblf.board_platform_is(g, "copper"):
     pass
-  elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
+  elif g.get("TARGET_BOARD_PLATFORM", "") not in g.get("QCOM_BOARD_PLATFORMS", ""):
     pass
   elif g["TARGET_PRODUCT"] in g.get("PLATFORM_LIST", []):
     pass
@@ -665,7 +665,7 @@
     pass
   elif not rblf.board_platform_is(g, "copper"):
     pass
-  elif g.get("TARGET_BOARD_PLATFORM", "") in g["QCOM_BOARD_PLATFORMS"]:
+  elif g.get("TARGET_BOARD_PLATFORM", "") in g.get("QCOM_BOARD_PLATFORMS", ""):
     pass
 `,
 	},
@@ -773,10 +773,10 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  rblf.enforce_product_packages_exist("")
-  rblf.enforce_product_packages_exist("foo")
-  rblf.require_artifacts_in_path("foo", "bar")
-  rblf.require_artifacts_in_path_relaxed("foo", "bar")
+  rblf.enforce_product_packages_exist(handle, "")
+  rblf.enforce_product_packages_exist(handle, "foo")
+  rblf.require_artifacts_in_path(handle, "foo", "bar")
+  rblf.require_artifacts_in_path_relaxed(handle, "foo", "bar")
   rblf.mkdist_for_goals(g, "goal", "from:to")
   rblf.add_product_dex_preopt_module_config(handle, "MyModule", "disable")
 `,
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index c0c4c98..7c39b9e 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -196,7 +196,6 @@
 	flavor   assignmentFlavor
 	location ErrorLocation
 	isTraced bool
-	previous *assignmentNode
 }
 
 func (asgn *assignmentNode) emit(gctx *generationContext) {
@@ -209,7 +208,7 @@
 		gctx.newLine()
 		gctx.tracedCount++
 		gctx.writef(`print("%s.%d: %s := ", `, gctx.starScript.mkFile, gctx.tracedCount, asgn.lhs.name())
-		asgn.lhs.emitGet(gctx, true)
+		asgn.lhs.emitGet(gctx)
 		gctx.writef(")")
 	}
 }
@@ -271,6 +270,7 @@
 func (cb *switchCase) emit(gctx *generationContext) {
 	cb.gate.emit(gctx)
 	gctx.indentLevel++
+	gctx.pushVariableAssignments()
 	hasStatements := false
 	for _, node := range cb.nodes {
 		if _, ok := node.(*commentNode); !ok {
@@ -282,6 +282,7 @@
 		gctx.emitPass()
 	}
 	gctx.indentLevel--
+	gctx.popVariableAssignments()
 }
 
 // A single complete if ... elseif ... else ... endif sequences
@@ -302,6 +303,7 @@
 }
 
 func (f *foreachNode) emit(gctx *generationContext) {
+	gctx.pushVariableAssignments()
 	gctx.newLine()
 	gctx.writef("for %s in ", f.varName)
 	f.list.emit(gctx)
@@ -318,4 +320,5 @@
 		gctx.emitPass()
 	}
 	gctx.indentLevel--
+	gctx.popVariableAssignments()
 }
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index be1b174..0a26ed8 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -21,9 +21,8 @@
 
 type variable interface {
 	name() string
-	emitGet(gctx *generationContext, isDefined bool)
+	emitGet(gctx *generationContext)
 	emitSet(gctx *generationContext, asgn *assignmentNode)
-	emitDefined(gctx *generationContext)
 	valueType() starlarkType
 	setValueType(t starlarkType)
 	defaultValueString() string
@@ -74,13 +73,11 @@
 
 func (pcv productConfigVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
 	emitAssignment := func() {
-		pcv.emitGet(gctx, true)
-		gctx.write(" = ")
+		gctx.writef("cfg[%q] = ", pcv.nam)
 		asgn.value.emitListVarCopy(gctx)
 	}
 	emitAppend := func() {
-		pcv.emitGet(gctx, true)
-		gctx.write(" += ")
+		gctx.writef("cfg[%q] += ", pcv.nam)
 		value := asgn.value
 		if pcv.valueType() == starlarkTypeString {
 			gctx.writef(`" " + `)
@@ -98,7 +95,7 @@
 	}
 
 	// If we are not sure variable has been assigned before, emit setdefault
-	needsSetDefault := asgn.previous == nil && !pcv.isPreset() && asgn.isSelfReferential()
+	needsSetDefault := !gctx.hasBeenAssigned(&pcv) && !pcv.isPreset() && asgn.isSelfReferential()
 
 	switch asgn.flavor {
 	case asgnSet:
@@ -121,34 +118,30 @@
 		emitAssignment()
 		gctx.indentLevel--
 	}
+
+	gctx.setHasBeenAssigned(&pcv)
 }
 
-func (pcv productConfigVariable) emitGet(gctx *generationContext, isDefined bool) {
-	if isDefined || pcv.isPreset() {
+func (pcv productConfigVariable) emitGet(gctx *generationContext) {
+	if gctx.hasBeenAssigned(&pcv) || pcv.isPreset() {
 		gctx.writef("cfg[%q]", pcv.nam)
 	} else {
 		gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString())
 	}
 }
 
-func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
-	gctx.writef("cfg.get(%q) != None", pcv.name())
-}
-
 type otherGlobalVariable struct {
 	baseVariable
 }
 
 func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
 	emitAssignment := func() {
-		scv.emitGet(gctx, true)
-		gctx.write(" = ")
+		gctx.writef("g[%q] = ", scv.nam)
 		asgn.value.emitListVarCopy(gctx)
 	}
 
 	emitAppend := func() {
-		scv.emitGet(gctx, true)
-		gctx.write(" += ")
+		gctx.writef("g[%q] += ", scv.nam)
 		value := asgn.value
 		if scv.valueType() == starlarkTypeString {
 			gctx.writef(`" " + `)
@@ -158,7 +151,7 @@
 	}
 
 	// If we are not sure variable has been assigned before, emit setdefault
-	needsSetDefault := asgn.previous == nil && !scv.isPreset() && asgn.isSelfReferential()
+	needsSetDefault := !gctx.hasBeenAssigned(&scv) && !scv.isPreset() && asgn.isSelfReferential()
 
 	switch asgn.flavor {
 	case asgnSet:
@@ -184,28 +177,22 @@
 		emitAssignment()
 		gctx.indentLevel--
 	}
+
+	gctx.setHasBeenAssigned(&scv)
 }
 
-func (scv otherGlobalVariable) emitGet(gctx *generationContext, isDefined bool) {
-	if isDefined || scv.isPreset() {
+func (scv otherGlobalVariable) emitGet(gctx *generationContext) {
+	if gctx.hasBeenAssigned(&scv) || scv.isPreset() {
 		gctx.writef("g[%q]", scv.nam)
 	} else {
 		gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString())
 	}
 }
 
-func (scv otherGlobalVariable) emitDefined(gctx *generationContext) {
-	gctx.writef("g.get(%q) != None", scv.name())
-}
-
 type localVariable struct {
 	baseVariable
 }
 
-func (lv localVariable) emitDefined(gctx *generationContext) {
-	gctx.writef(lv.String())
-}
-
 func (lv localVariable) String() string {
 	return "_" + lv.nam
 }
@@ -216,8 +203,7 @@
 		gctx.writef("%s = ", lv)
 		asgn.value.emitListVarCopy(gctx)
 	case asgnAppend:
-		lv.emitGet(gctx, false)
-		gctx.write(" += ")
+		gctx.writef("%s += ", lv)
 		value := asgn.value
 		if lv.valueType() == starlarkTypeString {
 			gctx.writef(`" " + `)
@@ -227,7 +213,7 @@
 	}
 }
 
-func (lv localVariable) emitGet(gctx *generationContext, _ bool) {
+func (lv localVariable) emitGet(gctx *generationContext) {
 	gctx.writef("%s", lv)
 }
 
@@ -236,7 +222,7 @@
 	value starlarkExpr
 }
 
-func (pv predefinedVariable) emitGet(gctx *generationContext, _ bool) {
+func (pv predefinedVariable) emitGet(gctx *generationContext) {
 	pv.value.emit(gctx)
 }
 
@@ -257,10 +243,6 @@
 	panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump()))
 }
 
-func (pv predefinedVariable) emitDefined(gctx *generationContext) {
-	gctx.write("True")
-}
-
 var localProductConfigVariables = map[string]string{
 	"LOCAL_AUDIO_PRODUCT_PACKAGE":         "PRODUCT_PACKAGES",
 	"LOCAL_AUDIO_PRODUCT_COPY_FILES":      "PRODUCT_COPY_FILES",
diff --git a/rust/builder.go b/rust/builder.go
index 00035b9..20ca5db 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -274,7 +274,7 @@
 		implicits = append(implicits, outputs.Paths()...)
 	}
 
-	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
+	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx))
 
 	if ctx.RustModule().compiler.CargoEnvCompat() {
 		if _, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
diff --git a/rust/config/global.go b/rust/config/global.go
index 1cf773e..2d5fa99 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -86,12 +86,7 @@
 		return "${RustDefaultBase}"
 	})
 
-	pctx.VariableFunc("RustVersion", func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv("RUST_PREBUILTS_VERSION"); override != "" {
-			return override
-		}
-		return RustDefaultVersion
-	})
+	pctx.VariableFunc("RustVersion", getRustVersionPctx)
 
 	pctx.StaticVariable("RustPath", "${RustBase}/${HostPrebuiltTag}/${RustVersion}")
 	pctx.StaticVariable("RustBin", "${RustPath}/bin")
@@ -103,3 +98,14 @@
 	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
 
 }
+
+func getRustVersionPctx(ctx android.PackageVarContext) string {
+	return GetRustVersion(ctx)
+}
+
+func GetRustVersion(ctx android.PathContext) string {
+	if override := ctx.Config().Getenv("RUST_PREBUILTS_VERSION"); override != "" {
+		return override
+	}
+	return RustDefaultVersion
+}
diff --git a/rust/library_test.go b/rust/library_test.go
index d78dcdd..4633cc7 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -200,23 +200,34 @@
 func TestAutoDeps(t *testing.T) {
 
 	ctx := testRust(t, `
-                rust_library_host {
-                        name: "libbar",
-                        srcs: ["bar.rs"],
-                        crate_name: "bar",
-                }
+		rust_library_host {
+			name: "libbar",
+			srcs: ["bar.rs"],
+			crate_name: "bar",
+		}
+		rust_library_host_rlib {
+			name: "librlib_only",
+			srcs: ["bar.rs"],
+			crate_name: "rlib_only",
+		}
 		rust_library_host {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
-                        rustlibs: ["libbar"],
+			rustlibs: [
+				"libbar",
+				"librlib_only",
+			],
 		}
-                rust_ffi_host {
-                        name: "libfoo.ffi",
-                        srcs: ["foo.rs"],
-                        crate_name: "foo",
-                        rustlibs: ["libbar"],
-                }`)
+		rust_ffi_host {
+			name: "libfoo.ffi",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			rustlibs: [
+				"libbar",
+				"librlib_only",
+			],
+		}`)
 
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
@@ -239,7 +250,9 @@
 		if android.InList("libbar.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
 			t.Errorf("libbar present as rlib dependency in dynamic lib")
 		}
-
+		if !android.InList("librlib_only.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
+			t.Errorf("librlib_only should be selected by rustlibs as an rlib.")
+		}
 	}
 }
 
diff --git a/rust/rust.go b/rust/rust.go
index d627261..c4fd148 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -332,6 +332,20 @@
 	return false
 }
 
+func (mod *Module) IsVndkPrebuiltLibrary() bool {
+	// Rust modules do not provide VNDK prebuilts
+	return false
+}
+
+func (mod *Module) IsVendorPublicLibrary() bool {
+	return mod.VendorProperties.IsVendorPublicLibrary
+}
+
+func (mod *Module) SdkAndPlatformVariantVisibleToMake() bool {
+	// Rust modules to not provide Sdk variants
+	return false
+}
+
 func (c *Module) IsVndkPrivate() bool {
 	return false
 }
@@ -841,24 +855,7 @@
 	toolchain := mod.toolchain(ctx)
 	mod.makeLinkType = cc.GetMakeLinkType(actx, mod)
 
-	// Differentiate static libraries that are vendor available
-	if mod.UseVndk() {
-		if mod.InProduct() && !mod.OnlyInProduct() {
-			mod.Properties.SubName += cc.ProductSuffix
-		} else {
-			mod.Properties.SubName += cc.VendorSuffix
-		}
-	} else if mod.InRamdisk() && !mod.OnlyInRamdisk() {
-		mod.Properties.SubName += cc.RamdiskSuffix
-	} else if mod.InVendorRamdisk() && !mod.OnlyInVendorRamdisk() {
-		mod.Properties.SubName += cc.VendorRamdiskSuffix
-	} else if mod.InRecovery() && !mod.OnlyInRecovery() {
-		mod.Properties.SubName += cc.RecoverySuffix
-	}
-
-	if mod.Target().NativeBridge == android.NativeBridgeEnabled {
-		mod.Properties.SubName += cc.NativeBridgeSuffix
-	}
+	mod.Properties.SubName = cc.GetSubnameProperty(actx, mod)
 
 	if !toolchain.Supported() {
 		// This toolchain's unsupported, there's nothing to do for this mod.
@@ -1371,13 +1368,12 @@
 	}
 
 	// rlibs
+	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: rlibVariation})
 	for _, lib := range deps.Rlibs {
 		depTag := rlibDepTag
 		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
 
-		actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: rlibVariation},
-		}...), depTag, lib)
+		actx.AddVariationDependencies(rlibDepVariations, depTag, lib)
 	}
 
 	// dylibs
@@ -1389,21 +1385,25 @@
 	// rustlibs
 	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
 		autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
-		if autoDep.depTag == rlibDepTag {
-			for _, lib := range deps.Rustlibs {
-				depTag := autoDep.depTag
-				lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
-				actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
-					{Mutator: "rust_libraries", Variation: autoDep.variation},
-				}...), depTag, lib)
+		for _, lib := range deps.Rustlibs {
+			if autoDep.depTag == rlibDepTag {
+				// Handle the rlib deptag case
+				addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations)
+			} else {
+				// autoDep.depTag is a dylib depTag. Not all rustlibs may be available as a dylib however.
+				// Check for the existence of the dylib deptag variant. Select it if available,
+				// otherwise select the rlib variant.
+				autoDepVariations := append(commonDepVariations,
+					blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation})
+				if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) {
+					actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib)
+				} else {
+					// If there's no dylib dependency available, try to add the rlib dependency instead.
+					addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations)
+				}
 			}
-		} else {
-			actx.AddVariationDependencies(
-				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
-				autoDep.depTag, deps.Rustlibs...)
 		}
 	}
-
 	// stdlibs
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
@@ -1479,6 +1479,12 @@
 	actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
 }
 
+// addRlibDependency will add an rlib dependency, rewriting to the snapshot library if available.
+func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo *cc.SnapshotInfo, variations []blueprint.Variation) {
+	lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+	actx.AddVariationDependencies(variations, rlibDepTag, lib)
+}
+
 func BeginMutator(ctx android.BottomUpMutatorContext) {
 	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
 		mod.beginMutator(ctx)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index e5214df..f985a49 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -23,13 +23,13 @@
 from signature_trie import signature_trie
 
 
-def dict_reader(csvfile):
+def dict_reader(csv_file):
     return csv.DictReader(
-        csvfile, delimiter=",", quotechar="|", fieldnames=["signature"])
+        csv_file, delimiter=",", quotechar="|", fieldnames=["signature"])
 
 
 def read_flag_trie_from_file(file):
-    with open(file, "r") as stream:
+    with open(file, "r", encoding="utf8") as stream:
         return read_flag_trie_from_stream(stream)
 
 
@@ -43,24 +43,24 @@
 
 
 def extract_subset_from_monolithic_flags_as_dict_from_file(
-        monolithicTrie, patternsFile):
+        monolithic_trie, patterns_file):
     """Extract a subset of flags from the dict of monolithic flags.
 
-    :param monolithicFlagsDict: the dict containing all the monolithic flags.
-    :param patternsFile: a file containing a list of signature patterns that
+    :param monolithic_trie: the trie containing all the monolithic flags.
+    :param patterns_file: a file containing a list of signature patterns that
     define the subset.
     :return: the dict from signature to row.
     """
-    with open(patternsFile, "r") as stream:
+    with open(patterns_file, "r", encoding="utf8") as stream:
         return extract_subset_from_monolithic_flags_as_dict_from_stream(
-            monolithicTrie, stream)
+            monolithic_trie, stream)
 
 
 def extract_subset_from_monolithic_flags_as_dict_from_stream(
-        monolithicTrie, stream):
+        monolithic_trie, stream):
     """Extract a subset of flags from the trie of monolithic flags.
 
-    :param monolithicTrie: the trie containing all the monolithic flags.
+    :param monolithic_trie: the trie containing all the monolithic flags.
     :param stream: a stream containing a list of signature patterns that define
     the subset.
     :return: the dict from signature to row.
@@ -68,7 +68,7 @@
     dict_signature_to_row = {}
     for pattern in stream:
         pattern = pattern.rstrip()
-        rows = monolithicTrie.get_matching_rows(pattern)
+        rows = monolithic_trie.get_matching_rows(pattern)
         for row in rows:
             signature = row["signature"]
             dict_signature_to_row[signature] = row
@@ -93,86 +93,110 @@
     return dict_signature_to_row
 
 
-def read_signature_csv_from_file_as_dict(csvFile):
+def read_signature_csv_from_file_as_dict(csv_file):
     """Read the csvFile into a dict.
 
     The first column is assumed to be the signature and used as the
     key.
 
     The whole row is stored as the value.
-    :param csvFile: the csv file to read
+    :param csv_file: the csv file to read
     :return: the dict from signature to row.
     """
-    with open(csvFile, "r") as f:
+    with open(csv_file, "r", encoding="utf8") as f:
         return read_signature_csv_from_stream_as_dict(f)
 
 
-def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
+def compare_signature_flags(monolithic_flags_dict, modular_flags_dict,
+                            implementation_flags):
     """Compare the signature flags between the two dicts.
 
-    :param monolithicFlagsDict: the dict containing the subset of the monolithic
-    flags that should be equal to the modular flags.
-    :param modularFlagsDict:the dict containing the flags produced by a single
+    :param monolithic_flags_dict: the dict containing the subset of the
+    monolithic flags that should be equal to the modular flags.
+    :param modular_flags_dict:the dict containing the flags produced by a single
     bootclasspath_fragment module.
     :return: list of mismatches., each mismatch is a tuple where the first item
     is the signature, and the second and third items are lists of the flags from
     modular dict, and monolithic dict respectively.
     """
-    mismatchingSignatures = []
+    mismatching_signatures = []
     # Create a sorted set of all the signatures from both the monolithic and
     # modular dicts.
-    allSignatures = sorted(
-        set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
-    for signature in allSignatures:
-        monolithicRow = monolithicFlagsDict.get(signature, {})
-        monolithicFlags = monolithicRow.get(None, [])
-        if signature in modularFlagsDict:
-            modularRow = modularFlagsDict.get(signature, {})
-            modularFlags = modularRow.get(None, [])
+    all_signatures = sorted(
+        set(chain(monolithic_flags_dict.keys(), modular_flags_dict.keys())))
+    for signature in all_signatures:
+        monolithic_row = monolithic_flags_dict.get(signature, {})
+        monolithic_flags = monolithic_row.get(None, [])
+        if signature in modular_flags_dict:
+            modular_row = modular_flags_dict.get(signature, {})
+            modular_flags = modular_row.get(None, [])
         else:
-            modularFlags = ["blocked"]
-        if monolithicFlags != modularFlags:
-            mismatchingSignatures.append(
-                (signature, modularFlags, monolithicFlags))
-    return mismatchingSignatures
+            modular_flags = implementation_flags
+        if monolithic_flags != modular_flags:
+            mismatching_signatures.append(
+                (signature, modular_flags, monolithic_flags))
+    return mismatching_signatures
 
 
 def main(argv):
     args_parser = argparse.ArgumentParser(
         description="Verify that sets of hidden API flags are each a subset of "
-        "the monolithic flag file.")
-    args_parser.add_argument("monolithicFlags", help="The monolithic flag file")
+        "the monolithic flag file. For each module this uses the provided "
+        "signature patterns to select a subset of the monolithic flags and "
+        "then it compares that subset against the filtered flags provided by "
+        "the module. If the module's filtered flags does not contain flags for "
+        "a signature then it is assumed to have been filtered out because it "
+        "was not part of an API and so is assumed to have the implementation "
+        "flags.")
     args_parser.add_argument(
-        "modularFlags",
-        nargs=argparse.REMAINDER,
-        help="Flags produced by individual bootclasspath_fragment modules")
+        "--monolithic-flags", help="The monolithic flag file")
+    args_parser.add_argument(
+        "--module-flags",
+        action="append",
+        help="A colon separated pair of paths. The first is a path to a "
+        "filtered set of flags, and the second is a path to a set of "
+        "signature patterns that identify the set of classes belonging to "
+        "a single bootclasspath_fragment module. Specify once for each module "
+        "that needs to be checked.")
+    args_parser.add_argument(
+        "--implementation-flag",
+        action="append",
+        help="A flag in the set of flags that identifies a signature which is "
+        "not part of an API, i.e. is the signature of a private implementation "
+        "member. Specify as many times as necessary to define the "
+        "implementation flag set. If this is not specified then the "
+        "implementation flag set is empty.")
     args = args_parser.parse_args(argv[1:])
 
     # Read in all the flags into the trie
-    monolithicFlagsPath = args.monolithicFlags
-    monolithicTrie = read_flag_trie_from_file(monolithicFlagsPath)
+    monolithic_flags_path = args.monolithic_flags
+    monolithic_trie = read_flag_trie_from_file(monolithic_flags_path)
+
+    implementation_flags = args.implementation_flag or []
 
     # For each subset specified on the command line, create dicts for the flags
     # provided by the subset and the corresponding flags from the complete set
     # of flags and compare them.
     failed = False
-    for modularPair in args.modularFlags:
-        parts = modularPair.split(":")
-        modularFlagsPath = parts[0]
-        modularPatternsPath = parts[1]
-        modularFlagsDict = read_signature_csv_from_file_as_dict(
-            modularFlagsPath)
-        monolithicFlagsSubsetDict = \
+    module_pairs = args.module_flags or []
+    for modular_pair in module_pairs:
+        parts = modular_pair.split(":")
+        modular_flags_path = parts[0]
+        modular_patterns_path = parts[1]
+        modular_flags_dict = read_signature_csv_from_file_as_dict(
+            modular_flags_path)
+        monolithic_flags_subset_dict = \
             extract_subset_from_monolithic_flags_as_dict_from_file(
-            monolithicTrie, modularPatternsPath)
-        mismatchingSignatures = compare_signature_flags(
-            monolithicFlagsSubsetDict, modularFlagsDict)
-        if mismatchingSignatures:
+                monolithic_trie, modular_patterns_path)
+        mismatching_signatures = compare_signature_flags(
+            monolithic_flags_subset_dict, modular_flags_dict,
+            implementation_flags)
+        if mismatching_signatures:
             failed = True
             print("ERROR: Hidden API flags are inconsistent:")
-            print("< " + modularFlagsPath)
-            print("> " + monolithicFlagsPath)
-            for mismatch in mismatchingSignatures:
+            print("< " + modular_flags_path)
+            print("> " + monolithic_flags_path)
+            for mismatch in mismatching_signatures:
                 signature = mismatch[0]
                 print()
                 print("< " + ",".join([signature] + mismatch[1]))
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index 8cf2959..0a489ee 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -17,24 +17,26 @@
 import io
 import unittest
 
-from verify_overlaps import *  #pylint: disable=unused-wildcard-import,wildcard-import
+import verify_overlaps as vo
 
 
-#pylint: disable=line-too-long
 class TestDetectOverlaps(unittest.TestCase):
 
-    def read_flag_trie_from_string(self, csvdata):
+    @staticmethod
+    def read_flag_trie_from_string(csvdata):
         with io.StringIO(csvdata) as f:
-            return read_flag_trie_from_stream(f)
+            return vo.read_flag_trie_from_stream(f)
 
-    def read_signature_csv_from_string_as_dict(self, csvdata):
+    @staticmethod
+    def read_signature_csv_from_string_as_dict(csvdata):
         with io.StringIO(csvdata) as f:
-            return read_signature_csv_from_stream_as_dict(f)
+            return vo.read_signature_csv_from_stream_as_dict(f)
 
+    @staticmethod
     def extract_subset_from_monolithic_flags_as_dict_from_string(
-            self, monolithic, patterns):
+            monolithic, patterns):
         with io.StringIO(patterns) as f:
-            return extract_subset_from_monolithic_flags_as_dict_from_stream(
+            return vo.extract_subset_from_monolithic_flags_as_dict_from_stream(
                 monolithic, f)
 
     extractInput = """
@@ -50,14 +52,14 @@
         monolithic = self.read_flag_trie_from_string(
             TestDetectOverlaps.extractInput)
 
-        patterns = 'Ljava/lang/Object;->hashCode()I'
+        patterns = "Ljava/lang/Object;->hashCode()I"
 
         subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
             monolithic, patterns)
         expected = {
-            'Ljava/lang/Object;->hashCode()I': {
-                None: ['public-api', 'system-api', 'test-api'],
-                'signature': 'Ljava/lang/Object;->hashCode()I',
+            "Ljava/lang/Object;->hashCode()I": {
+                None: ["public-api", "system-api", "test-api"],
+                "signature": "Ljava/lang/Object;->hashCode()I",
             },
         }
         self.assertEqual(expected, subset)
@@ -66,18 +68,18 @@
         monolithic = self.read_flag_trie_from_string(
             TestDetectOverlaps.extractInput)
 
-        patterns = 'java/lang/Object'
+        patterns = "java/lang/Object"
 
         subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
             monolithic, patterns)
         expected = {
-            'Ljava/lang/Object;->hashCode()I': {
-                None: ['public-api', 'system-api', 'test-api'],
-                'signature': 'Ljava/lang/Object;->hashCode()I',
+            "Ljava/lang/Object;->hashCode()I": {
+                None: ["public-api", "system-api", "test-api"],
+                "signature": "Ljava/lang/Object;->hashCode()I",
             },
-            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            "Ljava/lang/Object;->toString()Ljava/lang/String;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Object;->toString()Ljava/lang/String;",
             },
         }
         self.assertEqual(expected, subset)
@@ -86,20 +88,20 @@
         monolithic = self.read_flag_trie_from_string(
             TestDetectOverlaps.extractInput)
 
-        patterns = 'java/lang/Character'
+        patterns = "java/lang/Character"
 
         subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
             monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
-                {
-                    None: ['blocked'],
-                    'signature':
-                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-                },
-            'Ljava/lang/Character;->serialVersionUID:J': {
-                None: ['sdk'],
-                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            "Ljava/lang/Character$UnicodeScript;"
+            "->of(I)Ljava/lang/Character$UnicodeScript;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Character$UnicodeScript;"
+                             "->of(I)Ljava/lang/Character$UnicodeScript;",
+            },
+            "Ljava/lang/Character;->serialVersionUID:J": {
+                None: ["sdk"],
+                "signature": "Ljava/lang/Character;->serialVersionUID:J",
             },
         }
         self.assertEqual(expected, subset)
@@ -108,17 +110,17 @@
         monolithic = self.read_flag_trie_from_string(
             TestDetectOverlaps.extractInput)
 
-        patterns = 'java/lang/Character$UnicodeScript'
+        patterns = "java/lang/Character$UnicodeScript"
 
         subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
             monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
-                {
-                    None: ['blocked'],
-                    'signature':
-                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-                },
+            "Ljava/lang/Character$UnicodeScript;"
+            "->of(I)Ljava/lang/Character$UnicodeScript;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Character$UnicodeScript;"
+                             "->of(I)Ljava/lang/Character$UnicodeScript;",
+            },
         }
         self.assertEqual(expected, subset)
 
@@ -126,32 +128,32 @@
         monolithic = self.read_flag_trie_from_string(
             TestDetectOverlaps.extractInput)
 
-        patterns = 'java/lang/*'
+        patterns = "java/lang/*"
 
         subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
             monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
-                {
-                    None: ['blocked'],
-                    'signature':
-                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-                },
-            'Ljava/lang/Character;->serialVersionUID:J': {
-                None: ['sdk'],
-                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            "Ljava/lang/Character$UnicodeScript;"
+            "->of(I)Ljava/lang/Character$UnicodeScript;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Character$UnicodeScript;"
+                             "->of(I)Ljava/lang/Character$UnicodeScript;",
             },
-            'Ljava/lang/Object;->hashCode()I': {
-                None: ['public-api', 'system-api', 'test-api'],
-                'signature': 'Ljava/lang/Object;->hashCode()I',
+            "Ljava/lang/Character;->serialVersionUID:J": {
+                None: ["sdk"],
+                "signature": "Ljava/lang/Character;->serialVersionUID:J",
             },
-            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            "Ljava/lang/Object;->hashCode()I": {
+                None: ["public-api", "system-api", "test-api"],
+                "signature": "Ljava/lang/Object;->hashCode()I",
             },
-            'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+            "Ljava/lang/Object;->toString()Ljava/lang/String;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Object;->toString()Ljava/lang/String;",
+            },
+            "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
             },
         }
         self.assertEqual(expected, subset)
@@ -160,36 +162,36 @@
         monolithic = self.read_flag_trie_from_string(
             TestDetectOverlaps.extractInput)
 
-        patterns = 'java/**'
+        patterns = "java/**"
 
         subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
             monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
-                {
-                    None: ['blocked'],
-                    'signature':
-                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-                },
-            'Ljava/lang/Character;->serialVersionUID:J': {
-                None: ['sdk'],
-                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            "Ljava/lang/Character$UnicodeScript;"
+            "->of(I)Ljava/lang/Character$UnicodeScript;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Character$UnicodeScript;"
+                             "->of(I)Ljava/lang/Character$UnicodeScript;",
             },
-            'Ljava/lang/Object;->hashCode()I': {
-                None: ['public-api', 'system-api', 'test-api'],
-                'signature': 'Ljava/lang/Object;->hashCode()I',
+            "Ljava/lang/Character;->serialVersionUID:J": {
+                None: ["sdk"],
+                "signature": "Ljava/lang/Character;->serialVersionUID:J",
             },
-            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            "Ljava/lang/Object;->hashCode()I": {
+                None: ["public-api", "system-api", "test-api"],
+                "signature": "Ljava/lang/Object;->hashCode()I",
             },
-            'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+            "Ljava/lang/Object;->toString()Ljava/lang/String;": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/Object;->toString()Ljava/lang/String;",
             },
-            'Ljava/util/zip/ZipFile;-><clinit>()V': {
-                None: ['blocked'],
-                'signature': 'Ljava/util/zip/ZipFile;-><clinit>()V',
+            "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V": {
+                None: ["blocked"],
+                "signature": "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+            },
+            "Ljava/util/zip/ZipFile;-><clinit>()V": {
+                None: ["blocked"],
+                "signature": "Ljava/util/zip/ZipFile;-><clinit>()V",
             },
         }
         self.assertEqual(expected, subset)
@@ -200,7 +202,7 @@
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->hashCode()I,blocked
 """)
-        self.assertTrue('Duplicate signature: Ljava/lang/Object;->hashCode()I'
+        self.assertTrue("Duplicate signature: Ljava/lang/Object;->hashCode()I"
                         in str(context.exception))
 
     def test_read_trie_missing_member(self):
@@ -209,8 +211,8 @@
 Ljava/lang/Object,public-api,system-api,test-api
 """)
         self.assertTrue(
-            'Invalid signature: Ljava/lang/Object, does not identify a specific member'
-            in str(context.exception))
+            "Invalid signature: Ljava/lang/Object, "
+            "does not identify a specific member" in str(context.exception))
 
     def test_match(self):
         monolithic = self.read_signature_csv_from_string_as_dict("""
@@ -219,7 +221,8 @@
         modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 """)
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
         expected = []
         self.assertEqual(expected, mismatches)
 
@@ -230,12 +233,13 @@
         modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
 """)
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
         expected = [
             (
-                'Ljava/lang/Object;->toString()Ljava/lang/String;',
-                ['public-api', 'system-api', 'test-api'],
-                ['public-api'],
+                "Ljava/lang/Object;->toString()Ljava/lang/String;",
+                ["public-api", "system-api", "test-api"],
+                ["public-api"],
             ),
         ]
         self.assertEqual(expected, mismatches)
@@ -247,12 +251,13 @@
         modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
 """)
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
         expected = [
             (
-                'Ljava/lang/Object;->toString()Ljava/lang/String;',
-                ['public-api', 'system-api', 'test-api'],
-                ['blocked'],
+                "Ljava/lang/Object;->toString()Ljava/lang/String;",
+                ["public-api", "system-api", "test-api"],
+                ["blocked"],
             ),
         ]
         self.assertEqual(expected, mismatches)
@@ -264,26 +269,28 @@
         modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
 """)
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
         expected = [
             (
-                'Ljava/lang/Object;->toString()Ljava/lang/String;',
-                ['blocked'],
-                ['public-api', 'system-api', 'test-api'],
+                "Ljava/lang/Object;->toString()Ljava/lang/String;",
+                ["blocked"],
+                ["public-api", "system-api", "test-api"],
             ),
         ]
         self.assertEqual(expected, mismatches)
 
     def test_match_treat_missing_from_modular_as_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('')
+        monolithic = self.read_signature_csv_from_string_as_dict("")
         modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
 """)
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
         expected = [
             (
-                'Ljava/lang/Object;->toString()Ljava/lang/String;',
-                ['public-api', 'system-api', 'test-api'],
+                "Ljava/lang/Object;->toString()Ljava/lang/String;",
+                ["public-api", "system-api", "test-api"],
                 [],
             ),
         ]
@@ -294,12 +301,13 @@
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 """)
         modular = {}
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
         expected = [
             (
-                'Ljava/lang/Object;->hashCode()I',
-                ['blocked'],
-                ['public-api', 'system-api', 'test-api'],
+                "Ljava/lang/Object;->hashCode()I",
+                ["blocked"],
+                ["public-api", "system-api", "test-api"],
             ),
         ]
         self.assertEqual(expected, mismatches)
@@ -309,12 +317,50 @@
 Ljava/lang/Object;->hashCode()I,blocked
 """)
         modular = {}
-        mismatches = compare_signature_flags(monolithic, modular)
+        mismatches = vo.compare_signature_flags(monolithic, modular,
+                                                ["blocked"])
+        expected = []
+        self.assertEqual(expected, mismatches)
+
+    def test_match_treat_missing_from_modular_as_empty(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("")
+        modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+""")
+        mismatches = vo.compare_signature_flags(monolithic, modular, [])
+        expected = [
+            (
+                "Ljava/lang/Object;->toString()Ljava/lang/String;",
+                ["public-api", "system-api", "test-api"],
+                [],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_treat_missing_from_modular_as_empty(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+""")
+        modular = {}
+        mismatches = vo.compare_signature_flags(monolithic, modular, [])
+        expected = [
+            (
+                "Ljava/lang/Object;->hashCode()I",
+                [],
+                ["public-api", "system-api", "test-api"],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_empty_missing_from_modular(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I
+""")
+        modular = {}
+        mismatches = vo.compare_signature_flags(monolithic, modular, [])
         expected = []
         self.assertEqual(expected, mismatches)
 
 
-#pylint: enable=line-too-long
-
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main(verbosity=2)
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
index 23eee59..3e51fa1 100644
--- a/starlark_fmt/format.go
+++ b/starlark_fmt/format.go
@@ -39,21 +39,26 @@
 
 // PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
 func PrintStringList(items []string, indentLevel int) string {
-	return PrintList(items, indentLevel, `"%s"`)
+	return PrintList(items, indentLevel, func(s string) string {
+		if strings.Contains(s, "\"") {
+			return `'''%s'''`
+		}
+		return `"%s"`
+	})
 }
 
 // PrintList returns a Starlark-compatible string of list formmated as requested.
-func PrintList(items []string, indentLevel int, formatString string) string {
+func PrintList(items []string, indentLevel int, formatString func(string) string) string {
 	if len(items) == 0 {
 		return "[]"
 	} else if len(items) == 1 {
-		return fmt.Sprintf("["+formatString+"]", items[0])
+		return fmt.Sprintf("["+formatString(items[0])+"]", items[0])
 	}
 	list := make([]string, 0, len(items)+2)
 	list = append(list, "[")
 	innerIndent := Indention(indentLevel + 1)
 	for _, item := range items {
-		list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
+		list = append(list, fmt.Sprintf(`%s`+formatString(item)+`,`, innerIndent, item))
 	}
 	list = append(list, Indention(indentLevel)+"]")
 	return strings.Join(list, "\n")
diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go
index 90f78ef..9450a31 100644
--- a/starlark_fmt/format_test.go
+++ b/starlark_fmt/format_test.go
@@ -18,6 +18,10 @@
 	"testing"
 )
 
+func simpleFormat(s string) string {
+	return "%s"
+}
+
 func TestPrintEmptyStringList(t *testing.T) {
 	in := []string{}
 	indentLevel := 0
@@ -54,7 +58,7 @@
 func TestPrintEmptyList(t *testing.T) {
 	in := []string{}
 	indentLevel := 0
-	out := PrintList(in, indentLevel, "%s")
+	out := PrintList(in, indentLevel, simpleFormat)
 	expectedOut := "[]"
 	if out != expectedOut {
 		t.Errorf("Expected %q, got %q", expectedOut, out)
@@ -64,7 +68,7 @@
 func TestPrintSingleElementList(t *testing.T) {
 	in := []string{"1"}
 	indentLevel := 0
-	out := PrintList(in, indentLevel, "%s")
+	out := PrintList(in, indentLevel, simpleFormat)
 	expectedOut := `[1]`
 	if out != expectedOut {
 		t.Errorf("Expected %q, got %q", expectedOut, out)
@@ -74,7 +78,7 @@
 func TestPrintMultiElementList(t *testing.T) {
 	in := []string{"1", "2"}
 	indentLevel := 0
-	out := PrintList(in, indentLevel, "%s")
+	out := PrintList(in, indentLevel, simpleFormat)
 	expectedOut := `[
     1,
     2,
@@ -87,7 +91,7 @@
 func TestListWithNonZeroIndent(t *testing.T) {
 	in := []string{"1", "2"}
 	indentLevel := 1
-	out := PrintList(in, indentLevel, "%s")
+	out := PrintList(in, indentLevel, simpleFormat)
 	expectedOut := `[
         1,
         2,
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index f935f06..88ef615 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -98,9 +98,6 @@
 
 		"build/soong/scripts/jar-wrapper.sh": nil,
 
-		"build/make/core/proguard.flags":             nil,
-		"build/make/core/proguard_basic_keeps.flags": nil,
-
 		"jdk8/jre/lib/jce.jar": nil,
 		"jdk8/jre/lib/rt.jar":  nil,
 		"jdk8/lib/tools.jar":   nil,
diff --git a/ui/build/config.go b/ui/build/config.go
index dd5bd0c..e271bfc 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -144,6 +144,12 @@
 // fetchEnvConfig optionally fetches environment config from an
 // experiments system to control Soong features dynamically.
 func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
+	configName := envConfigName + "." + jsonSuffix
+	expConfigFetcher := &smpb.ExpConfigFetcher{}
+	defer func() {
+		ctx.Metrics.ExpConfigFetcher(expConfigFetcher)
+	}()
+
 	s, err := os.Stat(configFetcher)
 	if err != nil {
 		if os.IsNotExist(err) {
@@ -152,31 +158,38 @@
 		return err
 	}
 	if s.Mode()&0111 == 0 {
+		status := smpb.ExpConfigFetcher_ERROR
+		expConfigFetcher.Status = &status
 		return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
 	}
 
-	configExists := false
-	outConfigFilePath := filepath.Join(config.OutDir(), envConfigName+jsonSuffix)
-	if _, err := os.Stat(outConfigFilePath); err == nil {
-		configExists = true
-	}
-
 	tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
 	defer cancel()
-	cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir())
+	fetchStart := time.Now()
+	cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
+		"-output_config_name", configName)
 	if err := cmd.Start(); err != nil {
+		status := smpb.ExpConfigFetcher_ERROR
+		expConfigFetcher.Status = &status
 		return err
 	}
 
-	// If a config file already exists, return immediately and run the config file
-	// fetch in the background. Otherwise, wait for the config file to be fetched.
-	if configExists {
-		go cmd.Wait()
-		return nil
-	}
 	if err := cmd.Wait(); err != nil {
+		status := smpb.ExpConfigFetcher_ERROR
+		expConfigFetcher.Status = &status
 		return err
 	}
+	fetchEnd := time.Now()
+	expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
+	outConfigFilePath := filepath.Join(config.OutDir(), configName)
+	expConfigFetcher.Filename = proto.String(outConfigFilePath)
+	if _, err := os.Stat(outConfigFilePath); err == nil {
+		status := smpb.ExpConfigFetcher_CONFIG
+		expConfigFetcher.Status = &status
+	} else {
+		status := smpb.ExpConfigFetcher_NO_CONFIG
+		expConfigFetcher.Status = &status
+	}
 	return nil
 }
 
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 6f1ed60..0c62865 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -38,6 +38,7 @@
 	"time"
 
 	"android/soong/shared"
+
 	"google.golang.org/protobuf/proto"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -135,6 +136,11 @@
 	m.metrics.SystemResourceInfo = b
 }
 
+// ExpConfigFetcher stores information about the expconfigfetcher.
+func (m *Metrics) ExpConfigFetcher(b *soong_metrics_proto.ExpConfigFetcher) {
+	m.metrics.ExpConfigFetcher = b
+}
+
 // SetMetadataMetrics sets information about the build such as the target
 // product, host architecture and out directory.
 func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 26229c6..69f5689 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -217,6 +217,65 @@
 	return file_metrics_proto_rawDescGZIP(), []int{5, 0}
 }
 
+type ExpConfigFetcher_ConfigStatus int32
+
+const (
+	ExpConfigFetcher_NO_CONFIG ExpConfigFetcher_ConfigStatus = 0
+	ExpConfigFetcher_CONFIG    ExpConfigFetcher_ConfigStatus = 1
+	ExpConfigFetcher_ERROR     ExpConfigFetcher_ConfigStatus = 2
+)
+
+// Enum value maps for ExpConfigFetcher_ConfigStatus.
+var (
+	ExpConfigFetcher_ConfigStatus_name = map[int32]string{
+		0: "NO_CONFIG",
+		1: "CONFIG",
+		2: "ERROR",
+	}
+	ExpConfigFetcher_ConfigStatus_value = map[string]int32{
+		"NO_CONFIG": 0,
+		"CONFIG":    1,
+		"ERROR":     2,
+	}
+)
+
+func (x ExpConfigFetcher_ConfigStatus) Enum() *ExpConfigFetcher_ConfigStatus {
+	p := new(ExpConfigFetcher_ConfigStatus)
+	*p = x
+	return p
+}
+
+func (x ExpConfigFetcher_ConfigStatus) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ExpConfigFetcher_ConfigStatus) Descriptor() protoreflect.EnumDescriptor {
+	return file_metrics_proto_enumTypes[3].Descriptor()
+}
+
+func (ExpConfigFetcher_ConfigStatus) Type() protoreflect.EnumType {
+	return &file_metrics_proto_enumTypes[3]
+}
+
+func (x ExpConfigFetcher_ConfigStatus) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *ExpConfigFetcher_ConfigStatus) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = ExpConfigFetcher_ConfigStatus(num)
+	return nil
+}
+
+// Deprecated: Use ExpConfigFetcher_ConfigStatus.Descriptor instead.
+func (ExpConfigFetcher_ConfigStatus) EnumDescriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{9, 0}
+}
+
 type MetricsBase struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -274,6 +333,8 @@
 	BuildCommand *string `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
 	// The metrics for calling Bazel.
 	BazelRuns []*PerfInfo `protobuf:"bytes,27,rep,name=bazel_runs,json=bazelRuns" json:"bazel_runs,omitempty"`
+	// The metrics of the experiment config fetcher
+	ExpConfigFetcher *ExpConfigFetcher `protobuf:"bytes,28,opt,name=exp_config_fetcher,json=expConfigFetcher" json:"exp_config_fetcher,omitempty"`
 }
 
 // Default values for MetricsBase fields.
@@ -505,6 +566,13 @@
 	return nil
 }
 
+func (x *MetricsBase) GetExpConfigFetcher() *ExpConfigFetcher {
+	if x != nil {
+		return x.ExpConfigFetcher
+	}
+	return nil
+}
+
 type BuildConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1150,12 +1218,81 @@
 	return nil
 }
 
+type ExpConfigFetcher struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The result of the call to expconfigfetcher
+	// NO_CONFIG - Not part of experiment
+	// CONFIG - Part of experiment, config copied successfully
+	// ERROR - expconfigfetcher failed
+	Status *ExpConfigFetcher_ConfigStatus `protobuf:"varint,1,opt,name=status,enum=soong_build_metrics.ExpConfigFetcher_ConfigStatus" json:"status,omitempty"`
+	// The output config filename
+	Filename *string `protobuf:"bytes,2,opt,name=filename" json:"filename,omitempty"`
+	// Time, in microseconds, taken by the expconfigfetcher
+	Micros *uint64 `protobuf:"varint,3,opt,name=micros" json:"micros,omitempty"`
+}
+
+func (x *ExpConfigFetcher) Reset() {
+	*x = ExpConfigFetcher{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ExpConfigFetcher) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExpConfigFetcher) ProtoMessage() {}
+
+func (x *ExpConfigFetcher) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExpConfigFetcher.ProtoReflect.Descriptor instead.
+func (*ExpConfigFetcher) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *ExpConfigFetcher) GetStatus() ExpConfigFetcher_ConfigStatus {
+	if x != nil && x.Status != nil {
+		return *x.Status
+	}
+	return ExpConfigFetcher_NO_CONFIG
+}
+
+func (x *ExpConfigFetcher) GetFilename() string {
+	if x != nil && x.Filename != nil {
+		return *x.Filename
+	}
+	return ""
+}
+
+func (x *ExpConfigFetcher) GetMicros() uint64 {
+	if x != nil && x.Micros != nil {
+		return *x.Micros
+	}
+	return 0
+}
+
 var File_metrics_proto protoreflect.FileDescriptor
 
 var file_metrics_proto_rawDesc = []byte{
 	0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 	0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x22, 0xd8, 0x0c, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0xad, 0x0d, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
 	0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
 	0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
@@ -1249,122 +1386,140 @@
 	0x64, 0x12, 0x3c, 0x0a, 0x0a, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18,
 	0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
 	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66,
-	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x22,
-	0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12,
-	0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x53, 0x45,
-	0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e, 0x47, 0x10,
-	0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b,
-	0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10, 0x01, 0x12,
-	0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38,
-	0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x04, 0x22,
-	0xd3, 0x01, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
-	0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73,
-	0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x73, 0x65,
-	0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x65,
-	0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72,
-	0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x61, 0x7a,
-	0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x12,
-	0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x61, 0x7a, 0x65,
-	0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74,
-	0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61,
-	0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74,
-	0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65,
-	0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61,
-	0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12,
-	0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75,
-	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
-	0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
-	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61,
-	0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73,
-	0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c,
-	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61,
-	0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f,
-	0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d,
-	0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63,
-	0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
-	0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
-	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
-	0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
-	0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50,
-	0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e,
-	0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74,
-	0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
-	0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f,
-	0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79,
-	0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c,
-	0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11,
-	0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74,
-	0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61,
-	0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f,
-	0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61,
-	0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
-	0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70,
-	0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75,
-	0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75,
-	0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
-	0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74,
-	0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75,
-	0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74,
-	0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
-	0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74,
-	0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f,
-	0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77,
-	0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65,
-	0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f,
-	0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
-	0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a,
-	0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07,
-	0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f,
-	0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c,
-	0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f,
-	0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42,
-	0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b,
-	0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
-	0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63,
-	0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
-	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
-	0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
-	0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73,
-	0x22, 0xfa, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
-	0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11,
-	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
-	0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c,
-	0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61,
-	0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69,
-	0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
-	0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65,
-	0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73,
-	0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72,
-	0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x28, 0x5a,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x12,
+	0x53, 0x0a, 0x12, 0x65, 0x78, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x65,
+	0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68,
+	0x65, 0x72, 0x52, 0x10, 0x65, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74,
+	0x63, 0x68, 0x65, 0x72, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72,
+	0x69, 0x61, 0x6e, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d,
+	0x0a, 0x09, 0x55, 0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a,
+	0x03, 0x45, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b,
+	0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41,
+	0x52, 0x4d, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12,
+	0x07, 0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f,
+	0x36, 0x34, 0x10, 0x04, 0x22, 0xd3, 0x01, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12,
+	0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x06, 0x75, 0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63,
+	0x65, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24,
+	0x0a, 0x0e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e,
+	0x69, 0x6e, 0x6a, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69,
+	0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28,
+	0x09, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f,
+	0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63,
+	0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65,
+	0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c,
+	0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76,
+	0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x08,
+	0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d,
+	0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a,
+	0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65,
+	0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02,
+	0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a,
+	0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+	0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
+	0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22,
+	0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75,
+	0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75,
+	0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d,
+	0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f,
+	0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63,
+	0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b,
+	0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b,
+	0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f,
+	0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69,
+	0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a,
+	0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c,
+	0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50,
+	0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f,
+	0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09,
+	0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f,
+	0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76,
+	0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+	0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+	0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76,
+	0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+	0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74,
+	0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b,
+	0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53,
+	0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e,
+	0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09,
+	0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b,
+	0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55,
+	0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65,
+	0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65,
+	0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52,
+	0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42,
+	0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74,
+	0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63,
+	0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f,
+	0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a,
+	0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a,
+	0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c,
+	0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68,
+	0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b,
+	0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65,
+	0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e,
+	0x74, 0x73, 0x22, 0xc8, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78,
+	0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x34, 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, 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,
@@ -1382,46 +1537,50 @@
 	return file_metrics_proto_rawDescData
 }
 
-var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
-var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
 var file_metrics_proto_goTypes = []interface{}{
 	(MetricsBase_BuildVariant)(0),       // 0: soong_build_metrics.MetricsBase.BuildVariant
 	(MetricsBase_Arch)(0),               // 1: soong_build_metrics.MetricsBase.Arch
 	(ModuleTypeInfo_BuildSystem)(0),     // 2: soong_build_metrics.ModuleTypeInfo.BuildSystem
-	(*MetricsBase)(nil),                 // 3: soong_build_metrics.MetricsBase
-	(*BuildConfig)(nil),                 // 4: soong_build_metrics.BuildConfig
-	(*SystemResourceInfo)(nil),          // 5: soong_build_metrics.SystemResourceInfo
-	(*PerfInfo)(nil),                    // 6: soong_build_metrics.PerfInfo
-	(*ProcessResourceInfo)(nil),         // 7: soong_build_metrics.ProcessResourceInfo
-	(*ModuleTypeInfo)(nil),              // 8: soong_build_metrics.ModuleTypeInfo
-	(*CriticalUserJourneyMetrics)(nil),  // 9: soong_build_metrics.CriticalUserJourneyMetrics
-	(*CriticalUserJourneysMetrics)(nil), // 10: soong_build_metrics.CriticalUserJourneysMetrics
-	(*SoongBuildMetrics)(nil),           // 11: soong_build_metrics.SoongBuildMetrics
+	(ExpConfigFetcher_ConfigStatus)(0),  // 3: soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	(*MetricsBase)(nil),                 // 4: soong_build_metrics.MetricsBase
+	(*BuildConfig)(nil),                 // 5: soong_build_metrics.BuildConfig
+	(*SystemResourceInfo)(nil),          // 6: soong_build_metrics.SystemResourceInfo
+	(*PerfInfo)(nil),                    // 7: soong_build_metrics.PerfInfo
+	(*ProcessResourceInfo)(nil),         // 8: soong_build_metrics.ProcessResourceInfo
+	(*ModuleTypeInfo)(nil),              // 9: soong_build_metrics.ModuleTypeInfo
+	(*CriticalUserJourneyMetrics)(nil),  // 10: soong_build_metrics.CriticalUserJourneyMetrics
+	(*CriticalUserJourneysMetrics)(nil), // 11: soong_build_metrics.CriticalUserJourneysMetrics
+	(*SoongBuildMetrics)(nil),           // 12: soong_build_metrics.SoongBuildMetrics
+	(*ExpConfigFetcher)(nil),            // 13: soong_build_metrics.ExpConfigFetcher
 }
 var file_metrics_proto_depIdxs = []int32{
 	0,  // 0: soong_build_metrics.MetricsBase.target_build_variant:type_name -> soong_build_metrics.MetricsBase.BuildVariant
 	1,  // 1: soong_build_metrics.MetricsBase.target_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 2: soong_build_metrics.MetricsBase.host_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 3: soong_build_metrics.MetricsBase.host_2nd_arch:type_name -> soong_build_metrics.MetricsBase.Arch
-	6,  // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
-	6,  // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
-	6,  // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
-	6,  // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
-	6,  // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
-	11, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
-	4,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
-	5,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
-	6,  // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
-	7,  // 13: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
-	2,  // 14: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
-	3,  // 15: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
-	9,  // 16: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
-	6,  // 17: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
-	18, // [18:18] is the sub-list for method output_type
-	18, // [18:18] is the sub-list for method input_type
-	18, // [18:18] is the sub-list for extension type_name
-	18, // [18:18] is the sub-list for extension extendee
-	0,  // [0:18] is the sub-list for field type_name
+	7,  // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+	7,  // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+	7,  // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+	7,  // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+	7,  // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+	12, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+	5,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
+	6,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
+	7,  // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+	13, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
+	8,  // 14: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+	2,  // 15: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+	4,  // 16: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+	10, // 17: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+	7,  // 18: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+	3,  // 19: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	20, // [20:20] is the sub-list for method output_type
+	20, // [20:20] is the sub-list for method input_type
+	20, // [20:20] is the sub-list for extension type_name
+	20, // [20:20] is the sub-list for extension extendee
+	0,  // [0:20] is the sub-list for field type_name
 }
 
 func init() { file_metrics_proto_init() }
@@ -1538,14 +1697,26 @@
 				return nil
 			}
 		}
+		file_metrics_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ExpConfigFetcher); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_metrics_proto_rawDesc,
-			NumEnums:      3,
-			NumMessages:   9,
+			NumEnums:      4,
+			NumMessages:   10,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 26e4d73..814eb67 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -108,6 +108,9 @@
 
   // The metrics for calling Bazel.
   repeated PerfInfo bazel_runs = 27;
+
+  // The metrics of the experiment config fetcher
+  optional ExpConfigFetcher exp_config_fetcher = 28;
 }
 
 message BuildConfig {
@@ -239,3 +242,22 @@
   // Runtime metrics for soong_build execution.
   repeated PerfInfo events = 6;
 }
+
+message ExpConfigFetcher {
+  enum ConfigStatus {
+    NO_CONFIG = 0;
+    CONFIG = 1;
+    ERROR = 2;
+  }
+  // The result of the call to expconfigfetcher
+  // NO_CONFIG - Not part of experiment
+  // CONFIG - Part of experiment, config copied successfully
+  // ERROR - expconfigfetcher failed
+  optional ConfigStatus status = 1;
+
+  // The output config filename
+  optional string filename = 2;
+
+  // Time, in microseconds, taken by the expconfigfetcher
+  optional uint64 micros = 3;
+}