Merge changes from topic "stub-impl-per-api-domain"

* changes:
  Special case platform variant of bootstrap libs
  Select stub/impl per apex variant
  For test apexes, base_apex_name is the api domain
  Broaden the granularity of config_setting from apex_name to api_domain
  Print default val if all vals in axis match default val
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 433c063..71281a6 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -38,7 +38,13 @@
 	// allows modules to opt-in.
 	Bp2BuildDefaultFalseRecursively
 
-	DEFAULT_NINJA_WEIGHT = 1000
+	// Modules with build time of more than half a minute should have high priority.
+	DEFAULT_PRIORITIZED_WEIGHT = 1000
+	// Modules with build time of more than a few minute should have higher priority.
+	HIGH_PRIORITIZED_WEIGHT = 10 * DEFAULT_PRIORITIZED_WEIGHT
+	// Modules with inputs greater than the threshold should have high priority.
+	// Adjust this threshold if there are lots of wrong predictions.
+	INPUT_SIZE_THRESHOLD = 50
 )
 
 var (
@@ -59,7 +65,9 @@
 
 		"build/bazel":                        Bp2BuildDefaultTrueRecursively,
 		"build/make/target/product/security": Bp2BuildDefaultTrue,
+		"build/make/tools/protos":            Bp2BuildDefaultTrue,
 		"build/make/tools/releasetools":      Bp2BuildDefaultTrue,
+		"build/make/tools/sbom":              Bp2BuildDefaultTrue,
 		"build/make/tools/signapk":           Bp2BuildDefaultTrue,
 		"build/make/tools/zipalign":          Bp2BuildDefaultTrueRecursively,
 		"build/soong":                        Bp2BuildDefaultTrue,
@@ -305,6 +313,7 @@
 		"prebuilts/clang/host/linux-x86":                   Bp2BuildDefaultTrueRecursively,
 		"prebuilts/gradle-plugin":                          Bp2BuildDefaultTrueRecursively,
 		"prebuilts/runtime/mainline/platform/sdk":          Bp2BuildDefaultTrueRecursively,
+		"prebuilts/sdk":                                    Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/androidx":                   Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/androidx-legacy":            Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/extras/constraint-layout-x": Bp2BuildDefaultTrue,
@@ -449,9 +458,8 @@
 		"libidmap2_policies",
 		"libSurfaceFlingerProp",
 		// cc mainline modules
-		"code_coverage.policy",
-		"code_coverage.policy.other",
-		"codec2_soft_exports",
+
+		// com.android.media.swcodec
 		"com.android.media.swcodec",
 		"com.android.media.swcodec-androidManifest",
 		"com.android.media.swcodec-ld.config.txt",
@@ -459,6 +467,12 @@
 		"com.android.media.swcodec-mediaswcodec.rc",
 		"com.android.media.swcodec.certificate",
 		"com.android.media.swcodec.key",
+		"test_com.android.media.swcodec",
+
+		// deps
+		"code_coverage.policy",
+		"code_coverage.policy.other",
+		"codec2_soft_exports",
 		"flatbuffer_headers",
 		"framework-connectivity-protos",
 		"gemmlowp_headers",
@@ -1435,6 +1449,9 @@
 		"MetaDataBaseUnitTest", // depends on libstagefright
 		"AVCUtilsUnitTest",     // depends on libstagefright
 		"ColorUtilsTest",       // depends on libmediandk
+
+		// python_test_host with test data
+		"sbom_writers_test",
 	}
 
 	MixedBuildsDisabledList = []string{
@@ -1541,9 +1558,6 @@
 		"com.android.media.swcodec",
 		"test_com.android.media.swcodec",
 		"libstagefright_foundation",
-		"libcodec2_hidl@1.0",
-		"libcodec2_hidl@1.1",
-		"libcodec2_hidl@1.2",
 	}
 
 	// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
@@ -1562,267 +1576,12 @@
 	// "libstagefright_flacdec",
 	// "libutils",
 
-	// TODO(b/273282046): Make this list customizable to support various targets.
-	// The list of modules which are expected to spend lots of build time.
-	// With `--ninja_weight_source=soong`, ninja builds these modules and deps first.
-	HugeModulesMap = map[string]int{
-		"AccountManagementApp":                          DEFAULT_NINJA_WEIGHT,
-		"ActivityManagerPerfTestsStubApp1":              DEFAULT_NINJA_WEIGHT,
-		"ActivityManagerPerfTestsStubApp2":              DEFAULT_NINJA_WEIGHT,
-		"ActivityManagerPerfTestsStubApp3":              DEFAULT_NINJA_WEIGHT,
-		"api-stubs-docs-non-updatable":                  DEFAULT_NINJA_WEIGHT,
-		"AppCompatibilityTest":                          DEFAULT_NINJA_WEIGHT,
-		"AppTransitionTests":                            DEFAULT_NINJA_WEIGHT,
-		"art_compiler_tests":                            DEFAULT_NINJA_WEIGHT,
-		"art.module.intra.core.api.stubs.source":        DEFAULT_NINJA_WEIGHT,
-		"art.module.public.api.stubs.source":            DEFAULT_NINJA_WEIGHT,
-		"AttestationVerificationTest":                   DEFAULT_NINJA_WEIGHT,
-		"BatteryUsageStatsProtoTests":                   DEFAULT_NINJA_WEIGHT,
-		"bluetooth_test_gd_unit":                        DEFAULT_NINJA_WEIGHT,
-		"Bluetooth":                                     DEFAULT_NINJA_WEIGHT,
-		"BluetoothInstrumentationTests":                 DEFAULT_NINJA_WEIGHT,
-		"Calendar":                                      DEFAULT_NINJA_WEIGHT,
-		"CalendarProvider":                              DEFAULT_NINJA_WEIGHT,
-		"Camera2":                                       DEFAULT_NINJA_WEIGHT,
-		"CarRotaryControllerUnitTests":                  DEFAULT_NINJA_WEIGHT,
-		"CarSettingsForUnitTesting":                     DEFAULT_NINJA_WEIGHT,
-		"CarSettingsUnitTests":                          DEFAULT_NINJA_WEIGHT,
-		"CarSystemUI-tests":                             DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastApp":                              DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastLegacyApp":                        DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastReceiverOemUnitTests":             DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastServiceModule":                    DEFAULT_NINJA_WEIGHT,
-		"CompanionDeviceManager":                        DEFAULT_NINJA_WEIGHT,
-		"ConnectivityChecker":                           DEFAULT_NINJA_WEIGHT,
-		"Contacts":                                      DEFAULT_NINJA_WEIGHT,
-		"ContactsProvider":                              DEFAULT_NINJA_WEIGHT,
-		"ContentCapturePerfTests":                       DEFAULT_NINJA_WEIGHT,
-		"CorePerfTests":                                 DEFAULT_NINJA_WEIGHT,
-		"crosvm":                                        DEFAULT_NINJA_WEIGHT,
-		"CtsDomainVerificationDeviceMultiUserTestCases": DEFAULT_NINJA_WEIGHT,
-		"CtsLogdTestCases":                              DEFAULT_NINJA_WEIGHT,
-		"CtsMediaProviderTranscodeTests":                DEFAULT_NINJA_WEIGHT,
-		"CtsRollbackManagerHostTestHelperApp":           DEFAULT_NINJA_WEIGHT,
-		"CtsRollbackManagerHostTestHelperApp2":          DEFAULT_NINJA_WEIGHT,
-		"CtsRootPackageInstallerTestCases":              DEFAULT_NINJA_WEIGHT,
-		"CtsRootRollbackManagerHostTestHelperApp":       DEFAULT_NINJA_WEIGHT,
-		"CtsTranscodeTestAppSupportsHevc":               DEFAULT_NINJA_WEIGHT,
-		"CtsTranscodeTestAppSupportsSlowMotion":         DEFAULT_NINJA_WEIGHT,
-		"CuttlefishDisplayHotplugHelperApp":             DEFAULT_NINJA_WEIGHT,
-		"cvd-host_package":                              DEFAULT_NINJA_WEIGHT,
-		"DelegateTestApp":                               DEFAULT_NINJA_WEIGHT,
-		"DeskClock":                                     DEFAULT_NINJA_WEIGHT,
-		"Development":                                   DEFAULT_NINJA_WEIGHT,
-		"DeviceAdminTestApp":                            DEFAULT_NINJA_WEIGHT,
-		"DevicePolicyManagementRoleHolderTestApp":       DEFAULT_NINJA_WEIGHT,
-		"dex2oatd":                                      DEFAULT_NINJA_WEIGHT,
-		"DocumentsUI":                                   DEFAULT_NINJA_WEIGHT,
-		"EasterEgg":                                     DEFAULT_NINJA_WEIGHT,
-		"EffectProxyTest":                               DEFAULT_NINJA_WEIGHT,
-		"EmergencyInfo":                                 DEFAULT_NINJA_WEIGHT,
-		"EmptyTestApp":                                  DEFAULT_NINJA_WEIGHT,
-		"ExtServices":                                   DEFAULT_NINJA_WEIGHT,
-		"FacebookAppsScenarioTests":                     DEFAULT_NINJA_WEIGHT,
-		"flickerlib-core":                               DEFAULT_NINJA_WEIGHT,
-		"flickerlib":                                    DEFAULT_NINJA_WEIGHT,
-		"FlickerLibTest":                                DEFAULT_NINJA_WEIGHT,
-		"FlickerTests":                                  DEFAULT_NINJA_WEIGHT,
-		"framework-minus-apex":                          DEFAULT_NINJA_WEIGHT,
-		"framework-res":                                 DEFAULT_NINJA_WEIGHT,
-		"FrameworksCoreTests":                           DEFAULT_NINJA_WEIGHT,
-		"FrameworksMockingCoreTests":                    DEFAULT_NINJA_WEIGHT,
-		"FrameworksMockingServicesTests":                DEFAULT_NINJA_WEIGHT,
-		"FrameworksNetSmokeTests":                       DEFAULT_NINJA_WEIGHT,
-		"FrameworksNetTests":                            DEFAULT_NINJA_WEIGHT,
-		"FrameworksServicesTests":                       DEFAULT_NINJA_WEIGHT,
-		"FrameworksTelephonyTests":                      DEFAULT_NINJA_WEIGHT,
-		"FrameworksUiServicesTests":                     DEFAULT_NINJA_WEIGHT,
-		"FrameworksVcnTests":                            DEFAULT_NINJA_WEIGHT,
-		"Gallery2":                                      DEFAULT_NINJA_WEIGHT,
-		"GameCoreDevice":                                DEFAULT_NINJA_WEIGHT,
-		"GoogleBluetoothInstrumentationTests":           DEFAULT_NINJA_WEIGHT,
-		"guice_munged_srcs":                             DEFAULT_NINJA_WEIGHT,
-		"HalfSheetUX":                                   DEFAULT_NINJA_WEIGHT,
-		"ImePerfTests":                                  DEFAULT_NINJA_WEIGHT,
-		"imgdiag":                                       DEFAULT_NINJA_WEIGHT,
-		"ImsServiceEntitlement":                         DEFAULT_NINJA_WEIGHT,
-		"ImsServiceEntitlementUnitTests":                DEFAULT_NINJA_WEIGHT,
-		"InputTests":                                    DEFAULT_NINJA_WEIGHT,
-		"InstallTest":                                   DEFAULT_NINJA_WEIGHT,
-		"IntentResolver":                                DEFAULT_NINJA_WEIGHT,
-		"JankBench":                                     DEFAULT_NINJA_WEIGHT,
-		"jsilver":                                       DEFAULT_NINJA_WEIGHT,
-		"KeyChain":                                      DEFAULT_NINJA_WEIGHT,
-		"KeyChainTests":                                 DEFAULT_NINJA_WEIGHT,
-		"keystore2":                                     DEFAULT_NINJA_WEIGHT,
-		"LargeResourcesCompressed":                      DEFAULT_NINJA_WEIGHT,
-		"LatinIME":                                      DEFAULT_NINJA_WEIGHT,
-		"Launcher3QuickStepLib":                         DEFAULT_NINJA_WEIGHT,
-		"libaom":                                        DEFAULT_NINJA_WEIGHT,
-		"libart-broken":                                 DEFAULT_NINJA_WEIGHT,
-		"libart-compiler":                               DEFAULT_NINJA_WEIGHT,
-		"libart-disassembler":                           DEFAULT_NINJA_WEIGHT,
-		"libart":                                        DEFAULT_NINJA_WEIGHT,
-		"libartd":                                       DEFAULT_NINJA_WEIGHT,
-		"libaudiohal@7.1":                               DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_core_rs":                          DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_gd_unit_tests":                    DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_gd":                               DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_rs":                               DEFAULT_NINJA_WEIGHT,
-		"libbluetooth-for-tests":                        DEFAULT_NINJA_WEIGHT,
-		"libbt_common":                                  DEFAULT_NINJA_WEIGHT,
-		"libbt_packets_nonapex":                         DEFAULT_NINJA_WEIGHT,
-		"libbt_packets":                                 DEFAULT_NINJA_WEIGHT,
-		"libbt_shim_ffi":                                DEFAULT_NINJA_WEIGHT,
-		"libbt_shim":                                    DEFAULT_NINJA_WEIGHT,
-		"libbt-audio-hal-interface":                     DEFAULT_NINJA_WEIGHT,
-		"libbt-bta-core":                                DEFAULT_NINJA_WEIGHT,
-		"libbt-bta":                                     DEFAULT_NINJA_WEIGHT,
-		"libbt-common":                                  DEFAULT_NINJA_WEIGHT,
-		"libbt-hci":                                     DEFAULT_NINJA_WEIGHT,
-		"libbt-platform-protos-lite":                    DEFAULT_NINJA_WEIGHT,
-		"libbt-protos-lite":                             DEFAULT_NINJA_WEIGHT,
-		"libbt-sbc-decoder":                             DEFAULT_NINJA_WEIGHT,
-		"libc":                                          DEFAULT_NINJA_WEIGHT,
-		"libclap":                                       DEFAULT_NINJA_WEIGHT,
-		"libcodec2_soft_av1dec_gav1":                    DEFAULT_NINJA_WEIGHT,
-		"libcompositionengine_test":                     DEFAULT_NINJA_WEIGHT,
-		"libdevices":                                    DEFAULT_NINJA_WEIGHT,
-		"libfrontend_proto":                             DEFAULT_NINJA_WEIGHT,
-		"libhwtrust":                                    DEFAULT_NINJA_WEIGHT,
-		"libjni":                                        DEFAULT_NINJA_WEIGHT,
-		"libkeystore2":                                  DEFAULT_NINJA_WEIGHT,
-		"libkmr_ta":                                     DEFAULT_NINJA_WEIGHT,
-		"liblmp":                                        DEFAULT_NINJA_WEIGHT,
-		"libopenjdkjvmtid":                              DEFAULT_NINJA_WEIGHT,
-		"libprotobuf_deprecated":                        DEFAULT_NINJA_WEIGHT,
-		"libprotobuf":                                   DEFAULT_NINJA_WEIGHT,
-		"libregex":                                      DEFAULT_NINJA_WEIGHT,
-		"LibStatsPullTests":                             DEFAULT_NINJA_WEIGHT,
-		"libstd":                                        DEFAULT_NINJA_WEIGHT,
-		"libsurfaceflinger_unittest":                    DEFAULT_NINJA_WEIGHT,
-		"libsyn":                                        DEFAULT_NINJA_WEIGHT,
-		"libtokio":                                      DEFAULT_NINJA_WEIGHT,
-		"libuwb_core":                                   DEFAULT_NINJA_WEIGHT,
-		"libuwb_uci_jni_rust":                           DEFAULT_NINJA_WEIGHT,
-		"libuwb_uci_packets":                            DEFAULT_NINJA_WEIGHT,
-		"libvpx":                                        DEFAULT_NINJA_WEIGHT,
-		"libvulkan_enc":                                 DEFAULT_NINJA_WEIGHT,
-		"libwebrtc":                                     DEFAULT_NINJA_WEIGHT,
-		"LiveWallpapersPicker":                          DEFAULT_NINJA_WEIGHT,
-		"LockTaskApp":                                   DEFAULT_NINJA_WEIGHT,
-		"LongevityPlatformLibTests":                     DEFAULT_NINJA_WEIGHT,
-		"ManagedProvisioning":                           DEFAULT_NINJA_WEIGHT,
-		"ManagedProvisioningTests":                      DEFAULT_NINJA_WEIGHT,
-		"MediaProvider":                                 DEFAULT_NINJA_WEIGHT,
-		"MediaProviderClientTests":                      DEFAULT_NINJA_WEIGHT,
-		"MediaProviderLegacy":                           DEFAULT_NINJA_WEIGHT,
-		"messaging":                                     DEFAULT_NINJA_WEIGHT,
-		"metalava":                                      DEFAULT_NINJA_WEIGHT,
-		"MicrobenchmarkRunnerTests":                     DEFAULT_NINJA_WEIGHT,
-		"microdroid_manager":                            DEFAULT_NINJA_WEIGHT,
-		"minikin_tests":                                 DEFAULT_NINJA_WEIGHT,
-		"MLCTestApp":                                    DEFAULT_NINJA_WEIGHT,
-		"MmsService":                                    DEFAULT_NINJA_WEIGHT,
-		"MmsServiceTests":                               DEFAULT_NINJA_WEIGHT,
-		"module-lib-api-stubs-docs-non-updatable":       DEFAULT_NINJA_WEIGHT,
-		"motion_tool_lib_tests":                         DEFAULT_NINJA_WEIGHT,
-		"MtpService":                                    DEFAULT_NINJA_WEIGHT,
-		"MultiUserTests":                                DEFAULT_NINJA_WEIGHT,
-		"NearbyIntegrationUiTests":                      DEFAULT_NINJA_WEIGHT,
-		"net_test_bluetooth":                            DEFAULT_NINJA_WEIGHT,
-		"net_test_btif":                                 DEFAULT_NINJA_WEIGHT,
-		"net_test_main_shim":                            DEFAULT_NINJA_WEIGHT,
-		"net_test_stack":                                DEFAULT_NINJA_WEIGHT,
-		"net-tests-utils":                               DEFAULT_NINJA_WEIGHT,
-		"NetworkStackCoverageTests":                     DEFAULT_NINJA_WEIGHT,
-		"NetworkStackIntegrationTests":                  DEFAULT_NINJA_WEIGHT,
-		"NetworkStackNext":                              DEFAULT_NINJA_WEIGHT,
-		"NfcNci":                                        DEFAULT_NINJA_WEIGHT,
-		"NfcNciUnitTests":                               DEFAULT_NINJA_WEIGHT,
-		"NotEmptyTestApp":                               DEFAULT_NINJA_WEIGHT,
-		"NotificationFunctionalTests":                   DEFAULT_NINJA_WEIGHT,
-		"oatdumpd":                                      DEFAULT_NINJA_WEIGHT,
-		"OsuLogin":                                      DEFAULT_NINJA_WEIGHT,
-		"PackageInstaller":                              DEFAULT_NINJA_WEIGHT,
-		"PackageManagerComponentOverrideTests":          DEFAULT_NINJA_WEIGHT,
-		"PackageManagerPerfTests":                       DEFAULT_NINJA_WEIGHT,
-		"PackageManagerServiceServerTests":              DEFAULT_NINJA_WEIGHT,
-		"PackageManagerServiceUnitTests":                DEFAULT_NINJA_WEIGHT,
-		"PackageWatchdogTest":                           DEFAULT_NINJA_WEIGHT,
-		"PandoraServerLib":                              DEFAULT_NINJA_WEIGHT,
-		"pdl":                                           DEFAULT_NINJA_WEIGHT,
-		"perfetto_trace_java_protos":                    DEFAULT_NINJA_WEIGHT,
-		"perfetto_trace-full":                           DEFAULT_NINJA_WEIGHT,
-		"PermissionController":                          DEFAULT_NINJA_WEIGHT,
-		"PermissionControllerMockingTests":              DEFAULT_NINJA_WEIGHT,
-		"PixelAppCompTests":                             DEFAULT_NINJA_WEIGHT,
-		"platform-bootclasspath":                        DEFAULT_NINJA_WEIGHT,
-		"PlatformCommonScenarioTests":                   DEFAULT_NINJA_WEIGHT,
-		"PlatformComposeCoreTests":                      DEFAULT_NINJA_WEIGHT,
-		"platformprotoslite":                            DEFAULT_NINJA_WEIGHT,
-		"PlatformRuleTests":                             DEFAULT_NINJA_WEIGHT,
-		"precompiled_sepolicy-without_apex":             DEFAULT_NINJA_WEIGHT,
-		"PresencePolling":                               DEFAULT_NINJA_WEIGHT,
-		"PrintSpooler":                                  DEFAULT_NINJA_WEIGHT,
-		"QuickSearchBox":                                DEFAULT_NINJA_WEIGHT,
-		"RemoteDPCTestApp":                              DEFAULT_NINJA_WEIGHT,
-		"RemoteProvisioningServiceTests":                DEFAULT_NINJA_WEIGHT,
-		"RkpdAppUnitTests":                              DEFAULT_NINJA_WEIGHT,
-		"Robolectric_shadows_framework":                 DEFAULT_NINJA_WEIGHT,
-		"RoleHolderApp":                                 DEFAULT_NINJA_WEIGHT,
-		"SdkSandbox":                                    DEFAULT_NINJA_WEIGHT,
-		"service-appsearch":                             DEFAULT_NINJA_WEIGHT,
-		"service-connectivity":                          DEFAULT_NINJA_WEIGHT,
-		"service-uwb":                                   DEFAULT_NINJA_WEIGHT,
-		"service-wifi":                                  DEFAULT_NINJA_WEIGHT,
-		"services-non-updatable-stubs":                  DEFAULT_NINJA_WEIGHT,
-		"services":                                      DEFAULT_NINJA_WEIGHT,
-		"Settings-core":                                 DEFAULT_NINJA_WEIGHT,
-		"Settings":                                      DEFAULT_NINJA_WEIGHT,
-		"SettingsIntelligence":                          DEFAULT_NINJA_WEIGHT,
-		"SettingsLibTests":                              DEFAULT_NINJA_WEIGHT,
-		"SettingsProvider":                              DEFAULT_NINJA_WEIGHT,
-		"Shell":                                         DEFAULT_NINJA_WEIGHT,
-		"SimAppDialog":                                  DEFAULT_NINJA_WEIGHT,
-		"sl4a":                                          DEFAULT_NINJA_WEIGHT,
-		"SmsApp":                                        DEFAULT_NINJA_WEIGHT,
-		"SoundPicker":                                   DEFAULT_NINJA_WEIGHT,
-		"StagedInstallTest":                             DEFAULT_NINJA_WEIGHT,
-		"StatementService":                              DEFAULT_NINJA_WEIGHT,
-		"StatsdFrameworkTestApp":                        DEFAULT_NINJA_WEIGHT,
-		"StatsdFrameworkTestAppNoPermission":            DEFAULT_NINJA_WEIGHT,
-		"statsdprotolite":                               DEFAULT_NINJA_WEIGHT,
-		"Stk":                                           DEFAULT_NINJA_WEIGHT,
-		"StorageManager":                                DEFAULT_NINJA_WEIGHT,
-		"system-api-stubs-docs-non-updatable":           DEFAULT_NINJA_WEIGHT,
-		"SystemUI-core":                                 DEFAULT_NINJA_WEIGHT,
-		"SystemUI-tests-base":                           DEFAULT_NINJA_WEIGHT,
-		"SystemUI-tests":                                DEFAULT_NINJA_WEIGHT,
-		"SystemUI":                                      DEFAULT_NINJA_WEIGHT,
-		"SystemUIComposeFeatures":                       DEFAULT_NINJA_WEIGHT,
-		"SystemUIComposeFeaturesTests":                  DEFAULT_NINJA_WEIGHT,
-		"SystemUITests":                                 DEFAULT_NINJA_WEIGHT,
-		"Tag":                                           DEFAULT_NINJA_WEIGHT,
-		"Telecom":                                       DEFAULT_NINJA_WEIGHT,
-		"TelecomUnitTests":                              DEFAULT_NINJA_WEIGHT,
-		"telephony-common":                              DEFAULT_NINJA_WEIGHT,
-		"TelephonyProvider":                             DEFAULT_NINJA_WEIGHT,
-		"TeleService":                                   DEFAULT_NINJA_WEIGHT,
-		"test-api-stubs-docs-non-updatable":             DEFAULT_NINJA_WEIGHT,
-		"TetheringIntegrationTests":                     DEFAULT_NINJA_WEIGHT,
-		"TetheringNext":                                 DEFAULT_NINJA_WEIGHT,
-		"ThemePickerTests":                              DEFAULT_NINJA_WEIGHT,
-		"Traceur":                                       DEFAULT_NINJA_WEIGHT,
-		"UsbManagerTests":                               DEFAULT_NINJA_WEIGHT,
-		"UsbTests":                                      DEFAULT_NINJA_WEIGHT,
-		"virtmgr":                                       DEFAULT_NINJA_WEIGHT,
-		"WallpaperPicker2TestLib":                       DEFAULT_NINJA_WEIGHT,
-		"WallpaperPicker2Tests":                         DEFAULT_NINJA_WEIGHT,
-		"WifiDialog":                                    DEFAULT_NINJA_WEIGHT,
-		"wm-proto-parsers":                              DEFAULT_NINJA_WEIGHT,
-		"WMShellFlickerTests":                           DEFAULT_NINJA_WEIGHT,
-		"WmTests":                                       DEFAULT_NINJA_WEIGHT,
-		"wpa_supplicant":                                DEFAULT_NINJA_WEIGHT,
+	// The list of module types which are expected to spend lots of build time.
+	// With `--ninja_weight_source=soong`, ninja builds these module types and deps first.
+	HugeModuleTypePrefixMap = map[string]int{
+		"rust_":       HIGH_PRIORITIZED_WEIGHT,
+		"droidstubs":  DEFAULT_PRIORITIZED_WEIGHT,
+		"art_":        DEFAULT_PRIORITIZED_WEIGHT,
+		"ndk_library": DEFAULT_PRIORITIZED_WEIGHT,
 	}
 )
diff --git a/android/bazel.go b/android/bazel.go
index 58d9d87..114b1f5 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -71,7 +71,7 @@
 	MissingDeps []string `blueprint:"mutated"`
 }
 
-type bazelModuleProperties struct {
+type BazelModuleProperties struct {
 	// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
 	// will import the handcrafted build target into the autogenerated file. Note: this may result in
 	// a conflict due to duplicate targets if bp2build_available is also set.
@@ -96,7 +96,7 @@
 type properties struct {
 	// In "Bazel mixed build" mode, this represents the Bazel target replacing
 	// this Soong module.
-	Bazel_module bazelModuleProperties
+	Bazel_module BazelModuleProperties
 }
 
 // namespacedVariableProperties is a map from a string representing a Soong
@@ -431,7 +431,7 @@
 	}
 
 	propValue := b.bazelProperties.Bazel_module.Bp2build_available
-	packagePath := ctx.OtherModuleDir(module)
+	packagePath := moduleDirWithPossibleOverride(ctx, 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
@@ -440,7 +440,7 @@
 		return true
 	}
 
-	moduleName := module.Name()
+	moduleName := moduleNameWithPossibleOverride(ctx, module)
 	allowlist := ctx.Config().Bp2buildPackageConfig
 	moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
 	moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index bad7baf..ddbdbd4 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -453,8 +453,8 @@
 }
 
 func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
-	moduleName := ctx.OtherModuleName(module)
-	moduleDir := ctx.OtherModuleDir(module)
+	moduleName := moduleNameWithPossibleOverride(ctx, module)
+	moduleDir := moduleDirWithPossibleOverride(ctx, module)
 	if moduleDir == Bp2BuildTopLevel {
 		moduleDir = ""
 	}
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 87b2c8f..77e2515 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -218,7 +218,7 @@
 
 var bazelableBazelModuleBase = BazelModuleBase{
 	bazelProperties: properties{
-		Bazel_module: bazelModuleProperties{
+		Bazel_module: BazelModuleProperties{
 			CanConvertToBazel: true,
 		},
 	},
@@ -344,7 +344,7 @@
 				},
 				BazelModuleBase: BazelModuleBase{
 					bazelProperties: properties{
-						Bazel_module: bazelModuleProperties{
+						Bazel_module: BazelModuleProperties{
 							CanConvertToBazel:  true,
 							Bp2build_available: proptools.BoolPtr(true),
 						},
diff --git a/android/config.go b/android/config.go
index 0b54ed9..9e94e05 100644
--- a/android/config.go
+++ b/android/config.go
@@ -606,7 +606,7 @@
 	setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
 	setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
 
-	for _, module := range strings.Split(cmdArgs.BazelForceEnabledModules, ",") {
+	for _, module := range getForceEnabledModulesFromFlag(cmdArgs.BazelForceEnabledModules) {
 		config.bazelForceEnabledModules[module] = struct{}{}
 	}
 	config.BazelContext, err = NewBazelContext(config)
@@ -615,6 +615,13 @@
 	return Config{config}, err
 }
 
+func getForceEnabledModulesFromFlag(forceEnabledFlag string) []string {
+	if forceEnabledFlag == "" {
+		return []string{}
+	}
+	return strings.Split(forceEnabledFlag, ",")
+}
+
 // mockFileSystem replaces all reads with accesses to the provided map of
 // filenames to contents stored as a byte slice.
 func (c *config) mockFileSystem(bp string, fs map[string][]byte) {
@@ -1929,6 +1936,10 @@
 	return c.mixedBuildEnabledModules
 }
 
+func (c *config) GetMixedBuildsDisabledModules() map[string]struct{} {
+	return c.mixedBuildDisabledModules
+}
+
 func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) {
 	moduleName := ctx.Module().Name()
 	c.mixedBuildsLock.Lock()
diff --git a/android/deptag.go b/android/deptag.go
index be5c35c..a15443b 100644
--- a/android/deptag.go
+++ b/android/deptag.go
@@ -34,10 +34,10 @@
 
 var _ InstallNeededDependencyTag = InstallAlwaysNeededDependencyTag{}
 
-// IsInstallDepNeeded returns true if the dependency tag implements the InstallNeededDependencyTag
+// IsInstallDepNeededTag returns true if the dependency tag implements the InstallNeededDependencyTag
 // interface and the InstallDepNeeded returns true, meaning that the installed files of the parent
 // should depend on the installed files of the child.
-func IsInstallDepNeeded(tag blueprint.DependencyTag) bool {
+func IsInstallDepNeededTag(tag blueprint.DependencyTag) bool {
 	if i, ok := tag.(InstallNeededDependencyTag); ok {
 		return i.InstallDepNeeded()
 	}
diff --git a/android/filegroup.go b/android/filegroup.go
index 0ca5dc5..f30ee51 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -75,7 +75,8 @@
 
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
-	Srcs bazel.LabelListAttribute
+	Srcs                bazel.LabelListAttribute
+	Applicable_licenses bazel.LabelListAttribute
 }
 
 type bazelAidlLibraryAttributes struct {
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 18b63d3..73000a9 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -74,7 +74,7 @@
 		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
 			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
-			if isContainer || IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+			if isContainer || isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
 				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
 			}
 
diff --git a/android/module.go b/android/module.go
index c8670c3..db602a0 100644
--- a/android/module.go
+++ b/android/module.go
@@ -925,6 +925,12 @@
 	// and don't create a rule to install the file.
 	SkipInstall bool `blueprint:"mutated"`
 
+	// UninstallableApexPlatformVariant is set by MakeUninstallable called by the apex
+	// mutator.  MakeUninstallable also sets HideFromMake.  UninstallableApexPlatformVariant
+	// is used to avoid adding install or packaging dependencies into libraries provided
+	// by apexes.
+	UninstallableApexPlatformVariant bool `blueprint:"mutated"`
+
 	// Whether the module has been replaced by a prebuilt
 	ReplacedByPrebuilt bool `blueprint:"mutated"`
 
@@ -2009,6 +2015,7 @@
 // have other side effects, in particular when it adds a NOTICE file target,
 // which other install targets might depend on.
 func (m *ModuleBase) MakeUninstallable() {
+	m.commonProperties.UninstallableApexPlatformVariant = true
 	m.HideFromMake()
 }
 
@@ -2038,13 +2045,19 @@
 }
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
-// tag that is annotated as needing installation via the IsInstallDepNeeded method.
+// tag that is annotated as needing installation via the isInstallDepNeeded method.
 func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
 	var installDeps []*installPathsDepSet
 	var packagingSpecs []*packagingSpecsDepSet
 	ctx.VisitDirectDeps(func(dep Module) {
-		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() && !dep.IsSkipInstall() {
-			installDeps = append(installDeps, dep.base().installFilesDepSet)
+		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
+			// Installation is still handled by Make, so anything hidden from Make is not
+			// installable.
+			if !dep.IsHideFromMake() && !dep.IsSkipInstall() {
+				installDeps = append(installDeps, dep.base().installFilesDepSet)
+			}
+			// Add packaging deps even when the dependency is not installed so that uninstallable
+			// modules can still be packaged.  Often the package will be installed instead.
 			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
 		}
 	})
@@ -2052,6 +2065,17 @@
 	return installDeps, packagingSpecs
 }
 
+// isInstallDepNeeded returns true if installing the output files of the current module
+// should also install the output files of the given dependency and dependency tag.
+func isInstallDepNeeded(dep Module, tag blueprint.DependencyTag) bool {
+	// Don't add a dependency from the platform to a library provided by an apex.
+	if dep.base().commonProperties.UninstallableApexPlatformVariant {
+		return false
+	}
+	// Only install modules if the dependency tag is an InstallDepNeeded tag.
+	return IsInstallDepNeededTag(tag)
+}
+
 func (m *ModuleBase) FilesToInstall() InstallPaths {
 	return m.installFiles
 }
diff --git a/android/override_module.go b/android/override_module.go
index 86f582b..a4b7431 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,7 +28,6 @@
 // module based on it.
 
 import (
-	"fmt"
 	"sort"
 	"sync"
 
@@ -47,8 +46,8 @@
 
 	// Internal funcs to handle interoperability between override modules and prebuilts.
 	// i.e. cases where an overriding module, too, is overridden by a prebuilt module.
-	setOverriddenByPrebuilt(overridden bool)
-	getOverriddenByPrebuilt() bool
+	setOverriddenByPrebuilt(prebuilt Module)
+	getOverriddenByPrebuilt() Module
 
 	// Directory containing the Blueprint definition of the overriding module
 	setModuleDir(string)
@@ -61,7 +60,7 @@
 
 	overridingProperties []interface{}
 
-	overriddenByPrebuilt bool
+	overriddenByPrebuilt Module
 
 	moduleDir string
 }
@@ -97,11 +96,11 @@
 	return proptools.String(o.moduleProperties.Base)
 }
 
-func (o *OverrideModuleBase) setOverriddenByPrebuilt(overridden bool) {
-	o.overriddenByPrebuilt = overridden
+func (o *OverrideModuleBase) setOverriddenByPrebuilt(prebuilt Module) {
+	o.overriddenByPrebuilt = prebuilt
 }
 
-func (o *OverrideModuleBase) getOverriddenByPrebuilt() bool {
+func (o *OverrideModuleBase) getOverriddenByPrebuilt() Module {
 	return o.overriddenByPrebuilt
 }
 
@@ -121,7 +120,7 @@
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
-	override(ctx BaseModuleContext, o OverrideModule)
+	override(ctx BaseModuleContext, m Module, o OverrideModule)
 	GetOverriddenBy() string
 	GetOverriddenByModuleDir() string
 
@@ -192,7 +191,8 @@
 }
 
 // Overrides a base module with the given OverrideModule.
-func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) {
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) {
+
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
@@ -214,6 +214,17 @@
 	}
 	b.overridableModuleProperties.OverriddenBy = o.Name()
 	b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
+
+	if oBazelable, ok := o.base().module.(Bazelable); ok {
+		if bBazelable, ok := m.(Bazelable); ok {
+			oProps := oBazelable.bazelProps()
+			bProps := bBazelable.bazelProps()
+			bProps.Bazel_module.Bp2build_available = oProps.Bazel_module.Bp2build_available
+			bProps.Bazel_module.Label = oProps.Bazel_module.Label
+		} else {
+			ctx.ModuleErrorf("Override type cannot be Bazelable if original module type is not Bazelable %v %v.", o.Name(), m.Name())
+		}
+	}
 }
 
 // GetOverriddenBy returns the name of the override module that has overridden this module.
@@ -270,7 +281,7 @@
 				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
 			}
 			if prebuilt.UsePrebuilt() {
-				module.setOverriddenByPrebuilt(true)
+				module.setOverriddenByPrebuilt(dep)
 				return
 			}
 		})
@@ -302,9 +313,11 @@
 		// is specified.
 		ctx.AliasVariation(variants[0])
 		for i, o := range overrides {
-			mods[i+1].(OverridableModule).override(ctx, o)
-			if o.getOverriddenByPrebuilt() {
+			mods[i+1].(OverridableModule).override(ctx, mods[i+1], o)
+			if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil {
 				// The overriding module itself, too, is overridden by a prebuilt.
+				// Perform the same check for replacement
+				checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt)
 				// Copy the flag and hide it in make
 				mods[i+1].ReplacedByPrebuilt()
 			}
@@ -340,34 +353,26 @@
 // variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
 // or if this variant is not overridden.
 func ModuleNameWithPossibleOverride(ctx BazelConversionContext) string {
-	if overridable, ok := ctx.Module().(OverridableModule); ok {
+	return moduleNameWithPossibleOverride(ctx, ctx.Module())
+}
+
+func moduleNameWithPossibleOverride(ctx bazelOtherModuleContext, module blueprint.Module) string {
+	if overridable, ok := module.(OverridableModule); ok {
 		if o := overridable.GetOverriddenBy(); o != "" {
 			return o
 		}
 	}
-	return ctx.OtherModuleName(ctx.Module())
+	return ctx.OtherModuleName(module)
 }
 
-// ModuleDirWithPossibleOverride returns the dir of the OverrideModule that overrides the current
-// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
-// or if this variant is not overridden.
-func moduleDirWithPossibleOverride(ctx BazelConversionContext) string {
-	if overridable, ok := ctx.Module().(OverridableModule); ok {
+// moduleDirWithPossibleOverride returns the dir of the OverrideModule that overrides the current
+// variant of the given OverridableModule, or ctx.OtherModuleName() if the module is not an
+// OverridableModule or if the variant is not overridden.
+func moduleDirWithPossibleOverride(ctx bazelOtherModuleContext, module blueprint.Module) string {
+	if overridable, ok := module.(OverridableModule); ok {
 		if o := overridable.GetOverriddenByModuleDir(); o != "" {
 			return o
 		}
 	}
-	return ctx.OtherModuleDir(ctx.Module())
-}
-
-// MaybeBp2buildLabelOfOverridingModule returns the bazel label of the
-// overriding module of an OverridableModule (e.g. override_apex label of a base
-// apex), or the module's label itself if not overridden.
-func MaybeBp2buildLabelOfOverridingModule(ctx BazelConversionContext) string {
-	moduleName := ModuleNameWithPossibleOverride(ctx)
-	moduleDir := moduleDirWithPossibleOverride(ctx)
-	if moduleDir == Bp2BuildTopLevel {
-		moduleDir = ""
-	}
-	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
+	return ctx.OtherModuleDir(module)
 }
diff --git a/android/package.go b/android/package.go
index 2bf6521..7fbc700 100644
--- a/android/package.go
+++ b/android/package.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"path/filepath"
+
 	"android/soong/bazel"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -39,8 +41,8 @@
 }
 
 type bazelPackageAttributes struct {
-	Default_visibility          []string
-	Default_applicable_licenses bazel.LabelListAttribute
+	Default_visibility       []string
+	Default_package_metadata bazel.LabelListAttribute
 }
 
 type packageModule struct {
@@ -53,13 +55,32 @@
 var _ Bazelable = &packageModule{}
 
 func (p *packageModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+	defaultPackageMetadata := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses))
+	// If METADATA file exists in the package, add it to package(default_package_metadata=) using a
+	// filegroup(name="default_metadata_file") which can be accessed later on each module in Bazel
+	// using attribute "applicable_licenses".
+	// Attribute applicable_licenses of filegroup "default_metadata_file" has to be set to [],
+	// otherwise Bazel reports cyclic reference error.
+	if existed, _, _ := ctx.Config().fs.Exists(filepath.Join(ctx.ModuleDir(), "METADATA")); existed {
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class: "filegroup",
+			},
+			CommonAttributes{Name: "default_metadata_file"},
+			&bazelFilegroupAttributes{
+				Srcs:                bazel.MakeLabelListAttribute(BazelLabelForModuleSrc(ctx, []string{"METADATA"})),
+				Applicable_licenses: bazel.LabelListAttribute{Value: bazel.LabelList{Includes: []bazel.Label{}}, EmitEmptyList: true},
+			})
+		defaultPackageMetadata.Value.Add(&bazel.Label{Label: ":default_metadata_file"})
+	}
+
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class: "package",
 		},
 		CommonAttributes{},
 		&bazelPackageAttributes{
-			Default_applicable_licenses: bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses)),
+			Default_package_metadata: defaultPackageMetadata,
 			// FIXME(asmundak): once b/221436821 is resolved
 			Default_visibility: []string{"//visibility:public"},
 		})
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 91ac1f3..3833437 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -373,7 +373,7 @@
 
 func TestPackagingWithSkipInstallDeps(t *testing.T) {
 	// package -[dep]-> foo -[dep]-> bar      -[dep]-> baz
-	//                  OK           SKIPPED
+	// Packaging should continue transitively through modules that are not installed.
 	multiTarget := false
 	runPackagingTest(t, multiTarget,
 		`
@@ -396,5 +396,5 @@
 			name: "package",
 			deps: ["foo"],
 		}
-		`, []string{"lib64/foo"})
+		`, []string{"lib64/foo", "lib64/bar", "lib64/baz"})
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 9b5c0e9..95b772d 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -419,6 +419,20 @@
 	}
 }
 
+// checkInvariantsForSourceAndPrebuilt checks if invariants are kept when replacing
+// source with prebuilt. Note that the current module for the context is the source module.
+func checkInvariantsForSourceAndPrebuilt(ctx BaseModuleContext, s, p Module) {
+	if _, ok := s.(OverrideModule); ok {
+		// skip the check when the source module is `override_X` because it's only a placeholder
+		// for the actual source module. The check will be invoked for the actual module.
+		return
+	}
+	if sourcePartition, prebuiltPartition := s.PartitionTag(ctx.DeviceConfig()), p.PartitionTag(ctx.DeviceConfig()); sourcePartition != prebuiltPartition {
+		ctx.OtherModuleErrorf(p, "partition is different: %s(%s) != %s(%s)",
+			sourcePartition, ctx.ModuleName(), prebuiltPartition, ctx.OtherModuleName(p))
+	}
+}
+
 // PrebuiltSelectModuleMutator marks prebuilts that are used, either overriding source modules or
 // because the source module doesn't exist.  It also disables installing overridden source modules.
 func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
@@ -434,6 +448,8 @@
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(prebuiltModule Module) {
 			p := GetEmbeddedPrebuilt(prebuiltModule)
 			if p.usePrebuilt(ctx, s, prebuiltModule) {
+				checkInvariantsForSourceAndPrebuilt(ctx, s, prebuiltModule)
+
 				p.properties.UsePrebuilt = true
 				s.ReplacedByPrebuilt()
 			}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index fa40d1f..fc47cfd 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -497,6 +497,52 @@
 	}
 }
 
+func testPrebuiltError(t *testing.T, expectedError, bp string) {
+	t.Helper()
+	fs := MockFS{
+		"prebuilt_file": nil,
+	}
+	GroupFixturePreparers(
+		PrepareForTestWithArchMutator,
+		PrepareForTestWithPrebuilts,
+		PrepareForTestWithOverrides,
+		fs.AddToFixture(),
+		FixtureRegisterWithContext(registerTestPrebuiltModules),
+	).
+		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(expectedError)).
+		RunTestWithBp(t, bp)
+}
+
+func TestPrebuiltShouldNotChangePartition(t *testing.T) {
+	testPrebuiltError(t, `partition is different`, `
+		source {
+			name: "foo",
+			vendor: true,
+		}
+		prebuilt {
+			name: "foo",
+			prefer: true,
+			srcs: ["prebuilt_file"],
+		}`)
+}
+
+func TestPrebuiltShouldNotChangePartition_WithOverride(t *testing.T) {
+	testPrebuiltError(t, `partition is different`, `
+		source {
+			name: "foo",
+			vendor: true,
+		}
+		override_source {
+			name: "bar",
+			base: "foo",
+		}
+		prebuilt {
+			name: "bar",
+			prefer: true,
+			srcs: ["prebuilt_file"],
+		}`)
+}
+
 func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
 	registerTestPrebuiltModules(ctx)
 
diff --git a/android/testing.go b/android/testing.go
index fc39a9c..2a9c658 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -813,6 +813,20 @@
 	return path.RelativeToTop()
 }
 
+func allOutputs(p BuildParams) []string {
+	outputs := append(WritablePaths(nil), p.Outputs...)
+	outputs = append(outputs, p.ImplicitOutputs...)
+	if p.Output != nil {
+		outputs = append(outputs, p.Output)
+	}
+	return outputs.Strings()
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (p TestingBuildParams) AllOutputs() []string {
+	return allOutputs(p.BuildParams)
+}
+
 // baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
 type baseTestingComponent struct {
 	config   Config
@@ -954,12 +968,7 @@
 func (b baseTestingComponent) allOutputs() []string {
 	var outputFullPaths []string
 	for _, p := range b.provider.BuildParamsForTests() {
-		outputs := append(WritablePaths(nil), p.Outputs...)
-		outputs = append(outputs, p.ImplicitOutputs...)
-		if p.Output != nil {
-			outputs = append(outputs, p.Output)
-		}
-		outputFullPaths = append(outputFullPaths, outputs.Strings()...)
+		outputFullPaths = append(outputFullPaths, allOutputs(p)...)
 	}
 	return outputFullPaths
 }
diff --git a/android/variable.go b/android/variable.go
index 496f523..bf66135 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -469,6 +469,10 @@
 	SourceRootDirs []string `json:",omitempty"`
 
 	AfdoProfiles []string `json:",omitempty"`
+
+	ProductManufacturer string   `json:",omitempty"`
+	ProductBrand        string   `json:",omitempty"`
+	BuildVersionTags    []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -537,124 +541,102 @@
 	Module() Module
 }
 
-// ProductConfigProperty contains the information for a single property (may be a struct) paired
-// with the appropriate ProductConfigVariable.
+// ProductConfigOrSoongConfigProperty represents either a soong config variable + its value
+// or a product config variable. You can get both a ConfigurationAxis and a SelectKey from it
+// for use in bazel attributes. ProductVariableProperties() will return a map from properties ->
+// this interface -> property structs for use in bp2build converters
+type ProductConfigOrSoongConfigProperty interface {
+	// Name of the product variable or soong config variable
+	Name() string
+	// AlwaysEmit returns true for soong config variables but false for product variables. This
+	// is intended to indicate if we need to always emit empty lists in the select statements.
+	AlwaysEmit() bool
+	// ConfigurationAxis returns the bazel.ConfigurationAxis that represents this variable. The
+	// configuration axis will change depending on the variable and whether it's arch/os variant
+	// as well.
+	ConfigurationAxis() bazel.ConfigurationAxis
+	// SelectKey returns a string that represents the key of a select branch, however, it is not
+	// actually the real label written out to the build file.
+	// this.ConfigurationAxis().SelectKey(this.SelectKey()) will give the actual label.
+	SelectKey() string
+}
+
+// ProductConfigProperty represents a product config variable, and if it is arch-variant or not.
 type ProductConfigProperty struct {
 	// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
 	// "board"
-	Name string
+	name string
 
-	// Namespace of the variable, if this is a soong_config_module_type variable
-	// e.g. "acme", "ANDROID", "vendor_name"
-	Namespace string
-
-	// Unique configuration to identify this product config property (i.e. a
-	// primary key), as just using the product variable name is not sufficient.
-	//
-	// For product variables, this is the product variable name + optional
-	// archvariant information. e.g.
-	//
-	// product_variables: {
-	//     foo: {
-	//         cflags: ["-Dfoo"],
-	//     },
-	// },
-	//
-	// FullConfig would be "foo".
-	//
-	// target: {
-	//     android: {
-	//         product_variables: {
-	//             foo: {
-	//                 cflags: ["-Dfoo-android"],
-	//             },
-	//         },
-	//     },
-	// },
-	//
-	// FullConfig would be "foo-android".
-	//
-	// For soong config variables, this is the namespace + product variable name
-	// + value of the variable, if applicable. The value can also be
-	// conditions_default.
-	//
-	// e.g.
-	//
-	// soong_config_variables: {
-	//     feature1: {
-	//         conditions_default: {
-	//             cflags: ["-DDEFAULT1"],
-	//         },
-	//         cflags: ["-DFEATURE1"],
-	//     },
-	// }
-	//
-	// where feature1 is created in the "acme" namespace, so FullConfig would be
-	// "acme__feature1" and "acme__feature1__conditions_default".
-	//
-	// e.g.
-	//
-	// soong_config_variables: {
-	//     board: {
-	//         soc_a: {
-	//             cflags: ["-DSOC_A"],
-	//         },
-	//         soc_b: {
-	//             cflags: ["-DSOC_B"],
-	//         },
-	//         soc_c: {},
-	//         conditions_default: {
-	//             cflags: ["-DSOC_DEFAULT"]
-	//         },
-	//     },
-	// }
-	//
-	// where board is created in the "acme" namespace, so FullConfig would be
-	// "acme__board__soc_a", "acme__board__soc_b", and
-	// "acme__board__conditions_default"
-	FullConfig string
-
-	// keeps track of whether this product variable is nested under an arch variant
-	OuterAxis bazel.ConfigurationAxis
+	arch string
 }
 
-func (p *ProductConfigProperty) AlwaysEmit() bool {
-	return p.Namespace != ""
+func (p ProductConfigProperty) Name() string {
+	return p.name
 }
 
-func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
-	if p.Namespace == "" {
-		return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis)
+func (p ProductConfigProperty) AlwaysEmit() bool {
+	return false
+}
+
+func (p ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
+	return bazel.ProductVariableConfigurationAxis(p.arch != "", p.name+"__"+p.arch)
+}
+
+func (p ProductConfigProperty) SelectKey() string {
+	if p.arch == "" {
+		return strings.ToLower(p.name)
 	} else {
-		// Soong config variables can be uniquely identified by the namespace
-		// (e.g. acme, android) and the product variable name (e.g. board, size)
-		return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis)
+		return strings.ToLower(p.name + "-" + p.arch)
 	}
 }
 
+// SoongConfigProperty represents a soong config variable, its value if it's a string variable,
+// and if it's dependent on the OS or not
+type SoongConfigProperty struct {
+	name      string
+	namespace string
+	// Can be an empty string for bool/value soong config variables
+	value string
+	// If there is a target: field inside a soong config property struct, the os that it selects
+	// on will be represented here.
+	os string
+}
+
+func (p SoongConfigProperty) Name() string {
+	return p.name
+}
+
+func (p SoongConfigProperty) AlwaysEmit() bool {
+	return true
+}
+
+func (p SoongConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
+	return bazel.ProductVariableConfigurationAxis(false, p.namespace+"__"+p.name+"__"+p.os)
+}
+
 // SelectKey returns the literal string that represents this variable in a BUILD
 // select statement.
-func (p *ProductConfigProperty) SelectKey() string {
-	if p.Namespace == "" {
-		return strings.ToLower(p.FullConfig)
-	}
-
-	if p.FullConfig == bazel.ConditionsDefaultConfigKey {
+func (p SoongConfigProperty) SelectKey() string {
+	// p.value being conditions_default can happen with or without a desired os. When not using
+	// an os, we want to emit literally just //conditions:default in the select statement, but
+	// when using an os, we want to emit namespace__name__conditions_default__os, so that
+	// the branch is only taken if the variable is not set, and we're on the desired os.
+	// ConfigurationAxis#SelectKey will map the conditions_default result of this function to
+	// //conditions:default.
+	if p.value == bazel.ConditionsDefaultConfigKey && p.os == "" {
 		return bazel.ConditionsDefaultConfigKey
 	}
 
-	value := p.FullConfig
-	if value == p.Name {
-		value = ""
+	parts := []string{p.namespace, p.name}
+	if p.value != "" && p.value != bazel.ConditionsDefaultSelectKey {
+		parts = append(parts, p.value)
+	}
+	if p.os != "" {
+		parts = append(parts, p.os)
 	}
 
-	// e.g. acme__feature1, android__board__soc_a
-	selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__"))
-	if value != "" {
-		selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
-	}
-
-	return selectKey
+	// e.g. acme__feature1, android__board__soc_a, my_namespace__my_variables__my_value__my_os
+	return strings.ToLower(strings.Join(parts, "__"))
 }
 
 // ProductConfigProperties is a map of maps to group property values according
@@ -670,7 +652,7 @@
 //
 // The value of the map is the interface{} representing the value of the
 // property, like ["-DDEFINES"] for cflags.
-type ProductConfigProperties map[string]map[ProductConfigProperty]interface{}
+type ProductConfigProperties map[string]map[ProductConfigOrSoongConfigProperty]interface{}
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the given module.
@@ -681,26 +663,10 @@
 
 	if moduleBase.variableProperties != nil {
 		productVariablesProperty := proptools.FieldNameForProperty("product_variables")
-		productVariableValues(
-			productVariablesProperty,
-			moduleBase.variableProperties,
-			"",
-			"",
-			&productConfigProperties,
-			bazel.ConfigurationAxis{},
-		)
-
-		for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for /* axis */ _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
 			for config, props := range configToProps {
-				// GetArchVariantProperties is creating an instance of the requested type
-				// and productVariablesValues expects an interface, so no need to cast
-				productVariableValues(
-					productVariablesProperty,
-					props,
-					"",
-					config,
-					&productConfigProperties,
-					axis)
+				variableValues := reflect.ValueOf(props).Elem().FieldByName(productVariablesProperty)
+				productConfigProperties.AddProductConfigProperties(variableValues, config)
 			}
 		}
 	}
@@ -708,13 +674,8 @@
 	if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
 		for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
 			for _, namespacedVariableProp := range namespacedVariableProps {
-				productVariableValues(
-					soongconfig.SoongConfigProperty,
-					namespacedVariableProp,
-					namespace,
-					"",
-					&productConfigProperties,
-					bazel.NoConfigAxis)
+				variableValues := reflect.ValueOf(namespacedVariableProp).Elem().FieldByName(soongconfig.SoongConfigProperty)
+				productConfigProperties.AddSoongConfigProperties(namespace, variableValues)
 			}
 		}
 	}
@@ -723,30 +684,49 @@
 }
 
 func (p *ProductConfigProperties) AddProductConfigProperty(
-	propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
-	if (*p)[propertyName] == nil {
-		(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
-	}
+	propertyName, productVariableName, arch string, propertyValue interface{}) {
 
 	productConfigProp := ProductConfigProperty{
-		Namespace:  namespace,           // e.g. acme, android
-		Name:       productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
-		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
-		OuterAxis:  outerAxis,
+		name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
+		arch: arch,                // e.g. "", x86, arm64
 	}
 
-	if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
+	p.AddEitherProperty(propertyName, productConfigProp, propertyValue)
+}
+
+func (p *ProductConfigProperties) AddSoongConfigProperty(
+	propertyName, namespace, variableName, value, os string, propertyValue interface{}) {
+
+	soongConfigProp := SoongConfigProperty{
+		namespace: namespace,
+		name:      variableName, // e.g. size, feature1, feature2, FEATURE3, board
+		value:     value,
+		os:        os, // e.g. android, linux_x86
+	}
+
+	p.AddEitherProperty(propertyName, soongConfigProp, propertyValue)
+}
+
+func (p *ProductConfigProperties) AddEitherProperty(
+	propertyName string, key ProductConfigOrSoongConfigProperty, propertyValue interface{}) {
+	if (*p)[propertyName] == nil {
+		(*p)[propertyName] = make(map[ProductConfigOrSoongConfigProperty]interface{})
+	}
+
+	if existing, ok := (*p)[propertyName][key]; ok {
 		switch dst := existing.(type) {
 		case []string:
-			if src, ok := property.([]string); ok {
-				dst = append(dst, src...)
-				(*p)[propertyName][productConfigProp] = dst
+			src, ok := propertyValue.([]string)
+			if !ok {
+				panic("Conflicting types")
 			}
+			dst = append(dst, src...)
+			(*p)[propertyName][key] = dst
 		default:
-			panic(fmt.Errorf("TODO: handle merging value %s", existing))
+			panic(fmt.Errorf("TODO: handle merging value %#v", existing))
 		}
 	} else {
-		(*p)[propertyName][productConfigProp] = property
+		(*p)[propertyName][key] = propertyValue
 	}
 }
 
@@ -783,10 +763,7 @@
 	return v, true
 }
 
-func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) {
-	// variableValues can either be a product_variables or
-	// soong_config_variables struct.
-	//
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(variableValues reflect.Value, arch string) {
 	// Example of product_variables:
 	//
 	// product_variables: {
@@ -799,35 +776,7 @@
 	//         ],
 	//     },
 	// },
-	//
-	// Example of soong_config_variables:
-	//
-	// soong_config_variables: {
-	//      feature1: {
-	//        	conditions_default: {
-	//               ...
-	//          },
-	//          cflags: ...
-	//      },
-	//      feature2: {
-	//          cflags: ...
-	//        	conditions_default: {
-	//               ...
-	//          },
-	//      },
-	//      board: {
-	//         soc_a: {
-	//             ...
-	//         },
-	//         soc_a: {
-	//             ...
-	//         },
-	//         soc_c: {},
-	//         conditions_default: {
-	//              ...
-	//         },
-	//      },
-	// }
+
 	for i := 0; i < variableValues.NumField(); i++ {
 		// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
 		productVariableName := variableValues.Type().Field(i).Name
@@ -839,25 +788,78 @@
 			continue
 		}
 
-		// Unlike product variables, config variables require a few more
-		// indirections to extract the struct from the reflect.Value.
-		if v, ok := maybeExtractConfigVarProp(variableValue); ok {
-			variableValue = v
-		}
-
 		for j := 0; j < variableValue.NumField(); j++ {
 			property := variableValue.Field(j)
 			// e.g. Asflags, Cflags, Enabled, etc.
 			propertyName := variableValue.Type().Field(j).Name
-			// config can also be "conditions_default".
-			config := proptools.PropertyNameForField(propertyName)
+			if property.Kind() != reflect.Interface {
+				productConfigProperties.AddProductConfigProperty(propertyName, productVariableName, arch, property.Interface())
+			}
+		}
+	}
+
+}
+
+func (productConfigProperties *ProductConfigProperties) AddSoongConfigProperties(namespace string, soongConfigVariablesStruct reflect.Value) {
+	//
+	// Example of soong_config_variables:
+	//
+	// soong_config_variables: {
+	//      feature1: {
+	//          conditions_default: {
+	//               ...
+	//          },
+	//          cflags: ...
+	//      },
+	//      feature2: {
+	//          cflags: ...
+	//          conditions_default: {
+	//               ...
+	//          },
+	//      },
+	//      board: {
+	//         soc_a: {
+	//             ...
+	//         },
+	//         soc_b: {
+	//             ...
+	//         },
+	//         soc_c: {},
+	//         conditions_default: {
+	//              ...
+	//         },
+	//      },
+	// }
+	for i := 0; i < soongConfigVariablesStruct.NumField(); i++ {
+		// e.g. feature1, feature2, board
+		variableName := soongConfigVariablesStruct.Type().Field(i).Name
+		variableStruct := soongConfigVariablesStruct.Field(i)
+		// Check if any properties were set for the module
+		if variableStruct.IsZero() {
+			// e.g. feature1: {}
+			continue
+		}
+
+		// Unlike product variables, config variables require a few more
+		// indirections to extract the struct from the reflect.Value.
+		if v, ok := maybeExtractConfigVarProp(variableStruct); ok {
+			variableStruct = v
+		}
+
+		for j := 0; j < variableStruct.NumField(); j++ {
+			propertyOrStruct := variableStruct.Field(j)
+			// propertyOrValueName can either be:
+			//  - A property, like: Asflags, Cflags, Enabled, etc.
+			//  - A soong config string variable's value, like soc_a, soc_b, soc_c in the example above
+			//  - "conditions_default"
+			propertyOrValueName := variableStruct.Type().Field(j).Name
 
 			// If the property wasn't set, no need to pass it along
-			if property.IsZero() {
+			if propertyOrStruct.IsZero() {
 				continue
 			}
 
-			if v, ok := maybeExtractConfigVarProp(property); ok {
+			if v, ok := maybeExtractConfigVarProp(propertyOrStruct); ok {
 				// The field is a struct, which is used by:
 				// 1) soong_config_string_variables
 				//
@@ -875,6 +877,9 @@
 				//     cflags: ...,
 				//     static_libs: ...
 				// }
+				//
+				// This means that propertyOrValueName is either conditions_default, or a soong
+				// config string variable's value.
 				field := v
 				// Iterate over fields of this struct prop.
 				for k := 0; k < field.NumField(); k++ {
@@ -884,47 +889,59 @@
 					if field.Field(k).IsZero() && namespace == "" {
 						continue
 					}
-					actualPropertyName := field.Type().Field(k).Name
 
-					productConfigProperties.AddProductConfigProperty(
-						actualPropertyName,  // e.g. cflags, static_libs
-						namespace,           // e.g. acme, android
-						productVariableName, // e.g. size, feature1, FEATURE2, board
-						config,
-						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"],
-						outerAxis,
-					)
+					propertyName := field.Type().Field(k).Name
+					if propertyName == "Target" {
+						productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), field.Field(k))
+					} else if propertyName == "Arch" || propertyName == "Multilib" {
+						panic("Arch/Multilib are not currently supported in soong config variable structs")
+					} else {
+						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), "", field.Field(k).Interface())
+					}
 				}
-			} else if property.Kind() != reflect.Interface {
+			} else if propertyOrStruct.Kind() != reflect.Interface {
 				// If not an interface, then this is not a conditions_default or
-				// a struct prop. That is, this is a regular product variable,
-				// or a bool/value config variable.
-				config := productVariableName + suffix
-				productConfigProperties.AddProductConfigProperty(
-					propertyName,
-					namespace,
-					productVariableName,
-					config,
-					property.Interface(),
-					outerAxis,
-				)
+				// a struct prop. That is, this is a bool/value config variable.
+				if propertyOrValueName == "Target" {
+					productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, "", propertyOrStruct)
+				} else if propertyOrValueName == "Arch" || propertyOrValueName == "Multilib" {
+					panic("Arch/Multilib are not currently supported in soong config variable structs")
+				} else {
+					productConfigProperties.AddSoongConfigProperty(propertyOrValueName, namespace, variableName, "", "", propertyOrStruct.Interface())
+				}
 			}
 		}
 	}
 }
 
-// productVariableValues uses reflection to convert a property struct for
-// product_variables and soong_config_variables to structs that can be generated
-// as select statements.
-func productVariableValues(
-	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) {
-	if suffix != "" {
-		suffix = "-" + suffix
-	}
+func (productConfigProperties *ProductConfigProperties) AddSoongConfigPropertiesFromTargetStruct(namespace, soongConfigVariableName string, soongConfigVariableValue string, targetStruct reflect.Value) {
+	// targetStruct will be a struct with fields like "android", "host", "arm", "x86",
+	// "android_arm", etc. The values of each of those fields will be a regular property struct.
+	for i := 0; i < targetStruct.NumField(); i++ {
+		targetFieldName := targetStruct.Type().Field(i).Name
+		archOrOsSpecificStruct := targetStruct.Field(i)
+		for j := 0; j < archOrOsSpecificStruct.NumField(); j++ {
+			property := archOrOsSpecificStruct.Field(j)
+			// e.g. Asflags, Cflags, Enabled, etc.
+			propertyName := archOrOsSpecificStruct.Type().Field(j).Name
 
-	// variableValues represent the product_variables or soong_config_variables struct.
-	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
-	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis)
+			if targetFieldName == "Android" {
+				productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, "android", property.Interface())
+			} else if targetFieldName == "Host" {
+				for _, os := range osTypeList {
+					if os.Class == Host {
+						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
+					}
+				}
+			} else {
+				// One problem with supporting additional fields is that if multiple branches of
+				// "target" overlap, we don't want them to be in the same select statement (aka
+				// configuration axis). "android" and "host" are disjoint, so it's ok that we only
+				// have 2 axes right now. (soongConfigVariables and soongConfigVariablesPlusOs)
+				panic("TODO: support other target types in soong config variable structs: " + targetFieldName)
+			}
+		}
+	}
 }
 
 func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/apex/apex.go b/apex/apex.go
index e01406b..33ed111 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1828,6 +1828,7 @@
 	Certificate() java.Certificate
 	BaseModuleName() string
 	LintDepSets() java.LintDepSets
+	PrivAppAllowlist() android.OptionalPath
 }
 
 var _ androidApp = (*java.AndroidApp)(nil)
@@ -1848,7 +1849,7 @@
 	return buildId
 }
 
-func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile {
+func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []apexFile {
 	appDir := "app"
 	if aapp.Privileged() {
 		appDir = "priv-app"
@@ -1870,7 +1871,15 @@
 	}); ok {
 		af.overriddenPackageName = app.OverriddenManifestPackageName()
 	}
-	return af
+	apexFiles := []apexFile{af}
+
+	if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() {
+		dirInApex := filepath.Join("etc", "permissions")
+		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"privapp", dirInApex, etc, aapp)
+		apexFiles = append(apexFiles, privAppAllowlist)
+	}
+
+	return apexFiles
 }
 
 func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
@@ -1964,9 +1973,6 @@
 // overridden by different override_apex modules (e.g. Google or Go variants),
 // which is handled by the overrides mutators.
 func (a *apexBundle) GetBazelLabel(ctx android.BazelConversionPathContext, module blueprint.Module) string {
-	if _, ok := ctx.Module().(android.OverridableModule); ok {
-		return android.MaybeBp2buildLabelOfOverridingModule(ctx)
-	}
 	return a.BazelModuleBase.GetBazelLabel(ctx, a)
 }
 
@@ -2318,12 +2324,12 @@
 		case androidAppTag:
 			switch ap := child.(type) {
 			case *java.AndroidApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 				return true // track transitive dependencies
 			case *java.AndroidAppImport:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 			case *java.AndroidTestHelperApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 			case *java.AndroidAppSet:
 				appDir := "app"
 				if ap.Privileged() {
@@ -2675,7 +2681,7 @@
 	}
 
 	pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex()
-	if pathInApex != "" && !java.SkipDexpreoptBootJars(ctx) {
+	if pathInApex != "" {
 		pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
 		tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
 
@@ -3426,15 +3432,6 @@
 	// Module separator
 	//
 	m[android.AvailableToAnyApex] = []string{
-		// TODO(b/156996905) Set apex_available/min_sdk_version for androidx/extras support libraries
-		"androidx",
-		"androidx-constraintlayout_constraintlayout",
-		"androidx-constraintlayout_constraintlayout-nodeps",
-		"androidx-constraintlayout_constraintlayout-solver",
-		"androidx-constraintlayout_constraintlayout-solver-nodeps",
-		"com.google.android.material_material",
-		"com.google.android.material_material-nodeps",
-
 		"libclang_rt",
 		"libprofile-clang-extras",
 		"libprofile-clang-extras_ndk",
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 3ba4d8d..bf9c71b 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -33,6 +33,7 @@
 	"android/soong/cc"
 	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
+	"android/soong/filesystem"
 	"android/soong/java"
 	"android/soong/rust"
 	"android/soong/sh"
@@ -4186,6 +4187,174 @@
 	})
 }
 
+func TestVendorApexWithVndkPrebuilts(t *testing.T) {
+	ctx := testApex(t, "",
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.DeviceVndkVersion = proptools.StringPtr("27")
+		}),
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			cc.RegisterVendorSnapshotModules(ctx)
+		}),
+		withFiles(map[string][]byte{
+			"vendor/foo/Android.bp": []byte(`
+				apex {
+					name: "myapex",
+					binaries: ["foo"],
+					key: "myapex.key",
+					min_sdk_version: "27",
+					vendor: true,
+				}
+
+				cc_binary {
+					name: "foo",
+					vendor: true,
+					srcs: ["abc.cpp"],
+					shared_libs: [
+						"libllndk",
+						"libvndk",
+					],
+					nocrt: true,
+					system_shared_libs: [],
+					min_sdk_version: "27",
+				}
+
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+			`),
+			// Simulate VNDK prebuilts with vendor_snapshot
+			"prebuilts/vndk/Android.bp": []byte(`
+				vndk_prebuilt_shared {
+					name: "libllndk",
+					version: "27",
+					vendor_available: true,
+					product_available: true,
+					target_arch: "arm64",
+					arch: {
+						arm64: {
+							srcs: ["libllndk.so"],
+						},
+					},
+				}
+
+				vndk_prebuilt_shared {
+					name: "libvndk",
+					version: "27",
+					vendor_available: true,
+					product_available: true,
+					target_arch: "arm64",
+					arch: {
+						arm64: {
+							srcs: ["libvndk.so"],
+						},
+					},
+					vndk: {
+						enabled: true,
+					},
+					min_sdk_version: "27",
+				}
+
+				vndk_prebuilt_shared {
+					name: "libc++",
+					version: "27",
+					target_arch: "arm64",
+					vendor_available: true,
+					product_available: true,
+					vndk: {
+						enabled: true,
+						support_system_process: true,
+					},
+					arch: {
+						arm64: {
+							srcs: ["libc++.so"],
+						},
+					},
+					min_sdk_version: "apex_inherit",
+				}
+
+				vendor_snapshot {
+					name: "vendor_snapshot",
+					version: "27",
+					arch: {
+						arm64: {
+							vndk_libs: [
+								"libc++",
+								"libllndk",
+								"libvndk",
+							],
+							static_libs: [
+								"libc++demangle",
+								"libclang_rt.builtins",
+								"libunwind",
+							],
+						},
+					}
+				}
+
+				vendor_snapshot_static {
+					name: "libclang_rt.builtins",
+					version: "27",
+					target_arch: "arm64",
+					vendor: true,
+					arch: {
+						arm64: {
+							src: "libclang_rt.builtins-aarch64-android.a",
+						},
+					},
+				}
+
+				vendor_snapshot_static {
+					name: "libc++demangle",
+					version: "27",
+					target_arch: "arm64",
+					compile_multilib: "64",
+					vendor: true,
+					arch: {
+						arm64: {
+							src: "libc++demangle.a",
+						},
+					},
+					min_sdk_version: "apex_inherit",
+				}
+
+				vendor_snapshot_static {
+					name: "libunwind",
+					version: "27",
+					target_arch: "arm64",
+					compile_multilib: "64",
+					vendor: true,
+					arch: {
+						arm64: {
+							src: "libunwind.a",
+						},
+					},
+					min_sdk_version: "apex_inherit",
+				}
+			`),
+		}))
+
+	// Should embed the prebuilt VNDK libraries in the apex
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"bin/foo",
+		"prebuilts/vndk/libc++.so:lib64/libc++.so",
+		"prebuilts/vndk/libvndk.so:lib64/libvndk.so",
+	})
+
+	// Should link foo with prebuilt libraries (shared/static)
+	ldRule := ctx.ModuleForTests("foo", "android_vendor.27_arm64_armv8-a_myapex").Rule("ld")
+	android.AssertStringDoesContain(t, "should link to prebuilt llndk", ldRule.Args["libFlags"], "prebuilts/vndk/libllndk.so")
+	android.AssertStringDoesContain(t, "should link to prebuilt vndk", ldRule.Args["libFlags"], "prebuilts/vndk/libvndk.so")
+	android.AssertStringDoesContain(t, "should link to prebuilt libc++demangle", ldRule.Args["libFlags"], "prebuilts/vndk/libc++demangle.a")
+	android.AssertStringDoesContain(t, "should link to prebuilt libunwind", ldRule.Args["libFlags"], "prebuilts/vndk/libunwind.a")
+
+	// Should declare the LLNDK library as a "required" external dependency
+	manifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	requireNativeLibs := names(manifestRule.Args["requireNativeLibs"])
+	ensureListContains(t, requireNativeLibs, "libllndk.so")
+}
+
 func TestDependenciesInApexManifest(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -6045,6 +6214,8 @@
 			sdk_version: "current",
 			system_modules: "none",
 			privileged: true,
+			privapp_allowlist: "perms.xml",
+			package_name: "com.android.AppFooPriv",
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
@@ -6074,6 +6245,7 @@
 
 	ensureContains(t, copyCmds, "image.apex/app/AppFoo@TEST.BUILD_ID/AppFoo.apk")
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk")
+	ensureContains(t, copyCmds, "image.apex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 
 	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs")
 	// JNI libraries are uncompressed
@@ -10375,3 +10547,54 @@
 		}
 	}
 }
+
+func TestFileSystemShouldSkipApexLibraries(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		cc.PrepareForIntegrationTestWithCc,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithMyapex,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+	)
+	result := context.RunTestWithBp(t, `
+		android_system_image {
+			name: "myfilesystem",
+			deps: [
+				"libfoo",
+			],
+			linker_config_src: "linker.config.json",
+		}
+
+		cc_library {
+			name: "libfoo",
+			shared_libs: [
+				"libbar",
+			],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			apex_available: ["myapex"],
+		}
+
+		apex {
+			name: "myapex",
+			native_shared_libs: ["libbar"],
+			key: "myapex.key",
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
+	android.AssertStringListDoesNotContain(t, "filesystem should not have libbar",
+		inputs.Strings(),
+		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index d784818..3e55ccc 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -497,6 +497,26 @@
 		})
 	})
 
+	t.Run("generate boot image profile even if dexpreopt is disabled", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+			dexpreopt.FixtureDisableDexpreoptBootImages(true),
+		).RunTest(t)
+
+		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/boot-image.prof",
+			"etc/classpaths/bootclasspath.pb",
+			"javalib/bar.jar",
+			"javalib/foo.jar",
+		})
+	})
+
 	t.Run("boot image disable generate profile", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			commonPreparer,
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 2a0f6e9..b1b6a75 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -15,7 +15,10 @@
 
 import (
 	"android/soong/android"
+	"android/soong/android/allowlists"
 	"android/soong/bazel/cquery"
+	"fmt"
+	"path/filepath"
 	"strings"
 	"testing"
 )
@@ -326,7 +329,7 @@
 }
 
 func TestOverrideApexImageInMixedBuilds(t *testing.T) {
-	bp := `
+	originalBp := `
 apex_key{
 	name: "foo_key",
 }
@@ -340,122 +343,203 @@
 	min_sdk_version: "31",
 	package_name: "pkg_name",
 	file_contexts: ":myapex-file_contexts",
-	bazel_module: { label: "//:foo" },
-}
+	%s
+}`
+	overrideBp := `
 override_apex {
 	name: "override_foo",
 	key: "override_foo_key",
 	package_name: "override_pkg_name",
 	base: "foo",
-	bazel_module: { label: "//:override_foo" },
+	%s
 }
 `
 
-	outputBaseDir := "out/bazel"
-	result := android.GroupFixturePreparers(
-		prepareForApexTest,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.BazelContext = android.MockBazelContext{
-				OutputBaseDir: outputBaseDir,
-				LabelToApexInfo: map[string]cquery.ApexInfo{
-					"//:foo": cquery.ApexInfo{
-						// ApexInfo Starlark provider
-						SignedOutput:          "signed_out.apex",
-						UnsignedOutput:        "unsigned_out.apex",
-						BundleKeyInfo:         []string{"public_key", "private_key"},
-						ContainerKeyInfo:      []string{"container_cert", "container_private"},
-						SymbolsUsedByApex:     "foo_using.txt",
-						JavaSymbolsUsedByApex: "foo_using.xml",
-						BundleFile:            "apex_bundle.zip",
-						InstalledFiles:        "installed-files.txt",
-						RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+	originalApexBpDir := "original"
+	originalApexName := "foo"
+	overrideApexBpDir := "override"
+	overrideApexName := "override_foo"
 
-						// unused
-						PackageName:  "pkg_name",
-						ProvidesLibs: []string{"a", "b"},
+	defaultApexLabel := fmt.Sprintf("//%s:%s", originalApexBpDir, originalApexName)
+	defaultOverrideApexLabel := fmt.Sprintf("//%s:%s", overrideApexBpDir, overrideApexName)
 
-						// ApexMkInfo Starlark provider
-						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
-					},
-					"//:override_foo": cquery.ApexInfo{
-						// ApexInfo Starlark provider
-						SignedOutput:          "override_signed_out.apex",
-						UnsignedOutput:        "override_unsigned_out.apex",
-						BundleKeyInfo:         []string{"override_public_key", "override_private_key"},
-						ContainerKeyInfo:      []string{"override_container_cert", "override_container_private"},
-						SymbolsUsedByApex:     "override_foo_using.txt",
-						JavaSymbolsUsedByApex: "override_foo_using.xml",
-						BundleFile:            "override_apex_bundle.zip",
-						InstalledFiles:        "override_installed-files.txt",
-						RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+	testCases := []struct {
+		desc                    string
+		bazelModuleProp         string
+		apexLabel               string
+		overrideBazelModuleProp string
+		overrideApexLabel       string
+		bp2buildConfiguration   android.Bp2BuildConversionAllowlist
+	}{
+		{
+			desc:                    "both explicit labels",
+			bazelModuleProp:         `bazel_module: { label: "//:foo" },`,
+			apexLabel:               "//:foo",
+			overrideBazelModuleProp: `bazel_module: { label: "//:override_foo" },`,
+			overrideApexLabel:       "//:override_foo",
+			bp2buildConfiguration:   android.NewBp2BuildAllowlist(),
+		},
+		{
+			desc:                    "both explicitly allowed",
+			bazelModuleProp:         `bazel_module: { bp2build_available: true },`,
+			apexLabel:               defaultApexLabel,
+			overrideBazelModuleProp: `bazel_module: { bp2build_available: true },`,
+			overrideApexLabel:       defaultOverrideApexLabel,
+			bp2buildConfiguration:   android.NewBp2BuildAllowlist(),
+		},
+		{
+			desc:              "original allowed by dir, override allowed by name",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetDefaultConfig(
+				map[string]allowlists.BazelConversionConfigEntry{
+					originalApexBpDir: allowlists.Bp2BuildDefaultTrue,
+				}).SetModuleAlwaysConvertList([]string{
+				overrideApexName,
+			}),
+		},
+		{
+			desc:              "both allowed by name",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetModuleAlwaysConvertList([]string{
+				originalApexName,
+				overrideApexName,
+			}),
+		},
+		{
+			desc:              "override allowed by name",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetModuleAlwaysConvertList([]string{
+				overrideApexName,
+			}),
+		},
+		{
+			desc:              "override allowed by dir",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetDefaultConfig(
+				map[string]allowlists.BazelConversionConfigEntry{
+					overrideApexBpDir: allowlists.Bp2BuildDefaultTrue,
+				}).SetModuleAlwaysConvertList([]string{}),
+		},
+	}
 
-						// unused
-						PackageName:  "override_pkg_name",
-						ProvidesLibs: []string{"a", "b"},
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			outputBaseDir := "out/bazel"
+			result := android.GroupFixturePreparers(
+				prepareForApexTest,
+				android.FixtureAddTextFile(filepath.Join(originalApexBpDir, "Android.bp"), fmt.Sprintf(originalBp, tc.bazelModuleProp)),
+				android.FixtureAddTextFile(filepath.Join(overrideApexBpDir, "Android.bp"), fmt.Sprintf(overrideBp, tc.overrideBazelModuleProp)),
+				android.FixtureModifyContext(func(ctx *android.TestContext) {
+					ctx.RegisterBp2BuildConfig(tc.bp2buildConfiguration)
+				}),
+				android.FixtureModifyConfig(func(config android.Config) {
+					config.BazelContext = android.MockBazelContext{
+						OutputBaseDir: outputBaseDir,
+						LabelToApexInfo: map[string]cquery.ApexInfo{
+							tc.apexLabel: cquery.ApexInfo{
+								// ApexInfo Starlark provider
+								SignedOutput:          "signed_out.apex",
+								UnsignedOutput:        "unsigned_out.apex",
+								BundleKeyInfo:         []string{"public_key", "private_key"},
+								ContainerKeyInfo:      []string{"container_cert", "container_private"},
+								SymbolsUsedByApex:     "foo_using.txt",
+								JavaSymbolsUsedByApex: "foo_using.xml",
+								BundleFile:            "apex_bundle.zip",
+								InstalledFiles:        "installed-files.txt",
+								RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
 
-						// ApexMkInfo Starlark provider
-						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
-					},
-				},
+								// unused
+								PackageName:  "pkg_name",
+								ProvidesLibs: []string{"a", "b"},
+
+								// ApexMkInfo Starlark provider
+								MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+							},
+							tc.overrideApexLabel: cquery.ApexInfo{
+								// ApexInfo Starlark provider
+								SignedOutput:          "override_signed_out.apex",
+								UnsignedOutput:        "override_unsigned_out.apex",
+								BundleKeyInfo:         []string{"override_public_key", "override_private_key"},
+								ContainerKeyInfo:      []string{"override_container_cert", "override_container_private"},
+								SymbolsUsedByApex:     "override_foo_using.txt",
+								JavaSymbolsUsedByApex: "override_foo_using.xml",
+								BundleFile:            "override_apex_bundle.zip",
+								InstalledFiles:        "override_installed-files.txt",
+								RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+
+								// unused
+								PackageName:  "override_pkg_name",
+								ProvidesLibs: []string{"a", "b"},
+
+								// ApexMkInfo Starlark provider
+								MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+							},
+						},
+					}
+				}),
+			).RunTest(t)
+
+			m := result.ModuleForTests("foo", "android_common_override_foo_foo_image").Module()
+			ab, ok := m.(*apexBundle)
+			if !ok {
+				t.Fatalf("Expected module to be an apexBundle, was not")
 			}
-		}),
-	).RunTestWithBp(t, bp)
 
-	m := result.ModuleForTests("foo", "android_common_override_foo_foo_image").Module()
-	ab, ok := m.(*apexBundle)
-	if !ok {
-		t.Fatalf("Expected module to be an apexBundle, was not")
-	}
+			if w, g := "out/bazel/execroot/__main__/override_public_key", ab.publicKeyFile.String(); w != g {
+				t.Errorf("Expected public key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_public_key", ab.publicKeyFile.String(); w != g {
-		t.Errorf("Expected public key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_private_key", ab.privateKeyFile.String(); w != g {
+				t.Errorf("Expected private key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_private_key", ab.privateKeyFile.String(); w != g {
-		t.Errorf("Expected private key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_container_cert", ab.containerCertificateFile; g != nil && w != g.String() {
+				t.Errorf("Expected public container key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_container_cert", ab.containerCertificateFile.String(); w != g {
-		t.Errorf("Expected public container key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_container_private", ab.containerPrivateKeyFile; g != nil && w != g.String() {
+				t.Errorf("Expected private container key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_container_private", ab.containerPrivateKeyFile.String(); w != g {
-		t.Errorf("Expected private container key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_signed_out.apex", ab.outputFile.String(); w != g {
+				t.Errorf("Expected output file %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_signed_out.apex", ab.outputFile.String(); w != g {
-		t.Errorf("Expected output file %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
+				t.Errorf("Expected output file %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
-		t.Errorf("Expected output file %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
+				t.Errorf("Expected output file %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
-		t.Errorf("Expected output file %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_installed-files.txt", ab.installedFilesFile.String(); w != g {
+				t.Errorf("Expected installed-files.txt %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_installed-files.txt", ab.installedFilesFile.String(); w != g {
-		t.Errorf("Expected installed-files.txt %q, got %q", w, g)
-	}
+			mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+			var builder strings.Builder
+			mkData.Custom(&builder, "override_foo", "BAZEL_TARGET_", "", mkData)
 
-	mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
-	var builder strings.Builder
-	mkData.Custom(&builder, "override_foo", "BAZEL_TARGET_", "", mkData)
+			data := builder.String()
+			if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/override_apex_bundle.zip"; !strings.Contains(data, w) {
+				t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+			}
+			if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
+				t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+			}
 
-	data := builder.String()
-	if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/override_apex_bundle.zip"; !strings.Contains(data, w) {
-		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
-	}
-	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
-		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
-	}
-
-	// make modules to be installed to system
-	if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
-		t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
-	}
-	if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
-		t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
+			// make modules to be installed to system
+			if len(ab.makeModulesToInstall) != 1 || ab.makeModulesToInstall[0] != "c" {
+				t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+			}
+			if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
+				t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
+			}
+		})
 	}
 }
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 29299e9..d042fe8 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -292,8 +292,7 @@
 	case osArch:
 		return platformOsArchMap[config]
 	case productVariables:
-		if strings.HasSuffix(config, ConditionsDefaultConfigKey) {
-			// e.g. "acme__feature1__conditions_default" or "android__board__conditions_default"
+		if config == ConditionsDefaultConfigKey {
 			return ConditionsDefaultSelectKey
 		}
 		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
@@ -325,11 +324,11 @@
 )
 
 // ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis {
+func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis {
 	return ConfigurationAxis{
 		configurationType: productVariables,
 		subType:           variable,
-		outerAxisType:     outerAxis.configurationType,
+		archVariant:       archVariant,
 	}
 }
 
@@ -340,8 +339,8 @@
 	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
 	// distinguish between them without needing to list all 17 product variables.
 	subType string
-	// used to keep track of which product variables are arch variant
-	outerAxisType configurationType
+
+	archVariant bool
 }
 
 func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
diff --git a/bazel/properties.go b/bazel/properties.go
index c3b68a4..e22f4db 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -334,7 +334,7 @@
 			if containsArch {
 				allProductVariablesAreArchVariant := true
 				for k := range la.ConfigurableValues {
-					if k.configurationType == productVariables && k.outerAxisType != arch {
+					if k.configurationType == productVariables && !k.archVariant {
 						allProductVariablesAreArchVariant = false
 					}
 				}
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index cf03eb5..c56d11f 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -248,13 +248,13 @@
 			OsArchConfigurationAxis: labelListSelectValues{
 				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{
+			ProductVariableConfigurationAxis(false, "product_with_defaults"): labelListSelectValues{
 				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
 				"b":                        makeLabelList([]string{"b_val"}, []string{}),
 				"c":                        makeLabelList([]string{"c_val"}, []string{}),
 				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
+			ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{
 				"a": makeLabelList([]string{}, []string{"product_config_exclude"}),
 			},
 		},
@@ -282,13 +282,13 @@
 			"linux_x86":                makeLabels("linux_x86_include"),
 			ConditionsDefaultConfigKey: nilLabels,
 		},
-		ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): {
+		ProductVariableConfigurationAxis(false, "product_with_defaults"): {
 			"a":                        nilLabels,
 			"b":                        makeLabels("b_val"),
 			"c":                        makeLabels("c_val"),
 			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
 		},
-		ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+		ProductVariableConfigurationAxis(false, "product_only_with_excludes"): {
 			"a":                        nilLabels,
 			ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
 		},
@@ -679,7 +679,7 @@
 			OsArchConfigurationAxis: stringListSelectValues{
 				"linux_x86": {"linux_x86_include"},
 			},
-			ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
+			ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
 				"a": []string{"not_in_value"},
 			},
 		},
@@ -704,7 +704,7 @@
 			"linux": []string{"linux_include"},
 		},
 		OsArchConfigurationAxis: stringListSelectValues{},
-		ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
+		ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
 			"a": []string{"not_in_value"},
 		},
 	}
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 928a1f2..7f7aa6a 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -80,6 +80,7 @@
         static_libs: ["static_lib_dep"],
         java_version: "7",
         certificate: "foocert",
+        required: ["static_lib_dep"],
 }
 `,
 		ExpectedBazelTargets: []string{
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index c821f59..e51f608 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -259,17 +259,20 @@
 	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
 		Description: "java_binary_host with srcs, libs, resources.",
 		Filesystem: map[string]string{
-			"test.mf":   "Main-Class: com.android.test.MainClass",
-			"res/a.res": "",
-			"res/b.res": "",
-		},
-		Blueprint: `java_binary_host {
+			"adir/test.mf":   "Main-Class: com.android.test.MainClass",
+			"adir/res/a.res": "",
+			"adir/res/b.res": "",
+			"adir/Android.bp": `java_binary_host {
     name: "java-binary-host",
     manifest: "test.mf",
     srcs: ["a.java", "b.kt"],
     java_resources: ["res/a.res", "res/b.res"],
+    bazel_module: { bp2build_available: true },
 }
 `,
+		},
+		Dir:       "adir",
+		Blueprint: "",
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
 				"srcs": `[
@@ -280,6 +283,7 @@
         "res/a.res",
         "res/b.res",
     ]`,
+				"resource_strip_prefix": `"adir"`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 24b763b..60766f4 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -283,21 +283,25 @@
 
 func TestJavaLibraryResources(t *testing.T) {
 	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Dir: "adir",
 		Filesystem: map[string]string{
-			"res/a.res":      "",
-			"res/b.res":      "",
-			"res/dir1/b.res": "",
-		},
-		Blueprint: `java_library {
+			"adir/res/a.res":      "",
+			"adir/res/b.res":      "",
+			"adir/res/dir1/b.res": "",
+			"adir/Android.bp": `java_library {
     name: "java-lib-1",
-	java_resources: ["res/a.res", "res/b.res"],
+    java_resources: ["res/a.res", "res/b.res"],
+    bazel_module: { bp2build_available: true },
 }`,
+		},
+		Blueprint: "",
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"resources": `[
         "res/a.res",
         "res/b.res",
     ]`,
+				"resource_strip_prefix": `"adir"`,
 			}),
 			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
 		},
diff --git a/bp2build/package_conversion_test.go b/bp2build/package_conversion_test.go
index 3704b2d..ce848e4 100644
--- a/bp2build/package_conversion_test.go
+++ b/bp2build/package_conversion_test.go
@@ -15,9 +15,10 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/genrule"
-	"testing"
 )
 
 func registerDependentModules(ctx android.RegistrationContext) {
@@ -29,6 +30,7 @@
 	tests := []struct {
 		description string
 		modules     string
+		fs          map[string]string
 		expected    []ExpectedRuleTarget
 	}{
 		{
@@ -50,8 +52,8 @@
 					"package",
 					"",
 					AttrNameToString{
-						"default_applicable_licenses": `[":my_license"]`,
-						"default_visibility":          `["//visibility:public"]`,
+						"default_package_metadata": `[":my_license"]`,
+						"default_visibility":       `["//visibility:public"]`,
 					},
 					android.HostAndDeviceDefault,
 				},
@@ -67,6 +69,57 @@
 				},
 			},
 		},
+		{
+			description: "package has METADATA file",
+			fs: map[string]string{
+				"METADATA": ``,
+			},
+			modules: `
+license {
+  name: "my_license",
+  visibility: [":__subpackages__"],
+  license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+  license_text: ["NOTICE"],
+}
+
+package {
+  default_applicable_licenses: ["my_license"],
+}
+`,
+			expected: []ExpectedRuleTarget{
+				{
+					"package",
+					"",
+					AttrNameToString{
+						"default_package_metadata": `[
+        ":my_license",
+        ":default_metadata_file",
+    ]`,
+						"default_visibility": `["//visibility:public"]`,
+					},
+					android.HostAndDeviceDefault,
+				},
+				{
+					"android_license",
+					"my_license",
+					AttrNameToString{
+						"license_kinds": `["SPDX-license-identifier-Apache-2.0"]`,
+						"license_text":  `"NOTICE"`,
+						"visibility":    `[":__subpackages__"]`,
+					},
+					android.HostAndDeviceDefault,
+				},
+				{
+					"filegroup",
+					"default_metadata_file",
+					AttrNameToString{
+						"applicable_licenses": `[]`,
+						"srcs":                `["METADATA"]`,
+					},
+					android.HostAndDeviceDefault,
+				},
+			},
+		},
 	}
 	for _, test := range tests {
 		expected := make([]string, 0, len(test.expected))
@@ -80,6 +133,7 @@
 				ModuleTypeUnderTestFactory: android.PackageFactory,
 				Blueprint:                  test.modules,
 				ExpectedBazelTargets:       expected,
+				Filesystem:                 test.fs,
 			})
 	}
 }
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index ba42f34..ad07f68 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -1251,3 +1251,111 @@
     srcs = ["main.cc"],
 )`}})
 }
+
+func TestSoongConfigModuleType_CombinedWithArchVariantProperties(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "my_bool_variable",
+}
+
+soong_config_string_variable {
+    name: "my_string_variable",
+    values: [
+        "value1",
+        "value2",
+    ],
+}
+
+soong_config_module_type {
+    name: "special_build_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "my_namespace",
+    bool_variables: ["my_bool_variable"],
+    variables: ["my_string_variable"],
+    properties: ["target.android.cflags", "cflags"],
+}
+
+special_build_cc_defaults {
+    name: "sample_cc_defaults",
+    target: {
+        android: {
+            cflags: ["-DFOO"],
+        },
+    },
+    soong_config_variables: {
+        my_bool_variable: {
+            target: {
+                android: {
+                    cflags: ["-DBAR"],
+                },
+            },
+            conditions_default: {
+                target: {
+                    android: {
+                        cflags: ["-DBAZ"],
+                    },
+                },
+            },
+        },
+        my_string_variable: {
+            value1: {
+                cflags: ["-DVALUE1_NOT_ANDROID"],
+                target: {
+                    android: {
+                        cflags: ["-DVALUE1"],
+                    },
+                },
+            },
+            value2: {
+                target: {
+                    android: {
+                        cflags: ["-DVALUE2"],
+                    },
+                },
+            },
+            conditions_default: {
+                target: {
+                    android: {
+                        cflags: ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
+                    },
+                },
+            },
+        },
+    },
+}
+
+cc_binary {
+    name: "my_binary",
+    srcs: ["main.cc"],
+    defaults: ["sample_cc_defaults"],
+}`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem:                 map[string]string{},
+		ExpectedBazelTargets: []string{`cc_binary(
+    name = "my_binary",
+    copts = select({
+        "//build/bazel/platforms/os:android": ["-DFOO"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:my_namespace__my_bool_variable__android": ["-DBAR"],
+        "//build/bazel/product_variables:my_namespace__my_bool_variable__conditions_default__android": ["-DBAZ"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:my_namespace__my_string_variable__value1": ["-DVALUE1_NOT_ANDROID"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:my_namespace__my_string_variable__conditions_default__android": ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
+        "//build/bazel/product_variables:my_namespace__my_string_variable__value1__android": ["-DVALUE1"],
+        "//build/bazel/product_variables:my_namespace__my_string_variable__value2__android": ["-DVALUE2"],
+        "//conditions:default": [],
+    }),
+    local_includes = ["."],
+    srcs = ["main.cc"],
+    target_compatible_with = ["//build/bazel/platforms/os:android"],
+)`}})
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 980dd07..ce35b5c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -124,17 +124,14 @@
 						}
 					}
 				}
-				if c.Properties.IsSdkVariant {
+				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
 					// Make the SDK variant uninstallable so that there are not two rules to install
 					// to the same location.
 					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-
-					if c.Properties.SdkAndPlatformVariantVisibleToMake {
-						// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
-						// dependencies to the .sdk suffix when building a module that uses the SDK.
-						entries.SetString("SOONG_SDK_VARIANT_MODULES",
-							"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
-					}
+					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
+					// dependencies to the .sdk suffix when building a module that uses the SDK.
+					entries.SetString("SOONG_SDK_VARIANT_MODULES",
+						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
 				}
 			},
 		},
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 33eafe3..cf5f74d 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -537,7 +537,7 @@
 				if !ok {
 					ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
 				}
-				newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name)
+				newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name())
 				attr.SetSelectValue(productConfigProp.ConfigurationAxis(), productConfigProp.SelectKey(), newFlags)
 			}
 		}
@@ -1422,7 +1422,7 @@
 		// Collect all the configurations that an include or exclude property exists for.
 		// We want to iterate all configurations rather than either the include or exclude because, for a
 		// particular configuration, we may have either only an include or an exclude to handle.
-		productConfigProps := make(map[android.ProductConfigProperty]bool, len(props)+len(excludeProps))
+		productConfigProps := make(map[android.ProductConfigOrSoongConfigProperty]bool, len(props)+len(excludeProps))
 		for p := range props {
 			productConfigProps[p] = true
 		}
diff --git a/cc/cc.go b/cc/cc.go
index 3fbefcd..7237686 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1086,7 +1086,7 @@
 	panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", c.BaseModuleName()))
 }
 
-func (c *Module) FuzzSharedLibraries() android.Paths {
+func (c *Module) FuzzSharedLibraries() android.RuleBuilderInstalls {
 	if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
 		return fuzzer.sharedLibraries
 	}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 7aa8b91..dfefc11 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -103,7 +103,7 @@
 	*baseCompiler
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
 	installedSharedDeps []string
-	sharedLibraries     android.Paths
+	sharedLibraries     android.RuleBuilderInstalls
 }
 
 func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -213,19 +213,19 @@
 }
 
 func SharedLibraryInstallLocation(
-	libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
+	libraryBase string, isHost bool, fuzzDir string, archString string) string {
 	installLocation := "$(PRODUCT_OUT)/data"
 	if isHost {
 		installLocation = "$(HOST_OUT)"
 	}
 	installLocation = filepath.Join(
-		installLocation, fuzzDir, archString, "lib", libraryPath.Base())
+		installLocation, fuzzDir, archString, "lib", libraryBase)
 	return installLocation
 }
 
 // Get the device-only shared library symbols install directory.
-func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
-	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
+func SharedLibrarySymbolsInstallLocation(libraryBase string, fuzzDir string, archString string) string {
+	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryBase)
 }
 
 func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
@@ -242,15 +242,16 @@
 	// Grab the list of required shared libraries.
 	fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
 
-	for _, lib := range fuzzBin.sharedLibraries {
+	for _, ruleBuilderInstall := range fuzzBin.sharedLibraries {
+		install := ruleBuilderInstall.To
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			SharedLibraryInstallLocation(
-				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
-				SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+				SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 }
@@ -422,7 +423,7 @@
 		files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
-		files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
+		files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, ccModule, "unstripped")})
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
@@ -453,19 +454,25 @@
 
 // GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
 // packaging.
-func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
+func GetSharedLibsToZip(sharedLibraries android.RuleBuilderInstalls, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
 	var files []fuzz.FileToZip
 
 	fuzzDir := "fuzz"
 
-	for _, library := range sharedLibraries {
-		files = append(files, fuzz.FileToZip{library, destinationPathPrefix})
+	for _, ruleBuilderInstall := range sharedLibraries {
+		library := ruleBuilderInstall.From
+		install := ruleBuilderInstall.To
+		files = append(files, fuzz.FileToZip{
+			SourceFilePath:        library,
+			DestinationPathPrefix: destinationPathPrefix,
+			DestinationPath:       install,
+		})
 
 		// For each architecture-specific shared library dependency, we need to
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
 		installDestination := SharedLibraryInstallLocation(
-			library, module.Host(), fuzzDir, archString)
+			install, module.Host(), fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
 		}
@@ -483,7 +490,7 @@
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
 		if !module.Host() {
-			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
+			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
@@ -497,12 +504,12 @@
 // VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
 // runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
 // have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.ModuleContext) (android.Paths, []android.Module) {
+func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.Module) {
 	seen := make(map[string]bool)
 	recursed := make(map[string]bool)
 	deps := []android.Module{}
 
-	var sharedLibraries android.Paths
+	var sharedLibraries android.RuleBuilderInstalls
 
 	// Enumerate the first level of dependencies, as we discard all non-library
 	// modules in the BFS loop below.
@@ -510,22 +517,36 @@
 		if !IsValidSharedDependency(dep) {
 			return
 		}
+		if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
+			return
+		}
 		if seen[ctx.OtherModuleName(dep)] {
 			return
 		}
 		seen[ctx.OtherModuleName(dep)] = true
 		deps = append(deps, dep)
-		sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped"))
+
+		sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
+		installDestination := sharedLibraryInfo.SharedLibrary.Base()
+		ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, dep, "unstripped"), installDestination}
+		sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
 	})
 
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		if !IsValidSharedDependency(child) {
 			return false
 		}
+		if !ctx.OtherModuleHasProvider(child, SharedLibraryInfoProvider) {
+			return false
+		}
 		if !seen[ctx.OtherModuleName(child)] {
 			seen[ctx.OtherModuleName(child)] = true
 			deps = append(deps, child)
-			sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped"))
+
+			sharedLibraryInfo := ctx.OtherModuleProvider(child, SharedLibraryInfoProvider).(SharedLibraryInfo)
+			installDestination := sharedLibraryInfo.SharedLibrary.Base()
+			ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, child, "unstripped"), installDestination}
+			sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
 		}
 
 		if recursed[ctx.OtherModuleName(child)] {
diff --git a/cc/linkable.go b/cc/linkable.go
index 9578807..557f5d2 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -130,7 +130,7 @@
 
 	// FuzzSharedLibraries returns the shared library dependencies for this module.
 	// Expects that IsFuzzModule returns true.
-	FuzzSharedLibraries() android.Paths
+	FuzzSharedLibraries() android.RuleBuilderInstalls
 
 	Device() bool
 	Host() bool
diff --git a/cc/sdk.go b/cc/sdk.go
index 4f361eb..6341926 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -47,16 +47,16 @@
 
 			// Mark the SDK variant.
 			modules[1].(*Module).Properties.IsSdkVariant = true
-			// SDK variant is not supposed to be installed
-			modules[1].(*Module).Properties.PreventInstall = true
 
 			if ctx.Config().UnbundledBuildApps() {
 				// For an unbundled apps build, hide the platform variant from Make.
 				modules[0].(*Module).Properties.HideFromMake = true
+				modules[0].(*Module).Properties.PreventInstall = true
 			} else {
 				// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 				// exposed to Make.
 				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+				modules[1].(*Module).Properties.PreventInstall = true
 			}
 			ctx.AliasVariation("")
 		} else if isCcModule && ccModule.isImportedApiLibrary() {
@@ -74,8 +74,8 @@
 					if apiLibrary.hasApexStubs() {
 						// For an unbundled apps build, hide the platform variant from Make.
 						modules[1].(*Module).Properties.HideFromMake = true
-						modules[1].(*Module).Properties.PreventInstall = true
 					}
+					modules[1].(*Module).Properties.PreventInstall = true
 				} else {
 					// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 					// exposed to Make.
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 790440c..61925e3 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -101,95 +101,3 @@
 	assertDep(t, libsdkNDK, libcxxNDK)
 	assertDep(t, libsdkPlatform, libcxxPlatform)
 }
-
-func TestMakeModuleNameForSdkVariant(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			srcs: ["main_test.cpp"],
-			sdk_version: "current",
-			stl: "none",
-		}
-	`
-	platformVariant := "android_arm64_armv8-a_shared"
-	sdkVariant := "android_arm64_armv8-a_sdk_shared"
-	testCases := []struct {
-		name              string
-		unbundledApps     []string
-		variant           string
-		skipInstall       bool // soong skips install
-		hideFromMake      bool // no make entry
-		makeUninstallable bool // make skips install
-		makeModuleName    string
-	}{
-		{
-			name:          "platform variant in normal builds",
-			unbundledApps: nil,
-			variant:       platformVariant,
-			// installable in soong
-			skipInstall: false,
-			// visiable in Make as "libfoo"
-			hideFromMake:   false,
-			makeModuleName: "libfoo",
-			// installable in Make
-			makeUninstallable: false,
-		},
-		{
-			name:          "sdk variant in normal builds",
-			unbundledApps: nil,
-			variant:       sdkVariant,
-			// soong doesn't install
-			skipInstall: true,
-			// visible in Make as "libfoo.sdk"
-			hideFromMake:   false,
-			makeModuleName: "libfoo.sdk",
-			// but not installed
-			makeUninstallable: true,
-		},
-		{
-			name:          "platform variant in unbunded builds",
-			unbundledApps: []string{"bar"},
-			variant:       platformVariant,
-			// installable in soong
-			skipInstall: false,
-			// hidden from make
-			hideFromMake: true,
-		},
-		{
-			name:          "sdk variant in unbunded builds",
-			unbundledApps: []string{"bar"},
-			variant:       sdkVariant,
-			// soong doesn't install
-			skipInstall: true,
-			// visible in Make as "libfoo"
-			hideFromMake:   false,
-			makeModuleName: "libfoo",
-			// but not installed
-			makeUninstallable: true,
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			fixture := android.GroupFixturePreparers(prepareForCcTest,
-				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					variables.Unbundled_build_apps = tc.unbundledApps
-				}),
-			)
-			ctx := fixture.RunTestWithBp(t, bp).TestContext
-			module := ctx.ModuleForTests("libfoo", tc.variant).Module().(*Module)
-			android.AssertBoolEquals(t, "IsSkipInstall", tc.skipInstall, module.IsSkipInstall())
-			android.AssertBoolEquals(t, "HideFromMake", tc.hideFromMake, module.HiddenFromMake())
-			if !tc.hideFromMake {
-				entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
-				android.AssertStringEquals(t, "LOCAL_MODULE",
-					tc.makeModuleName, entries.EntryMap["LOCAL_MODULE"][0])
-				actualUninstallable := false
-				if actual, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
-					actualUninstallable = "true" == actual[0]
-				}
-				android.AssertBoolEquals(t, "LOCAL_UNINSTALLABLE_MODULE",
-					tc.makeUninstallable, actualUninstallable)
-			}
-		})
-	}
-}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index e6e5660..d2531c0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -250,7 +250,11 @@
 		for _, path := range m.VintfFragments() {
 			prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
 		}
-		prop.MinSdkVersion = m.MinSdkVersion()
+		if m.IsPrebuilt() {
+			prop.MinSdkVersion = "apex_inherit"
+		} else {
+			prop.MinSdkVersion = m.MinSdkVersion()
+		}
 
 		// install config files. ignores any duplicates.
 		for _, path := range append(m.InitRc(), m.VintfFragments()...) {
@@ -324,13 +328,13 @@
 						}
 					}
 				}
-				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
+				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem)
 				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
 			} else {
 				stem = ctx.ModuleName(m)
 			}
 
-			propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
+			propOut = filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem+".json")
 		} else if m.Binary() {
 			// binary flags
 			prop.Symlinks = m.Symlinks()
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 619500e..c5431b3 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -23,6 +23,17 @@
 	"testing"
 )
 
+func checkJsonContents(t *testing.T, ctx android.TestingSingleton, jsonPath string, key string, value string) {
+	jsonOut := ctx.MaybeOutput(jsonPath)
+	if jsonOut.Rule == nil {
+		t.Errorf("%q expected but not found", jsonPath)
+		return
+	}
+	if !strings.Contains(jsonOut.Args["content"], fmt.Sprintf("%q:%q", key, value)) {
+		t.Errorf("%q must include %q:%q but it only has %v", jsonPath, key, value, jsonOut.Args["content"])
+	}
+}
+
 func TestVendorSnapshotCapture(t *testing.T) {
 	bp := `
 	cc_library {
@@ -52,6 +63,7 @@
 		name: "libvendor_available",
 		vendor_available: true,
 		nocrt: true,
+		min_sdk_version: "29",
 	}
 
 	cc_library_headers {
@@ -155,6 +167,9 @@
 			filepath.Join(staticDir, "libvendor_available.a.json"),
 			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
 
+		checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
+		checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
+
 		// For binary executables, all vendor:true and vendor_available modules are captured.
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
@@ -1657,3 +1672,69 @@
 		}
 	}
 }
+
+func TestSnapshotInRelativeInstallPath(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor_available_var",
+		vendor_available: true,
+		stem: "libvendor_available",
+		relative_install_path: "var",
+		nocrt: true,
+	}
+`
+
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var jsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only non-VNDK vendor_available modules are captured
+		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		sharedDirVar := filepath.Join(sharedDir, "var")
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available_var", "libvendor_available.so", sharedDirVar, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvendor_available.so.json"),
+			filepath.Join(sharedDirVar, "libvendor_available.so.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+
+	// fake snapshot should have all outputs in the normal snapshot.
+	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+	for _, output := range snapshotSingleton.AllOutputs() {
+		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+			t.Errorf("%q expected but not found", fakeOutput)
+		}
+	}
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index 30bfdd8..9b70004 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -241,7 +241,7 @@
 func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
 	return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
 		ctx.VisitAllModules(func(m android.Module) {
-			if c, ok := m.(*Module); ok && predicate(c) {
+			if c, ok := m.(*Module); ok && predicate(c) && !c.IsVndkPrebuiltLibrary() {
 				filename, err := getVndkFileName(c)
 				if err != nil {
 					ctx.ModuleErrorf(m, "%s", err)
@@ -402,6 +402,11 @@
 		m.VendorProperties.IsVNDKPrivate = Bool(prebuiltLib.Properties.Llndk.Private)
 	}
 
+	if m.IsVndkPrebuiltLibrary() && !m.IsVndk() {
+		m.VendorProperties.IsLLNDK = true
+		// TODO(b/280697209): copy "llndk.private" flag to vndk_prebuilt_shared
+	}
+
 	if (isLib && lib.buildShared()) || (isPrebuiltLib && prebuiltLib.buildShared()) {
 		if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
 			processVndkLibrary(mctx, m)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 53e0e55..5de2326 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -30,6 +30,7 @@
 	"android/soong/shared"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/metrics"
@@ -256,18 +257,47 @@
 }
 
 func writeNinjaHint(ctx *android.Context) error {
-	wantModules := make([]string, len(allowlists.HugeModulesMap))
-	i := 0
-	for k := range allowlists.HugeModulesMap {
-		wantModules[i] = k
-		i += 1
-	}
-	outputsMap := ctx.Context.GetOutputsFromModuleNames(wantModules)
-	var outputBuilder strings.Builder
-	for k, v := range allowlists.HugeModulesMap {
-		for _, output := range outputsMap[k] {
-			outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, v))
+	ctx.BeginEvent("ninja_hint")
+	defer ctx.EndEvent("ninja_hint")
+	// The current predictor focuses on reducing false negatives.
+	// If there are too many false positives (e.g., most modules are marked as positive),
+	// real long-running jobs cannot run early.
+	// Therefore, the model should be adjusted in this case.
+	// The model should also be adjusted if there are critical false negatives.
+	predicate := func(j *blueprint.JsonModule) (prioritized bool, weight int) {
+		prioritized = false
+		weight = 0
+		for prefix, w := range allowlists.HugeModuleTypePrefixMap {
+			if strings.HasPrefix(j.Type, prefix) {
+				prioritized = true
+				weight = w
+				return
+			}
 		}
+		dep_count := len(j.Deps)
+		src_count := 0
+		for _, a := range j.Module["Actions"].([]blueprint.JSONAction) {
+			src_count += len(a.Inputs)
+		}
+		input_size := dep_count + src_count
+
+		// Current threshold is an arbitrary value which only consider recall rather than accuracy.
+		if input_size > allowlists.INPUT_SIZE_THRESHOLD {
+			prioritized = true
+			weight += ((input_size) / allowlists.INPUT_SIZE_THRESHOLD) * allowlists.DEFAULT_PRIORITIZED_WEIGHT
+
+			// To prevent some modules from having too large a priority value.
+			if weight > allowlists.HIGH_PRIORITIZED_WEIGHT {
+				weight = allowlists.HIGH_PRIORITIZED_WEIGHT
+			}
+		}
+		return
+	}
+
+	outputsMap := ctx.Context.GetWeightedOutputsFromPredicate(predicate)
+	var outputBuilder strings.Builder
+	for output, weight := range outputsMap {
+		outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, weight))
 	}
 	weightListFile := filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_weight_list")
 
@@ -289,9 +319,9 @@
 }
 
 // Errors out if any modules expected to be mixed_built were not, unless
-// there is a platform incompatibility.
+// the modules did not exist.
 func checkForAllowlistIntegrityError(configuration android.Config, isStagingMode bool) error {
-	modules := findModulesNotMixedBuiltForAnyVariant(configuration, isStagingMode)
+	modules := findMisconfiguredModules(configuration, isStagingMode)
 	if len(modules) == 0 {
 		return nil
 	}
@@ -299,29 +329,50 @@
 	return fmt.Errorf("Error: expected the following modules to be mixed_built: %s", modules)
 }
 
+// Returns true if the given module has all of the following true:
+//  1. Is allowlisted to be built with Bazel.
+//  2. Has a variant which is *not* built with Bazel.
+//  3. Has no variant which is built with Bazel.
+//
+// This indicates the allowlisting of this variant had no effect.
+// TODO(b/280457637): Return true for nonexistent modules.
+func isAllowlistMisconfiguredForModule(module string, mixedBuildsEnabled map[string]struct{}, mixedBuildsDisabled map[string]struct{}) bool {
+	_, enabled := mixedBuildsEnabled[module]
+
+	if enabled {
+		return false
+	}
+
+	_, disabled := mixedBuildsDisabled[module]
+	return disabled
+
+}
+
 // Returns the list of modules that should have been mixed_built (per the
 // allowlists and cmdline flags) but were not.
-func findModulesNotMixedBuiltForAnyVariant(configuration android.Config, isStagingMode bool) []string {
+// Note: nonexistent modules are excluded from the list. See b/280457637
+func findMisconfiguredModules(configuration android.Config, isStagingMode bool) []string {
 	retval := []string{}
 	forceEnabledModules := configuration.BazelModulesForceEnabledByFlag()
 
 	mixedBuildsEnabled := configuration.GetMixedBuildsEnabledModules()
+	mixedBuildsDisabled := configuration.GetMixedBuildsDisabledModules()
 	for _, module := range allowlists.ProdMixedBuildsEnabledList {
-		if _, ok := mixedBuildsEnabled[module]; !ok && module != "" {
+		if isAllowlistMisconfiguredForModule(module, mixedBuildsEnabled, mixedBuildsDisabled) {
 			retval = append(retval, module)
 		}
 	}
 
 	if isStagingMode {
 		for _, module := range allowlists.StagingMixedBuildsEnabledList {
-			if _, ok := mixedBuildsEnabled[module]; !ok && module != "" {
+			if isAllowlistMisconfiguredForModule(module, mixedBuildsEnabled, mixedBuildsDisabled) {
 				retval = append(retval, module)
 			}
 		}
 	}
 
 	for module, _ := range forceEnabledModules {
-		if _, ok := mixedBuildsEnabled[module]; !ok && module != "" {
+		if isAllowlistMisconfiguredForModule(module, mixedBuildsEnabled, mixedBuildsDisabled) {
 			retval = append(retval, module)
 		}
 	}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 301246a..1c6aaad 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -94,12 +94,12 @@
 	}, {
 		flag:        "--upload-metrics-only",
 		description: "upload metrics without building anything",
-		config:      uploadOnlyConfig,
+		config:      build.UploadOnlyConfig,
 		stdio:       stdio,
 		// Upload-only mode mostly skips to the metrics-uploading phase of soong_ui.
 		// However, this invocation marks the true "end of the build", and thus we
 		// need to update the total runtime of the build to include this upload step.
-		run: updateTotalRealTime,
+		run: finalizeBazelMetrics,
 	},
 }
 
@@ -203,8 +203,6 @@
 	bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
 
-	//the profile file generated by Bazel"
-	bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
 	metricsFiles := []string{
 		buildErrorFile,           // build error strings
 		rbeMetricsFile,           // high level metrics related to remote build execution.
@@ -226,7 +224,7 @@
 		criticalPath.WriteToMetrics(met)
 		met.Dump(soongMetricsFile)
 		if !config.SkipMetricsUpload() {
-			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
+			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
 		}
 	}()
 	c.run(buildCtx, config, args)
@@ -451,14 +449,6 @@
 	return build.NewConfig(ctx)
 }
 
-// uploadOnlyConfig explicitly requires no arguments.
-func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
-	if len(args) > 0 {
-		fmt.Printf("--upload-only does not require arguments.")
-	}
-	return build.UploadOnlyConfig(ctx)
-}
-
 func buildActionConfig(ctx build.Context, args ...string) build.Config {
 	flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
 	flags.SetOutput(ctx.Writer)
@@ -700,6 +690,15 @@
 	}
 }
 
+func finalizeBazelMetrics(ctx build.Context, config build.Config, args []string) {
+	updateTotalRealTime(ctx, config, args)
+
+	logsDir := config.LogsDir()
+	logsPrefix := config.GetLogsPrefix()
+	bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
+	bazelProfileFile := filepath.Join(logsDir, logsPrefix+"analyzed_bazel_profile.txt")
+	build.ProcessBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx, config)
+}
 func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
 	soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
 
@@ -710,7 +709,7 @@
 	}
 	met := ctx.ContextImpl.Metrics
 
-	err = met.UpdateTotalRealTime(data)
+	err = met.UpdateTotalRealTimeAndNonZeroExit(data, config.BazelExitCode())
 	if err != nil {
 		ctx.Fatal(err)
 	}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 609a29c..0cc3bd6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -475,7 +475,16 @@
 	ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
 }
 
+func IsDex2oatNeeded(ctx android.PathContext) bool {
+	global := GetGlobalConfig(ctx)
+	return !global.DisablePreopt || !global.DisablePreoptBootImages
+}
+
 func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
+	if !IsDex2oatNeeded(ctx) {
+		return nil
+	}
+
 	dex2oatBin := dex2oatModuleName(ctx.Config())
 
 	// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index a590c72..2b38793 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -100,11 +100,19 @@
 	return rule, nil
 }
 
+// If dexpreopt is applicable to the module, returns whether dexpreopt is disabled. Otherwise, the
+// behavior is undefined.
+// When it returns true, dexpreopt artifacts will not be generated, but profile will still be
+// generated if profile-guided compilation is requested.
 func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
 	if ctx.Config().UnbundledBuild() {
 		return true
 	}
 
+	if global.DisablePreopt {
+		return true
+	}
+
 	if contains(global.DisablePreoptModules, module.Name) {
 		return true
 	}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 47ae494..6ed0736 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -174,3 +174,17 @@
 		dexpreoptConfig.DisableGenerateProfile = disable
 	})
 }
+
+// FixtureDisableDexpreoptBootImages sets the DisablePreoptBootImages property in the global config.
+func FixtureDisableDexpreoptBootImages(disable bool) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.DisablePreoptBootImages = disable
+	})
+}
+
+// FixtureDisableDexpreopt sets the DisablePreopt property in the global config.
+func FixtureDisableDexpreopt(disable bool) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.DisablePreopt = disable
+	})
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index f76529d..2a1b404 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -61,6 +61,7 @@
 type FileToZip struct {
 	SourceFilePath        android.Path
 	DestinationPathPrefix string
+	DestinationPath       string
 }
 
 type ArchOs struct {
@@ -443,7 +444,7 @@
 			FlagWithOutput("-o ", corpusZip)
 		rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
 		command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
-		files = append(files, FileToZip{corpusZip, ""})
+		files = append(files, FileToZip{SourceFilePath: corpusZip})
 	}
 
 	// Package the data into a zipfile.
@@ -456,17 +457,17 @@
 			command.FlagWithArg("-C ", intermediateDir)
 			command.FlagWithInput("-f ", f)
 		}
-		files = append(files, FileToZip{dataZip, ""})
+		files = append(files, FileToZip{SourceFilePath: dataZip})
 	}
 
 	// The dictionary.
 	if fuzzModule.Dictionary != nil {
-		files = append(files, FileToZip{fuzzModule.Dictionary, ""})
+		files = append(files, FileToZip{SourceFilePath: fuzzModule.Dictionary})
 	}
 
 	// Additional fuzz config.
 	if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
-		files = append(files, FileToZip{fuzzModule.Config, ""})
+		files = append(files, FileToZip{SourceFilePath: fuzzModule.Config})
 	}
 
 	return files
@@ -485,6 +486,9 @@
 		} else {
 			command.Flag("-P ''")
 		}
+		if file.DestinationPath != "" {
+			command.FlagWithArg("-e ", file.DestinationPath)
+		}
 		command.FlagWithInput("-f ", file.SourceFilePath)
 	}
 
@@ -502,7 +506,7 @@
 	}
 
 	s.FuzzTargets[module.Name()] = true
-	archDirs[archOs] = append(archDirs[archOs], FileToZip{fuzzZip, ""})
+	archDirs[archOs] = append(archDirs[archOs], FileToZip{SourceFilePath: fuzzZip})
 
 	return archDirs[archOs], true
 }
diff --git a/java/app.go b/java/app.go
index 7bb8cdb..4656cae 100755
--- a/java/app.go
+++ b/java/app.go
@@ -33,8 +33,17 @@
 
 func init() {
 	RegisterAppBuildComponents(android.InitRegistrationContext)
+	pctx.HostBinToolVariable("ModifyAllowlistCmd", "modify_permissions_allowlist")
 }
 
+var (
+	modifyAllowlist = pctx.AndroidStaticRule("modifyAllowlist",
+		blueprint.RuleParams{
+			Command:     "${ModifyAllowlistCmd} $in $packageName $out",
+			CommandDeps: []string{"${ModifyAllowlistCmd}"},
+		}, "packageName")
+)
+
 func RegisterAppBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app", AndroidAppFactory)
 	ctx.RegisterModuleType("android_test", AndroidTestFactory)
@@ -115,6 +124,9 @@
 	// Prefer using other specific properties if build behaviour must be changed; avoid using this
 	// flag for anything but neverallow rules (unless the behaviour change is invisible to owners).
 	Updatable *bool
+
+	// Specifies the file that contains the allowlist for this app.
+	Privapp_allowlist *string `android:"path"`
 }
 
 // android_app properties that can be overridden by override_android_app
@@ -179,6 +191,8 @@
 	android.ApexBundleDepsInfo
 
 	javaApiUsedByOutputFile android.ModuleOutPath
+
+	privAppAllowlist android.OptionalPath
 }
 
 func (a *AndroidApp) IsInstallable() bool {
@@ -205,6 +219,10 @@
 	return a.jniCoverageOutputs
 }
 
+func (a *AndroidApp) PrivAppAllowlist() android.OptionalPath {
+	return a.privAppAllowlist
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type Certificate struct {
@@ -269,6 +287,10 @@
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
+	if a.appProperties.Privapp_allowlist != nil && !Bool(a.appProperties.Privileged) {
+		ctx.PropertyErrorf("privapp_allowlist", "privileged must be set in order to use privapp_allowlist")
+	}
+
 	for _, cert := range a.appProperties.Additional_certificates {
 		cert = android.SrcIsModule(cert)
 		if cert != "" {
@@ -598,6 +620,27 @@
 	return a.installApkName
 }
 
+func (a *AndroidApp) createPrivappAllowlist(ctx android.ModuleContext) *android.OutputPath {
+	if a.appProperties.Privapp_allowlist == nil {
+		return nil
+	}
+	if a.overridableAppProperties.Package_name == nil {
+		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
+	}
+	packageName := *a.overridableAppProperties.Package_name
+	fileName := "privapp_allowlist_" + packageName + ".xml"
+	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   modifyAllowlist,
+		Input:  android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist),
+		Output: outPath,
+		Args: map[string]string{
+			"packageName": packageName,
+		},
+	})
+	return &outPath
+}
+
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
 	var apkDeps android.Paths
 
@@ -733,18 +776,27 @@
 	BuildBundleModule(ctx, bundleFile, a.exportPackage, jniJarFile, dexJarFile)
 	a.bundleFile = bundleFile
 
+	allowlist := a.createPrivappAllowlist(ctx)
+	if allowlist != nil {
+		a.privAppAllowlist = android.OptionalPathForPath(allowlist)
+	}
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 
 	// Install the app package.
-	if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() &&
-		!a.appProperties.PreventInstall {
-
+	shouldInstallAppPackage := (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && !a.appProperties.PreventInstall
+	if shouldInstallAppPackage {
 		var extraInstalledPaths android.Paths
 		for _, extra := range a.extraOutputFiles {
 			installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
 			extraInstalledPaths = append(extraInstalledPaths, installed)
 		}
 		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
+
+		if a.privAppAllowlist.Valid() {
+			installPath := android.PathForModuleInstall(ctx, "etc", "permissions")
+			ctx.InstallFile(installPath, a.privAppAllowlist.Path().Base(), a.privAppAllowlist.Path())
+		}
 	}
 
 	a.buildAppDependencyInfo(ctx)
@@ -1570,7 +1622,7 @@
 
 	ctx.CreateBazelTargetModule(
 		props,
-		android.CommonAttributes{Name: a.Name()},
+		android.CommonAttributes{Name: a.Name(), SkipData: proptools.BoolPtr(true)},
 		appAttrs,
 	)
 
diff --git a/java/app_import.go b/java/app_import.go
index bfd6767..9c01960 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -335,12 +335,11 @@
 
 	if proptools.Bool(a.properties.Preprocessed) {
 		output := srcApk
-		// TODO(b/185811447) Uncomment this after all existing failing apks set skip_preprocessed_apk_checks: true
-		//if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
-		//	writableOutput := android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
-		//	a.validatePreprocessedApk(ctx, srcApk, writableOutput)
-		//	output = writableOutput
-		//}
+		if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
+			writableOutput := android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
+			a.validatePreprocessedApk(ctx, srcApk, writableOutput)
+			output = writableOutput
+		}
 		a.outputFile = output
 		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
@@ -423,6 +422,10 @@
 	return a.provenanceMetaDataFile
 }
 
+func (a *AndroidAppImport) PrivAppAllowlist() android.OptionalPath {
+	return android.OptionalPath{}
+}
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
 var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 845a962..bb8fab9 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -657,29 +657,28 @@
 	}
 }
 
-// TODO(b/185811447) Uncomment this after all existing failing apks set skip_preprocessed_apk_checks: true
-//func TestAndroidAppImport_Preprocessed(t *testing.T) {
-//	ctx, _ := testJava(t, `
-//		android_app_import {
-//			name: "foo",
-//			apk: "prebuilts/apk/app.apk",
-//			presigned: true,
-//			preprocessed: true,
-//		}
-//		`)
-//
-//	apkName := "foo.apk"
-//	variant := ctx.ModuleForTests("foo", "android_common")
-//	outputBuildParams := variant.Output("validated-prebuilt/" + apkName).BuildParams
-//	if outputBuildParams.Rule.String() != android.Cp.String() {
-//		t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
-//	}
-//
-//	// Make sure compression and aligning were validated.
-//	if len(outputBuildParams.Validations) != 2 {
-//		t.Errorf("Expected compression/alignment validation rules, found %d validations", len(outputBuildParams.Validations))
-//	}
-//}
+func TestAndroidAppImport_Preprocessed(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			preprocessed: true,
+		}
+		`)
+
+	apkName := "foo.apk"
+	variant := ctx.ModuleForTests("foo", "android_common")
+	outputBuildParams := variant.Output("validated-prebuilt/" + apkName).BuildParams
+	if outputBuildParams.Rule.String() != android.Cp.String() {
+		t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+	}
+
+	// Make sure compression and aligning were validated.
+	if len(outputBuildParams.Validations) != 2 {
+		t.Errorf("Expected compression/alignment validation rules, found %d validations", len(outputBuildParams.Validations))
+	}
+}
 
 func TestAndroidTestImport_UncompressDex(t *testing.T) {
 	testCases := []struct {
diff --git a/java/app_test.go b/java/app_test.go
index 7e97b0f..daff94c 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -3539,3 +3539,51 @@
 		android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
 	}
 }
+
+func TestPrivappAllowlist(t *testing.T) {
+	testJavaError(t, "privileged must be set in order to use privapp_allowlist", `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "perms.xml",
+		}
+	`)
+
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+		t,
+		`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "perms.xml",
+			privileged: true,
+			package_name: "com.android.foo",
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.google.android.foo",
+		}
+		`,
+	)
+	app := result.ModuleForTests("foo", "android_common")
+	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+
+	// verify that privapp allowlist is created
+	app.Output("out/soong/.intermediates/foo/android_common/privapp_allowlist_com.android.foo.xml")
+	overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
+	expectedAllowlist := "perms.xml"
+	actualAllowlist := app.Rule("modifyAllowlist").Input.String()
+	if expectedAllowlist != actualAllowlist {
+		t.Errorf("expected allowlist to be %q; got %q", expectedAllowlist, actualAllowlist)
+	}
+	overrideActualAllowlist := overrideApp.Rule("modifyAllowlist").Input.String()
+	if expectedAllowlist != overrideActualAllowlist {
+		t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlist, overrideActualAllowlist)
+	}
+
+	// verify that permissions are copied to device
+	app.Output("out/soong/target/product/test_device/system/etc/permissions/privapp_allowlist_com.android.foo.xml")
+	overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/privapp_allowlist_com.google.android.foo.xml")
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f692563..6ccf5a3 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -506,7 +506,7 @@
 		}
 	}
 
-	if SkipDexpreoptBootJars(ctx) {
+	if !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 
@@ -901,10 +901,6 @@
 
 // produceBootImageFiles builds the boot image files from the source if it is required.
 func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
-	if SkipDexpreoptBootJars(ctx) {
-		return bootImageOutputs{}
-	}
-
 	// Only generate the boot image if the configuration does not skip it.
 	return b.generateBootImageBuildActions(ctx, imageConfig)
 }
@@ -929,6 +925,13 @@
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
 
+	// If dexpreopt of boot image jars should be skipped, generate only a profile.
+	if SkipDexpreoptBootJars(ctx) {
+		return bootImageOutputs{
+			profile: profile,
+		}
+	}
+
 	// Build boot image files for the host variants.
 	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
 
diff --git a/java/dex.go b/java/dex.go
index 4d6aa34..f7c1361 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -140,7 +140,7 @@
 			`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
-			`--pg-conf-output ${outConfig} ` +
+			`-printconfiguration ${outConfig} ` +
 			`-printusage ${outUsage} ` +
 			`--deps-file ${out}.d ` +
 			`$r8Flags && ` +
diff --git a/java/dex_test.go b/java/dex_test.go
index 97fc3d0..2ba3831 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -23,7 +23,7 @@
 )
 
 func TestR8(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
 			srcs: ["foo.java"],
@@ -191,7 +191,7 @@
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
-			fixturePreparer := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd
+			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			if tc.unbundled {
 				fixturePreparer = android.GroupFixturePreparers(
 					fixturePreparer,
@@ -258,7 +258,7 @@
 }
 
 func TestR8Flags(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
 			srcs: ["foo.java"],
@@ -287,7 +287,7 @@
 }
 
 func TestD8(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
 			srcs: ["foo.java"],
@@ -328,7 +328,7 @@
 }
 
 func TestProguardFlagsInheritance(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
 			static_libs: [
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0ffedf6..a96b312 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -180,6 +180,8 @@
 	return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
 }
 
+// Returns whether dexpreopt is applicable to the module.
+// When it returns true, neither profile nor dexpreopt artifacts will be generated.
 func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
 	if !ctx.Device() {
 		return true
@@ -205,14 +207,6 @@
 
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisablePreopt {
-		return true
-	}
-
-	if inList(moduleName(ctx), global.DisablePreoptModules) {
-		return true
-	}
-
 	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
 	if isApexVariant(ctx) {
 		// Don't preopt APEX variant module unless the module is an APEX system server jar.
@@ -232,7 +226,7 @@
 }
 
 func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
-	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 	dexpreopt.RegisterToolDeps(ctx)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f4c0935..8e79674 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -500,9 +500,6 @@
 
 // Generate build rules for boot images.
 func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	if SkipDexpreoptBootJars(ctx) {
-		return
-	}
 	if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
 		// No module has enabled dexpreopting, so we assume there will be no boot image to make.
 		return
@@ -1002,7 +999,7 @@
 // (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
 // correct output directories.
 func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
-	if d.dexpreoptConfigForMake != nil {
+	if d.dexpreoptConfigForMake != nil && !SkipDexpreoptBootJars(ctx) {
 		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
 		ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
 	}
@@ -1014,6 +1011,10 @@
 			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
 		}
 
+		if SkipDexpreoptBootJars(ctx) {
+			return
+		}
+
 		global := dexpreopt.GetGlobalConfig(ctx)
 		dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 3d2c5c3..f91ac5c 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -438,3 +438,28 @@
 
 	android.AssertIntEquals(t, "entries count", 0, len(entriesList))
 }
+
+func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithFakeApexMutator,
+		dexpreopt.FixtureDisableDexpreopt(true),
+	)
+
+	result := preparers.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			installable: true,
+			dex_preopt: {
+				profile: "art-profile",
+			},
+			srcs: ["a.java"],
+		}`)
+
+	ctx := result.TestContext
+	dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+
+	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"}
+
+	android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs())
+}
diff --git a/java/fuzz.go b/java/fuzz.go
index 1d6b913..9a0c908 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -250,11 +250,11 @@
 		files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// Add .jar
-		files = append(files, fuzz.FileToZip{javaFuzzModule.implementationJarFile, ""})
+		files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.implementationJarFile})
 
 		// Add jni .so files
 		for _, fPath := range javaFuzzModule.jniFilePaths {
-			files = append(files, fuzz.FileToZip{fPath, ""})
+			files = append(files, fuzz.FileToZip{SourceFilePath: fPath})
 		}
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
diff --git a/java/java.go b/java/java.go
index d3e74fd..06130cd 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2710,8 +2710,13 @@
 	var resources bazel.LabelList
 	var resourceStripPrefix *string
 
+	if m.properties.Java_resources != nil && len(m.properties.Java_resource_dirs) > 0 {
+		ctx.ModuleErrorf("bp2build doesn't support both java_resources and java_resource_dirs being set on the same module.")
+	}
+
 	if m.properties.Java_resources != nil {
 		resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources))
+		resourceStripPrefix = proptools.StringPtr(ctx.ModuleDir())
 	}
 
 	//TODO(b/179889880) handle case where glob includes files outside package
@@ -2975,7 +2980,7 @@
 func javaLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
 	return bazel.BazelTargetModuleProperties{
 		Rule_class:        "java_library",
-		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/java:library.bzl",
 	}
 }
 
@@ -3084,7 +3089,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "java_binary",
-		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+		Bzl_load_location: "@rules_java//java:defs.bzl",
 	}
 	binAttrs := &javaBinaryHostAttributes{
 		Runtime_deps: runtimeDeps,
@@ -3140,7 +3145,7 @@
 	}
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "java_import",
-		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/java:import.bzl",
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(i.Name())
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 0ea3609..d5779f7 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -103,7 +103,7 @@
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	b.hiddenAPIDepsMutator(ctx)
 
-	if SkipDexpreoptBootJars(ctx) {
+	if !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 
@@ -187,11 +187,6 @@
 	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
-	// Nothing to do if skipping the dexpreopt of boot image jars.
-	if SkipDexpreoptBootJars(ctx) {
-		return
-	}
-
 	b.generateBootImageBuildActions(ctx, platformModules, apexModules)
 }
 
@@ -429,6 +424,12 @@
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
 
+	// If dexpreopt of boot image jars should be skipped, generate only a profile.
+	global := dexpreopt.GetGlobalConfig(ctx)
+	if global.DisablePreoptBootImages {
+		return
+	}
+
 	// Build boot image files for the android variants.
 	androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
 
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 206d995..044802e 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -104,20 +104,51 @@
 	return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
 }
 
+func hasBazelPrebuilt(module string) bool {
+	return module == "android" || module == "core-for-system-modules"
+}
+
+func bazelPrebuiltApiModuleName(module, scope, version string) string {
+	bazelModule := module
+	switch module {
+	case "android":
+		bazelModule = "android_jar"
+	case "core-for-system-modules":
+		bazelModule = "core_jar"
+	}
+	bazelVersion := version
+	if version == "current" {
+		bazelVersion = strconv.Itoa(android.FutureApiLevelInt)
+	}
+	bazelScope := scope
+	switch scope {
+	case "module-lib":
+		bazelScope = "module"
+	case "system-server":
+		bazelScope = "system_server"
+	}
+	return fmt.Sprintf("//prebuilts/sdk:%s_%s_%s", bazelScope, bazelVersion, bazelModule)
+}
+
 func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
 	props := struct {
-		Name        *string
-		Jars        []string
-		Sdk_version *string
-		Installable *bool
-		Compile_dex *bool
-	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version))
-	props.Jars = append(props.Jars, path)
-	props.Sdk_version = proptools.StringPtr(sdkVersion)
-	props.Installable = proptools.BoolPtr(false)
-	props.Compile_dex = proptools.BoolPtr(compileDex)
-
+		Name         *string
+		Jars         []string
+		Sdk_version  *string
+		Installable  *bool
+		Compile_dex  *bool
+		Bazel_module android.BazelModuleProperties
+	}{
+		Name:        proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)),
+		Jars:        []string{path},
+		Sdk_version: proptools.StringPtr(sdkVersion),
+		Installable: proptools.BoolPtr(false),
+		Compile_dex: proptools.BoolPtr(compileDex),
+	}
+	if hasBazelPrebuilt(module) {
+		props.Bazel_module = android.BazelModuleProperties{
+			Label: proptools.StringPtr(bazelPrebuiltApiModuleName(module, scope, version))}
+	}
 	mctx.CreateModule(ImportFactory, &props)
 }
 
@@ -135,6 +166,19 @@
 	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
 }
 
+func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) {
+	genruleProps := struct {
+		Name *string
+		Srcs []string
+		Out  []string
+		Cmd  *string
+	}{}
+	genruleProps.Name = proptools.StringPtr(name)
+	genruleProps.Out = []string{name}
+	genruleProps.Cmd = proptools.StringPtr("echo " + version + " > $(out)")
+	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
+}
+
 func createEmptyFile(mctx android.LoadHookContext, name string) {
 	props := struct {
 		Name *string
@@ -233,9 +277,10 @@
 	type latestApiInfo struct {
 		module, scope, path string
 		version             int
+		isExtensionApiFile  bool
 	}
 
-	getLatest := func(files []string) map[string]latestApiInfo {
+	getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
 		m := make(map[string]latestApiInfo)
 		for _, f := range files {
 			module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
@@ -245,16 +290,16 @@
 			key := module + "." + scope
 			info, exists := m[key]
 			if !exists || version > info.version {
-				m[key] = latestApiInfo{module, scope, f, version}
+				m[key] = latestApiInfo{module, scope, f, version, isExtensionApiFile}
 			}
 		}
 		return m
 	}
 
-	latest := getLatest(apiLevelFiles)
+	latest := getLatest(apiLevelFiles, false)
 	if p.properties.Extensions_dir != nil {
 		extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
-		for k, v := range getLatest(extensionApiFiles) {
+		for k, v := range getLatest(extensionApiFiles, true) {
 			if _, exists := latest[k]; !exists {
 				mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
 			}
@@ -267,6 +312,12 @@
 	for _, k := range android.SortedKeys(latest) {
 		info := latest[k]
 		name := PrebuiltApiModuleName(info.module, info.scope, "latest")
+		latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version")
+		if info.isExtensionApiFile {
+			createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, strconv.Itoa(info.version))
+		} else {
+			createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, "-1")
+		}
 		createApiModule(mctx, name, info.path)
 	}
 
diff --git a/python/python.go b/python/python.go
index 1a12973..8fde638 100644
--- a/python/python.go
+++ b/python/python.go
@@ -265,7 +265,6 @@
 			}
 			if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
 				if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() &&
-					mctx.ModuleName() != "par_test" &&
 					mctx.ModuleName() != "py2-cmd" &&
 					mctx.ModuleName() != "py2-stdlib" {
 					mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration")
diff --git a/python/tests/Android.bp b/python/tests/Android.bp
index a656859..e5569ba 100644
--- a/python/tests/Android.bp
+++ b/python/tests/Android.bp
@@ -28,29 +28,6 @@
         unit_test: false,
     },
     version: {
-        py2: {
-            enabled: true,
-            embedded_launcher: true,
-        },
-        py3: {
-            enabled: false,
-            embedded_launcher: true,
-        },
-    },
-}
-
-python_test_host {
-    name: "par_test3",
-    main: "par_test.py",
-    srcs: [
-        "par_test.py",
-        "testpkg/par_test.py",
-    ],
-    // Is not implemented as a python unittest
-    test_options: {
-        unit_test: false,
-    },
-    version: {
         py3: {
             embedded_launcher: true,
         },
diff --git a/python/tests/py-cmd_test.py b/python/tests/py-cmd_test.py
index acda2d7..c7ba0ab 100644
--- a/python/tests/py-cmd_test.py
+++ b/python/tests/py-cmd_test.py
@@ -57,7 +57,7 @@
 
 if sys.version_info[0] == 2:
     assert_equal("len(sys.path)", len(sys.path), 4)
-    assert_equal("sys.path[0]", sys.path[0], os.path.dirname(__file__))
+    assert_equal("sys.path[0]", sys.path[0], os.path.abspath(os.path.dirname(__file__)))
     assert_equal("sys.path[1]", sys.path[1], "/extra")
     assert_equal("sys.path[2]", sys.path[2], os.path.join(sys.executable, "internal"))
     assert_equal("sys.path[3]", sys.path[3], os.path.join(sys.executable, "internal", "stdlib"))
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
index 35941dc..f4abae5 100755
--- a/python/tests/runtest.sh
+++ b/python/tests/runtest.sh
@@ -24,10 +24,9 @@
 fi
 
 if [[ ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ) ||
-      ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 ) ||
       ( ! -f $ANDROID_HOST_OUT/bin/py2-cmd ) ||
       ( ! -f $ANDROID_HOST_OUT/bin/py3-cmd )]]; then
-  echo "Run 'm par_test par_test3 py2-cmd py3-cmd' first"
+  echo "Run 'm par_test py2-cmd py3-cmd' first"
   exit 1
 fi
 
@@ -41,12 +40,6 @@
 
 ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test/par_test --arg1 arg2
 
-PYTHONHOME= PYTHONPATH= $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
-PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
-PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
-
-ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 --arg1 arg2
-
 cd $(dirname ${BASH_SOURCE[0]})
 
 PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index b513409..e12c527 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -33,11 +33,7 @@
     fileName = fileName[:-1]
 assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
 
-# Python3 is returning None here for me, and I haven't found any problems caused by this.
-if sys.version_info[0] == 2:
-  assert_equal("__package__", __package__, "testpkg")
-else:
-  assert_equal("__package__", __package__, None)
+assert_equal("__package__", __package__, "testpkg")
 
 assert_equal("__loader__.archive", __loader__.archive, archive)
 assert_equal("__loader__.prefix", __loader__.prefix, "testpkg/")
diff --git a/rust/config/global.go b/rust/config/global.go
index 2d1f0c1..748bb3d 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.68.0"
+	RustDefaultVersion = "1.69.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/fuzz.go b/rust/fuzz.go
index d7e7ddf..c2b9405 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -31,7 +31,7 @@
 	*binaryDecorator
 
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
-	sharedLibraries     android.Paths
+	sharedLibraries     android.RuleBuilderInstalls
 	installedSharedDeps []string
 }
 
@@ -119,15 +119,17 @@
 	// Grab the list of required shared libraries.
 	fuzz.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
 
-	for _, lib := range fuzz.sharedLibraries {
+	for _, ruleBuilderInstall := range fuzz.sharedLibraries {
+		install := ruleBuilderInstall.To
+
 		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
 			cc.SharedLibraryInstallLocation(
-				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
-				cc.SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+				cc.SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 }
diff --git a/rust/rust.go b/rust/rust.go
index 7b520cd..dc53cc0 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -649,7 +649,7 @@
 	panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", mod.BaseModuleName()))
 }
 
-func (mod *Module) FuzzSharedLibraries() android.Paths {
+func (mod *Module) FuzzSharedLibraries() android.RuleBuilderInstalls {
 	if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
 		return fuzzer.sharedLibraries
 	}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 9367ff0..97f6ab4 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -237,3 +237,20 @@
     name: "jars-to-module-info-java",
     src: "jars-to-module-info-java.sh",
 }
+
+python_binary_host {
+    name: "modify_permissions_allowlist",
+    main: "modify_permissions_allowlist.py",
+    srcs: [
+        "modify_permissions_allowlist.py",
+    ],
+}
+
+python_test_host {
+    name: "modify_permissions_allowlist_test",
+    main: "modify_permissions_allowlist_test.py",
+    srcs: [
+        "modify_permissions_allowlist_test.py",
+        "modify_permissions_allowlist.py",
+    ],
+}
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index 869fd3f..dad2b47 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -72,6 +72,7 @@
 javax\.xml\.validation
 javax\.xml\.xpath
 jdk\.internal
+jdk\.internal\.access
 jdk\.internal\.math
 jdk\.internal\.misc
 jdk\.internal\.ref
diff --git a/scripts/modify_permissions_allowlist.py b/scripts/modify_permissions_allowlist.py
new file mode 100755
index 0000000..38ec7ec
--- /dev/null
+++ b/scripts/modify_permissions_allowlist.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for modifying privileged permission allowlists."""
+
+from __future__ import print_function
+
+import argparse
+import sys
+from xml.dom import minidom
+
+
+class InvalidRootNodeException(Exception):
+  pass
+
+
+class InvalidNumberOfPrivappPermissionChildren(Exception):
+  pass
+
+
+def modify_allowlist(allowlist_dom, package_name):
+  if allowlist_dom.documentElement.tagName != 'permissions':
+    raise InvalidRootNodeException
+  nodes = allowlist_dom.getElementsByTagName('privapp-permissions')
+  if nodes.length != 1:
+    raise InvalidNumberOfPrivappPermissionChildren
+  privapp_permissions = nodes[0]
+  privapp_permissions.setAttribute('package', package_name)
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('input', help='input allowlist template file')
+  parser.add_argument(
+      'package_name', help='package name to use in the allowlist'
+  )
+  parser.add_argument('output', help='output allowlist file')
+
+  return parser.parse_args()
+
+
+def main():
+  try:
+    args = parse_args()
+    doc = minidom.parse(args.input)
+    modify_allowlist(doc, args.package_name)
+    with open(args.output, 'w') as output_file:
+      doc.writexml(output_file, encoding='utf-8')
+  except Exception as err:
+    print('error: ' + str(err), file=sys.stderr)
+    sys.exit(-1)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/modify_permissions_allowlist_test.py b/scripts/modify_permissions_allowlist_test.py
new file mode 100755
index 0000000..ee8b12c
--- /dev/null
+++ b/scripts/modify_permissions_allowlist_test.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for modify_permissions_allowlist.py."""
+
+from __future__ import print_function
+
+import unittest
+
+from xml.dom import minidom
+
+from modify_permissions_allowlist import InvalidRootNodeException, InvalidNumberOfPrivappPermissionChildren, modify_allowlist
+
+
+class ModifyPermissionsAllowlistTest(unittest.TestCase):
+
+  def test_invalid_root(self):
+    xml_data = '<foo></foo>'
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(InvalidRootNodeException, modify_allowlist, xml_dom, 'x')
+
+  def test_no_packages(self):
+    xml_data = '<permissions></permissions>'
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(
+        InvalidNumberOfPrivappPermissionChildren, modify_allowlist, xml_dom, 'x'
+    )
+
+  def test_multiple_packages(self):
+    xml_data = (
+        '<permissions>'
+        '  <privapp-permissions package="foo.bar"></privapp-permissions>'
+        '  <privapp-permissions package="bar.baz"></privapp-permissions>'
+        '</permissions>'
+    )
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(
+        InvalidNumberOfPrivappPermissionChildren, modify_allowlist, xml_dom, 'x'
+    )
+
+  def test_modify_package_name(self):
+    xml_data = (
+        '<permissions>'
+        '  <privapp-permissions package="foo.bar">'
+        '    <permission name="myperm1"/>'
+        '  </privapp-permissions>'
+        '</permissions>'
+    )
+    xml_dom = minidom.parseString(xml_data)
+    modify_allowlist(xml_dom, 'bar.baz')
+    expected_data = (
+        '<?xml version="1.0" ?>'
+        '<permissions>'
+        '  <privapp-permissions package="bar.baz">'
+        '    <permission name="myperm1"/>'
+        '  </privapp-permissions>'
+        '</permissions>'
+    )
+    self.assertEqual(expected_data, xml_dom.toxml())
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/scripts/rbc-run b/scripts/rbc-run
deleted file mode 100755
index 8d93f0e..0000000
--- a/scripts/rbc-run
+++ /dev/null
@@ -1,18 +0,0 @@
-#! /bin/bash
-# Convert and run one configuration
-# Args: a product/board makefile optionally followed by additional arguments
-#       that will be passed to rbcrun.
-[[ $# -gt 1 && -f "$1" && -f "$2" ]] || { echo "Usage: ${0##*/} product.mk input_variables.mk [Additional rbcrun arguments]" >&2; exit 1; }
-set -eu
-
-declare -r output_root="${OUT_DIR:-out}"
-declare -r runner="${output_root}/rbcrun"
-declare -r converter="${output_root}/mk2rbc"
-declare -r launcher="${output_root}/rbc/launcher.rbc"
-declare -r makefile_list="${output_root}/.module_paths/configuration.list"
-declare -r makefile="$1"
-declare -r input_variables="$2"
-shift 2
-"${converter}" -mode=write -r --outdir "${output_root}/rbc" --input_variables "${input_variables}" --launcher="${launcher}" --makefile_list="${makefile_list}" "${makefile}"
-"${runner}" RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ "${launcher}"
-
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 3a2ecc0..6159ea9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -19,12 +19,14 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 )
 
 var prepareForSdkTestWithJava = android.GroupFixturePreparers(
 	java.PrepareForTestWithJavaBuildComponents,
 	PrepareForTestWithSdkBuildComponents,
+	dexpreopt.PrepareForTestWithFakeDex2oatd,
 
 	// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
 	// consistent and all files referenced from the snapshot's Android.bp file have actually been
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index c921ca6..d2eede6 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -474,16 +474,18 @@
 	}}
 }
 
-func InitShBinaryModule(s *ShBinary) {
+func initShBinaryModule(s *ShBinary, useBazel bool) {
 	s.AddProperties(&s.properties)
-	android.InitBazelModule(s)
+	if useBazel {
+		android.InitBazelModule(s)
+	}
 }
 
 // sh_binary is for a shell script or batch file to be installed as an
 // executable binary to <partition>/bin.
 func ShBinaryFactory() android.Module {
 	module := &ShBinary{}
-	InitShBinaryModule(module)
+	initShBinaryModule(module, true)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
 	return module
 }
@@ -492,7 +494,7 @@
 // to $(HOST_OUT)/bin.
 func ShBinaryHostFactory() android.Module {
 	module := &ShBinary{}
-	InitShBinaryModule(module)
+	initShBinaryModule(module, true)
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 	return module
 }
@@ -500,7 +502,7 @@
 // sh_test defines a shell script based test module.
 func ShTestFactory() android.Module {
 	module := &ShTest{}
-	InitShBinaryModule(&module.ShBinary)
+	initShBinaryModule(&module.ShBinary, false)
 	module.AddProperties(&module.testProperties)
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
@@ -510,7 +512,7 @@
 // sh_test_host defines a shell script based test module that runs on a host.
 func ShTestHostFactory() android.Module {
 	module := &ShTest{}
-	InitShBinaryModule(&module.ShBinary)
+	initShBinaryModule(&module.ShBinary, false)
 	module.AddProperties(&module.testProperties)
 	// Default sh_test_host to unit_tests = true
 	if module.testProperties.Test_options.Unit_test == nil {
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index b79754c..959ae4c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -46,6 +46,7 @@
         "soong-ui-tracer",
     ],
     srcs: [
+        "bazel_metrics.go",
         "build.go",
         "cleanbuild.go",
         "config.go",
diff --git a/ui/build/bazel_metrics.go b/ui/build/bazel_metrics.go
new file mode 100644
index 0000000..c0690c1
--- /dev/null
+++ b/ui/build/bazel_metrics.go
@@ -0,0 +1,135 @@
+// Copyright 2023 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 build
+
+// This file contains functionality to parse bazel profile data into
+// a bazel_metrics proto, defined in build/soong/ui/metrics/bazel_metrics_proto
+// These metrics are later uploaded in upload.go
+
+import (
+	"bufio"
+	"os"
+	"strconv"
+	"strings"
+
+	"android/soong/shared"
+	"google.golang.org/protobuf/proto"
+
+	bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
+)
+
+func parseTimingToNanos(str string) int64 {
+	millisString := removeDecimalPoint(str)
+	timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+	return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+	percentageString := removeDecimalPoint(str)
+	//remove the % at the end of the string
+	percentage := strings.ReplaceAll(percentageString, "%", "")
+	percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+	return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+	// The format is always 0.425 or 10.425
+	return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+	words := strings.Fields(line)
+	timing := words[3]
+	return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+	words := strings.Fields(line)
+	getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+		// Sample lines include:
+		// Total launch phase time   0.011 s    2.59%
+		// Total target pattern evaluation phase time  0.011 s    2.59%
+		var beginning int
+		var end int
+		for ind, word := range words {
+			if word == "Total" {
+				beginning = ind + 1
+			} else if beginning > 0 && word == "phase" {
+				end = ind
+				break
+			}
+		}
+		phaseName := strings.Join(words[beginning:end], " ")
+
+		// end is now "phase" - advance by 2 for timing and 4 for percentage
+		percentageString := words[end+4]
+		timingString := words[end+2]
+		timing := parseTimingToNanos(timingString)
+		percentagePortion := parsePercentageToTenThousandths(percentageString)
+		return phaseName, timing, percentagePortion
+	}
+
+	phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+	phaseTiming := bazel_metrics_proto.PhaseTiming{}
+	phaseTiming.DurationNanos = &timing
+	phaseTiming.PortionOfBuildTime = &portion
+
+	phaseTiming.PhaseName = &phaseName
+	return phaseTiming
+}
+
+// This method takes a file created by bazel's --analyze-profile mode and
+// writes bazel metrics data to the provided filepath.
+func ProcessBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context, config Config) {
+	if bazelProfileFile == "" {
+		return
+	}
+
+	readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+		//serialize the proto, write it
+		bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+		file, err := os.ReadFile(filepath)
+		if err != nil {
+			ctx.Fatalln("Error reading metrics file\n", err)
+		}
+
+		scanner := bufio.NewScanner(strings.NewReader(string(file)))
+		scanner.Split(bufio.ScanLines)
+
+		var phaseTimings []*bazel_metrics_proto.PhaseTiming
+		for scanner.Scan() {
+			line := scanner.Text()
+			if strings.HasPrefix(line, "Total run time") {
+				total := parseTotal(line)
+				bazelMetrics.Total = &total
+			} else if strings.HasPrefix(line, "Total") {
+				phaseTiming := parsePhaseTiming(line)
+				phaseTimings = append(phaseTimings, &phaseTiming)
+			}
+		}
+		bazelMetrics.PhaseTimings = phaseTimings
+
+		return bazelMetrics
+	}
+
+	if _, err := os.Stat(bazelProfileFile); err != nil {
+		// We can assume bazel didn't run if the profile doesn't exist
+		return
+	}
+	bazelProto := readBazelProto(bazelProfileFile)
+	bazelProto.ExitCode = proto.Int32(config.bazelExitCode)
+	shared.Save(&bazelProto, bazelMetricsFile)
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index 2dda52a..8ec9680 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -89,7 +89,8 @@
 	skipMetricsUpload        bool
 	buildStartedTime         int64 // For metrics-upload-only - manually specify a build-started time
 	buildFromTextStub        bool
-	ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
+	ensureAllowlistIntegrity bool  // For CI builds - make sure modules are mixed-built
+	bazelExitCode            int32 // For b-runs - necessary for updating NonZeroExit
 
 	// From the product config
 	katiArgs        []string
@@ -298,11 +299,12 @@
 	return true
 }
 
-func UploadOnlyConfig(ctx Context, _ ...string) Config {
+func UploadOnlyConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
 		environ:       OsEnvironment(),
 		sandboxConfig: &SandboxConfig{},
 	}
+	ret.parseArgs(ctx, args)
 	srcDir := absPath(ctx, ".")
 	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
 	if err := loadEnvConfig(ctx, ret, bc); err != nil {
@@ -883,6 +885,14 @@
 			}
 		} else if arg == "--ensure-allowlist-integrity" {
 			c.ensureAllowlistIntegrity = true
+		} else if strings.HasPrefix(arg, "--bazel-exit-code=") {
+			bazelExitCodeStr := strings.TrimPrefix(arg, "--bazel-exit-code=")
+			val, err := strconv.Atoi(bazelExitCodeStr)
+			if err == nil {
+				c.bazelExitCode = int32(val)
+			} else {
+				ctx.Fatalf("Error parsing bazel-exit-code", err)
+			}
 		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
@@ -1723,6 +1733,10 @@
 	return time.UnixMilli(c.buildStartedTime)
 }
 
+func (c *configImpl) BazelExitCode() int32 {
+	return c.bazelExitCode
+}
+
 func GetMetricsUploader(topDir string, env *Environment) string {
 	if p, ok := env.Get("METRICS_UPLOADER"); ok {
 		metricsUploader := filepath.Join(topDir, p)
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index efe7478..b83b5cb 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -150,6 +150,7 @@
 	"PRODUCT_INCLUDE_TAGS",
 	"PRODUCT_SOURCE_ROOT_DIRS",
 	"TARGET_PRODUCT",
+	"TARGET_RELEASE",
 	"TARGET_BUILD_VARIANT",
 	"TARGET_BUILD_APPS",
 	"TARGET_BUILD_UNBUNDLED",
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 3f628cf..62079fe 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -87,6 +87,8 @@
 			"TEST_MAPPING",
 			// Bazel top-level file to mark a directory as a Bazel workspace.
 			"WORKSPACE",
+			// METADATA file of packages
+			"METADATA",
 		},
 		// Bazel Starlark configuration files and all .mk files for product/board configuration.
 		IncludeSuffixes: []string{".bzl", ".mk"},
@@ -189,6 +191,13 @@
 		ctx.Fatalf("Could not find OWNERS: %v", err)
 	}
 
+	// Recursively look for all METADATA files.
+	metadataFiles := f.FindNamedAt(".", "METADATA")
+	err = dumpListToFile(ctx, config, metadataFiles, filepath.Join(dumpDir, "METADATA.list"))
+	if err != nil {
+		ctx.Fatalf("Could not find METADATA: %v", err)
+	}
+
 	// Recursively look for all TEST_MAPPING files.
 	testMappings := f.FindNamedAt(".", "TEST_MAPPING")
 	err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 6479925..3b9d301 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -183,8 +183,6 @@
 		return
 	}
 
-	ctx.Status.Status("Dumping rbe metrics...")
-
 	outputDir := config.rbeProxyLogsDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 563199b..18bf3b9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -400,6 +400,8 @@
 			pbi.Inputs = append(pbi.Inputs,
 				config.Bp2BuildFilesMarkerFile(),
 				filepath.Join(config.FileListDir(), "bazel.list"))
+		case bp2buildFilesTag:
+			pbi.Inputs = append(pbi.Inputs, filepath.Join(config.FileListDir(), "METADATA.list"))
 		}
 		invocations = append(invocations, pbi)
 	}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 9959e6f..9f14bdd 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,21 +18,16 @@
 // another.
 
 import (
-	"bufio"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"strconv"
-	"strings"
 	"time"
 
-	"android/soong/shared"
 	"android/soong/ui/metrics"
 
 	"google.golang.org/protobuf/proto"
 
-	bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
 	upload_proto "android/soong/ui/metrics/upload_proto"
 )
 
@@ -78,113 +73,12 @@
 	return metricsFiles
 }
 
-func parseTimingToNanos(str string) int64 {
-	millisString := removeDecimalPoint(str)
-	timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
-	return timingMillis * 1000000
-}
-
-func parsePercentageToTenThousandths(str string) int32 {
-	percentageString := removeDecimalPoint(str)
-	//remove the % at the end of the string
-	percentage := strings.ReplaceAll(percentageString, "%", "")
-	percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
-	return int32(percentagePortion)
-}
-
-func removeDecimalPoint(numString string) string {
-	// The format is always 0.425 or 10.425
-	return strings.ReplaceAll(numString, ".", "")
-}
-
-func parseTotal(line string) int64 {
-	words := strings.Fields(line)
-	timing := words[3]
-	return parseTimingToNanos(timing)
-}
-
-func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
-	words := strings.Fields(line)
-	getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
-		// Sample lines include:
-		// Total launch phase time   0.011 s    2.59%
-		// Total target pattern evaluation phase time  0.011 s    2.59%
-		var beginning int
-		var end int
-		for ind, word := range words {
-			if word == "Total" {
-				beginning = ind + 1
-			} else if beginning > 0 && word == "phase" {
-				end = ind
-				break
-			}
-		}
-		phaseName := strings.Join(words[beginning:end], " ")
-
-		// end is now "phase" - advance by 2 for timing and 4 for percentage
-		percentageString := words[end+4]
-		timingString := words[end+2]
-		timing := parseTimingToNanos(timingString)
-		percentagePortion := parsePercentageToTenThousandths(percentageString)
-		return phaseName, timing, percentagePortion
-	}
-
-	phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
-	phaseTiming := bazel_metrics_proto.PhaseTiming{}
-	phaseTiming.DurationNanos = &timing
-	phaseTiming.PortionOfBuildTime = &portion
-
-	phaseTiming.PhaseName = &phaseName
-	return phaseTiming
-}
-
-func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
-	if bazelProfileFile == "" {
-		return
-	}
-
-	readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
-		//serialize the proto, write it
-		bazelMetrics := bazel_metrics_proto.BazelMetrics{}
-
-		file, err := os.ReadFile(filepath)
-		if err != nil {
-			ctx.Fatalln("Error reading metrics file\n", err)
-		}
-
-		scanner := bufio.NewScanner(strings.NewReader(string(file)))
-		scanner.Split(bufio.ScanLines)
-
-		var phaseTimings []*bazel_metrics_proto.PhaseTiming
-		for scanner.Scan() {
-			line := scanner.Text()
-			if strings.HasPrefix(line, "Total run time") {
-				total := parseTotal(line)
-				bazelMetrics.Total = &total
-			} else if strings.HasPrefix(line, "Total") {
-				phaseTiming := parsePhaseTiming(line)
-				phaseTimings = append(phaseTimings, &phaseTiming)
-			}
-		}
-		bazelMetrics.PhaseTimings = phaseTimings
-
-		return bazelMetrics
-	}
-
-	if _, err := os.Stat(bazelProfileFile); err != nil {
-		// We can assume bazel didn't run if the profile doesn't exist
-		return
-	}
-	bazelProto := readBazelProto(bazelProfileFile)
-	shared.Save(&bazelProto, bazelMetricsFile)
-}
-
 // UploadMetrics uploads a set of metrics files to a server for analysis.
 // The metrics files are first copied to a temporary directory
 // and the uploader is then executed in the background to allow the user/system
 // to continue working. Soong communicates to the uploader through the
 // upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
@@ -194,7 +88,6 @@
 		return
 	}
 
-	processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
 	// Several of the files might be directories.
 	metricsFiles := pruneMetricsFiles(paths)
 	if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 58d9237..1fcded9 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -166,7 +166,7 @@
 				metricsUploader: tt.uploader,
 			}}
 
-			UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
+			UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
 		})
 	}
 }
@@ -221,7 +221,7 @@
 				metricsUploader: "echo",
 			}}
 
-			UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
+			UploadMetrics(ctx, config, true, time.Now(), metricsFile)
 			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
 		})
 	}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 82d11ed..a282e20 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -228,7 +228,7 @@
 	m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
 }
 
-func (m *Metrics) UpdateTotalRealTime(data []byte) error {
+func (m *Metrics) UpdateTotalRealTimeAndNonZeroExit(data []byte, bazelExitCode int32) error {
 	if err := proto.Unmarshal(data, &m.metrics); err != nil {
 		return fmt.Errorf("Failed to unmarshal proto", err)
 	}
@@ -236,6 +236,9 @@
 	endTime := uint64(time.Now().UnixNano())
 
 	*m.metrics.Total.RealTime = *proto.Uint64(endTime - startTime)
+
+	bazelError := bazelExitCode != 0
+	m.metrics.NonZeroExit = proto.Bool(bazelError)
 	return nil
 }
 
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index def76aa..a2ccc20 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -78,6 +78,15 @@
 	return nil
 }
 
+type explicitFile struct{}
+
+func (explicitFile) String() string { return `""` }
+
+func (explicitFile) Set(s string) error {
+	fileArgsBuilder.ExplicitPathInZip(s)
+	return nil
+}
+
 type dir struct{}
 
 func (dir) String() string { return `""` }
@@ -173,6 +182,7 @@
 	flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
 	flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
 	flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
+	flags.Var(&explicitFile{}, "e", "filename to use in the zip file for the next -f argument")
 
 	flags.Parse(expandedArgs[1:])
 
diff --git a/zip/zip.go b/zip/zip.go
index 6f1a8ad..5e1a104 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -80,6 +80,7 @@
 
 type FileArg struct {
 	PathPrefixInZip, SourcePrefixToStrip string
+	ExplicitPathInZip                    string
 	SourceFiles                          []string
 	JunkPaths                            bool
 	GlobDir                              string
@@ -124,6 +125,10 @@
 	arg := b.state
 	arg.SourceFiles = []string{name}
 	b.fileArgs = append(b.fileArgs, arg)
+
+	if b.state.ExplicitPathInZip != "" {
+		b.state.ExplicitPathInZip = ""
+	}
 	return b
 }
 
@@ -189,6 +194,12 @@
 	return b
 }
 
+// ExplicitPathInZip sets the path in the zip file for the next File call.
+func (b *FileArgsBuilder) ExplicitPathInZip(s string) *FileArgsBuilder {
+	b.state.ExplicitPathInZip = s
+	return b
+}
+
 func (b *FileArgsBuilder) Error() error {
 	if b == nil {
 		return nil
@@ -425,7 +436,9 @@
 
 	var dest string
 
-	if fa.JunkPaths {
+	if fa.ExplicitPathInZip != "" {
+		dest = fa.ExplicitPathInZip
+	} else if fa.JunkPaths {
 		dest = filepath.Base(src)
 	} else {
 		var err error
diff --git a/zip/zip_test.go b/zip/zip_test.go
index e7fdea8..c64c3f4 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -454,6 +454,60 @@
 				fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
 			},
 		},
+		{
+			name: "explicit path",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/a").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("foo", fileA, zip.Deflate),
+				fh("a/a/b", fileB, zip.Deflate),
+			},
+		},
+		{
+			name: "explicit path with prefix",
+			args: fileArgsBuilder().
+				PathPrefixInZip("prefix").
+				ExplicitPathInZip("foo").
+				File("a/a/a").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("prefix/foo", fileA, zip.Deflate),
+				fh("prefix/a/a/b", fileB, zip.Deflate),
+			},
+		},
+		{
+			name: "explicit path with glob",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/a*").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("foo", fileA, zip.Deflate),
+				fh("a/a/b", fileB, zip.Deflate),
+			},
+		},
+		{
+			name: "explicit path with junk paths",
+			args: fileArgsBuilder().
+				JunkPaths(true).
+				ExplicitPathInZip("foo/bar").
+				File("a/a/a*").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("foo/bar", fileA, zip.Deflate),
+				fh("b", fileB, zip.Deflate),
+			},
+		},
 
 		// errors
 		{
@@ -490,6 +544,22 @@
 				File("d/a/a"),
 			err: ConflictingFileError{},
 		},
+		{
+			name: "error explicit path conflicting",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/a").
+				ExplicitPathInZip("foo").
+				File("a/a/b"),
+			err: ConflictingFileError{},
+		},
+		{
+			name: "error explicit path conflicting glob",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/*"),
+			err: ConflictingFileError{},
+		},
 	}
 
 	for _, test := range testCases {