Merge "Upload bp2build_metrics file"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 2e4068d..ac3158e 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -726,7 +726,6 @@
 
 		// aar support
 		"prebuilt_car-ui-androidx-core-common",         // TODO(b/224773339), genrule dependency creates an .aar, not a .jar
-		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
 		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
 		// ERROR: The dependencies for the following 1 jar(s) are not complete.
 		// 1.bazel-out/android_target-fastbuild/bin/prebuilts/tools/common/m2/_aar/robolectric-monitor-1.0.2-alpha1/classes_and_libs_merged.jar
@@ -1345,14 +1344,13 @@
 		"prebuilt_kotlin-stdlib-jdk8",
 		"prebuilt_kotlin-test",
 		// TODO(b/217750501) exclude_files property not supported
-		"prebuilt_platform-robolectric-4.4-prebuilt",
 		"prebuilt_platform-robolectric-4.5.1-prebuilt",
 		"prebuilt_currysrc_org.eclipse",
 	}
 
 	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
 	// in either prod mode or staging mode.
-	ProdMixedBuildsEnabledList = []string{}
+	ProdMixedBuildsEnabledList = []string{"com.android.tzdata"}
 
 	// Staging-mode allowlist. Modules in this list are only built
 	// by Bazel with --bazel-mode-staging. This list should contain modules
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index e2751d6..acb81a4 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -81,7 +81,7 @@
 	// all request-relevant information about a target and returns a string containing
 	// this information.
 	// The function should have the following properties:
-	//   - `target` is the only parameter to this function (a configured target).
+	//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
 	//   - The return value must be a string.
 	//   - The function body should not be indented outside of its own scope.
 	StarlarkFunctionBody() string
@@ -743,12 +743,12 @@
 }
 `
 	functionDefFormatString := `
-def %s(target):
+def %s(target, id_string):
 %s
 `
 	mainSwitchSectionFormatString := `
   if id_string in %s:
-    return id_string + ">>" + %s(target)
+    return id_string + ">>" + %s(target, id_string)
 `
 
 	for requestType := range requestTypeToCqueryIdEntries {
@@ -1050,18 +1050,26 @@
 
 	for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
 		var outputs []Path
+		var orderOnlies []Path
 		for _, depsetDepHash := range depset.TransitiveDepSetHashes {
 			otherDepsetName := bazelDepsetName(depsetDepHash)
 			outputs = append(outputs, PathForPhony(ctx, otherDepsetName))
 		}
 		for _, artifactPath := range depset.DirectArtifacts {
-			outputs = append(outputs, PathForBazelOut(ctx, artifactPath))
+			pathInBazelOut := PathForBazelOut(ctx, artifactPath)
+			if artifactPath == "bazel-out/volatile-status.txt" {
+				// See https://bazel.build/docs/user-manual#workspace-status
+				orderOnlies = append(orderOnlies, pathInBazelOut)
+			} else {
+				outputs = append(outputs, pathInBazelOut)
+			}
 		}
 		thisDepsetName := bazelDepsetName(depset.ContentHash)
 		ctx.Build(pctx, BuildParams{
 			Rule:      blueprint.Phony,
 			Outputs:   []WritablePath{PathForPhony(ctx, thisDepsetName)},
 			Implicits: outputs,
+			OrderOnly: orderOnlies,
 		})
 	}
 
diff --git a/android/license.go b/android/license.go
index cde5e6e..ab8431a 100644
--- a/android/license.go
+++ b/android/license.go
@@ -15,10 +15,12 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"fmt"
-	"github.com/google/blueprint"
 	"os"
+
+	"github.com/google/blueprint"
+
+	"android/soong/bazel"
 )
 
 type licenseKindDependencyTag struct {
@@ -101,6 +103,14 @@
 }
 
 func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+	for i, license := range m.properties.License_kinds {
+		for j := i + 1; j < len(m.properties.License_kinds); j++ {
+			if license == m.properties.License_kinds[j] {
+				ctx.ModuleErrorf("Duplicated license kind: %q", license)
+				break
+			}
+		}
+	}
 	ctx.AddVariationDependencies(nil, licenseKindTag, m.properties.License_kinds...)
 }
 
diff --git a/android/license_test.go b/android/license_test.go
index 7222cd7..89e7f06 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -90,6 +90,36 @@
 		},
 	},
 	{
+		name: "must not duplicate license_kind",
+		fs: map[string][]byte{
+			"top/Android.bp": []byte(`
+				license_kind {
+					name: "top_by_exception_only",
+					conditions: ["by_exception_only"],
+					visibility: ["//visibility:private"],
+				}
+
+				license_kind {
+					name: "top_by_exception_only_2",
+					conditions: ["by_exception_only"],
+					visibility: ["//visibility:private"],
+				}
+
+				license {
+					name: "top_proprietary",
+					license_kinds: [
+						"top_by_exception_only",
+						"top_by_exception_only_2",
+						"top_by_exception_only"
+					],
+					visibility: ["//visibility:public"],
+				}`),
+		},
+		expectedErrors: []string{
+			`top/Android.bp:14:5: module "top_proprietary": Duplicated license kind: "top_by_exception_only"`,
+		},
+	},
+	{
 		name: "license_kind module must exist",
 		fs: map[string][]byte{
 			"top/Android.bp": []byte(`
diff --git a/android/neverallow.go b/android/neverallow.go
index 293bac8..ad9880a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -59,6 +59,7 @@
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
 	AddNeverAllowRules(createBp2BuildRule())
+	AddNeverAllowRules(createCcStubsRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -214,6 +215,17 @@
 	}
 }
 
+func createCcStubsRule() Rule {
+	ccStubsImplementationInstallableProjectsAllowedList := []string{
+		"packages/modules/Virtualization/vm_payload",
+	}
+
+	return NeverAllow().
+		NotIn(ccStubsImplementationInstallableProjectsAllowedList...).
+		WithMatcher("stubs.implementation_installable", isSetMatcherInstance).
+		Because("implementation_installable can only be used in allowed projects.")
+}
+
 func createUncompressDexRules() []Rule {
 	return []Rule{
 		NeverAllow().
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 4772799..5f5f9a1 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -367,6 +367,22 @@
 			"framework can't be used when building against SDK",
 		},
 	},
+	// Test for the rule restricting use of implementation_installable
+	{
+		name: `"implementation_installable" outside allowed list`,
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				cc_library {
+					name: "outside_allowed_list",
+					stubs: {
+                                                implementation_installable: true,
+					},
+				}`),
+		},
+		expectedErrors: []string{
+			`module "outside_allowed_list": violates neverallow`,
+		},
+	},
 }
 
 var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -419,6 +435,10 @@
 	Platform struct {
 		Shared_libs []string
 	}
+
+	Stubs struct {
+		Implementation_installable *bool
+	}
 }
 
 type mockCcLibraryModule struct {
diff --git a/apex/apex.go b/apex/apex.go
index 04808c1..b1b4e47 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2301,7 +2301,7 @@
 				//
 				// Always include if we are a host-apex however since those won't have any
 				// system libraries.
-				if !am.DirectlyInAnyApex() {
+				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
 					// we need a module name for Make
 					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
 					if !android.InList(name, a.requiredDeps) {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ea3e734..883c3c8 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -8413,6 +8413,30 @@
 	}
 }
 
+func TestApexSet_NativeBridge(t *testing.T) {
+	ctx := testApex(t, `
+		apex_set {
+			name: "myapex",
+			set: "myapex.apks",
+			filename: "foo_v2.apex",
+			overrides: ["foo"],
+		}
+	`,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.Targets[android.Android] = []android.Target{
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "", Abi: []string{"x86_64"}}},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled},
+			}
+		}),
+	)
+
+	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+
+	// Check extract_apks tool parameters. No native bridge arch expected
+	extractedApex := m.Output("extracted/myapex.apks")
+	android.AssertStringEquals(t, "abis", "X86_64", extractedApex.Args["abis"])
+}
+
 func TestNoStaticLinkingToStubsLib(t *testing.T) {
 	testApexError(t, `.*required by "mylib" is a native library providing stub.*`, `
 		apex {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 39446a1..6fdd50a 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -24,6 +24,7 @@
 	"android/soong/android"
 	"android/soong/java"
 	"android/soong/provenance"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -831,6 +832,8 @@
 	}
 	apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
 	p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+	// Filter out NativeBridge archs (b/260115309)
+	abis := java.SupportedAbis(ctx, true)
 	ctx.Build(pctx,
 		android.BuildParams{
 			Rule:        extractMatchingApex,
@@ -838,7 +841,7 @@
 			Inputs:      android.Paths{apexSet},
 			Output:      p.extractedApex,
 			Args: map[string]string{
-				"abis":              strings.Join(java.SupportedAbis(ctx), ","),
+				"abis":              strings.Join(abis, ","),
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(p.properties.Prerelease)),
 				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
 			},
diff --git a/bazel/configurability.go b/bazel/configurability.go
index a93aa00..3f4cc73 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -203,6 +203,11 @@
 	osAndInApexMap = map[string]string{
 		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
 		AndroidAndNonApex:          "//build/bazel/rules/apex:android-non_apex",
+		osDarwin:                   "//build/bazel/platforms/os:darwin",
+		osLinux:                    "//build/bazel/platforms/os:linux",
+		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
+		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
+		osWindows:                  "//build/bazel/platforms/os:windows",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
 	}
 
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index b675f17..e4830d3 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -50,7 +50,7 @@
 // all request-relevant information about a target and returns a string containing
 // this information.
 // The function should have the following properties:
-//   - `target` is the only parameter to this function (a configured target).
+//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
 func (g getOutputFilesRequestType) StarlarkFunctionBody() string {
@@ -75,7 +75,7 @@
 // all request-relevant information about a target and returns a string containing
 // this information.
 // The function should have the following properties:
-//   - `target` is the only parameter to this function (a configured target).
+//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
 func (g getPythonBinaryRequestType) StarlarkFunctionBody() string {
@@ -102,13 +102,16 @@
 // all request-relevant information about a target and returns a string containing
 // this information.
 // The function should have the following properties:
-//   - `target` is the only parameter to this function (a configured target).
+//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
 func (g getCcInfoType) StarlarkFunctionBody() string {
 	return `
 outputFiles = [f.path for f in target.files.to_list()]
-cc_info = providers(target)["CcInfo"]
+p = providers(target)
+cc_info = p.get("CcInfo")
+if not cc_info:
+  fail("%s did not provide CcInfo" % id_string)
 
 includes = cc_info.compilation_context.includes.to_list()
 system_includes = cc_info.compilation_context.system_includes.to_list()
@@ -120,8 +123,8 @@
 linker_inputs = cc_info.linking_context.linker_inputs.to_list()
 
 static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo"
-if static_info_tag in providers(target):
-  static_info = providers(target)[static_info_tag]
+if static_info_tag in p:
+  static_info = p[static_info_tag]
   ccObjectFiles = [f.path for f in static_info.objects]
   rootStaticArchives = [static_info.root_static_archive.path]
 else:
@@ -141,14 +144,14 @@
 unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
 unstripped = ""
 
-if shared_info_tag in providers(target):
-  shared_info = providers(target)[shared_info_tag]
+if shared_info_tag in p:
+  shared_info = p[shared_info_tag]
   path = shared_info.output_file.path
   sharedLibraries.append(path)
   rootSharedLibraries += [path]
   unstripped = path
-  if unstripped_tag in providers(target):
-    unstripped = providers(target)[unstripped_tag].unstripped.path
+  if unstripped_tag in p:
+    unstripped = p[unstripped_tag].unstripped.path
 else:
   for linker_input in linker_inputs:
     for library in linker_input.libraries:
@@ -160,14 +163,13 @@
 
 toc_file = ""
 toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo"
-if toc_file_tag in providers(target):
-  toc_file = providers(target)[toc_file_tag].toc.path
+if toc_file_tag in p:
+  toc_file = p[toc_file_tag].toc.path
 else:
   # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization
   pass
 
 tidy_files = []
-p = providers(target)
 clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
 if clang_tidy_info:
   tidy_files = [v.path for v in clang_tidy_info.tidy_files.to_list()]
@@ -213,11 +215,14 @@
 // The returned string is the body of a Starlark function which obtains
 // all request-relevant information about a target and returns a string containing
 // this information. The function should have the following properties:
-//   - `target` is the only parameter to this function (a configured target).
+//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
 func (g getApexInfoType) StarlarkFunctionBody() string {
-	return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"]
+	return `
+info = providers(target).get("//build/bazel/rules/apex:apex.bzl%ApexInfo")
+if not info:
+  fail("%s did not provide ApexInfo" % id_string)
 bundle_key_info = info.bundle_key_info
 container_key_info = info.container_key_info
 return json_encode({
diff --git a/bazel/properties.go b/bazel/properties.go
index 823cda8..ee9609a 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -843,6 +843,26 @@
 // ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
 // the base value and included in default values as appropriate.
 func (lla *LabelListAttribute) ResolveExcludes() {
+	// If there are OsAndInApexAxis, we need to use
+	//   * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
+	//     included in non-Android OSes
+	//   * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
+	//     be included in the non-Android OSes
+	if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
+		inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
+		for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
+			// OsAndroid has already handled its excludes.
+			// We only need to copy the excludes from other arches, so if there are none, skip it.
+			if config == OsAndroid || len(labels.Excludes) == 0 {
+				continue
+			}
+			lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
+				Includes: inApexLabels.Includes,
+				Excludes: labels.Excludes,
+			}
+		}
+	}
+
 	for axis, configToLabels := range lla.ConfigurableValues {
 		baseLabels := lla.Value.deepCopy()
 		for config, val := range configToLabels {
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 4c86374..61acf68 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -2976,6 +2976,63 @@
 	})
 }
 
+func TestCcLibraryExcludesLibsHost(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"bar.map.txt": "",
+		},
+		Blueprint: simpleModuleDoNotConvertBp2build("cc_library", "bazlib") + `
+cc_library {
+	name: "quxlib",
+	stubs: { symbol_file: "bar.map.txt", versions: ["current"] },
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+	name: "barlib",
+	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+	name: "foolib",
+	shared_libs: ["barlib", "quxlib"],
+	target: {
+		host: {
+			shared_libs: ["bazlib"],
+			exclude_shared_libs: ["barlib"],
+		},
+	},
+	include_build_directory: false,
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{
+			"implementation_dynamic_deps": `select({
+        "//build/bazel/platforms/os:darwin": [":bazlib"],
+        "//build/bazel/platforms/os:linux": [":bazlib"],
+        "//build/bazel/platforms/os:linux_bionic": [":bazlib"],
+        "//build/bazel/platforms/os:linux_musl": [":bazlib"],
+        "//build/bazel/platforms/os:windows": [":bazlib"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:darwin": [":quxlib"],
+        "//build/bazel/platforms/os:linux": [":quxlib"],
+        "//build/bazel/platforms/os:linux_bionic": [":quxlib"],
+        "//build/bazel/platforms/os:linux_musl": [":quxlib"],
+        "//build/bazel/platforms/os:windows": [":quxlib"],
+        "//build/bazel/rules/apex:android-in_apex": [
+            ":barlib_stub_libs_current",
+            ":quxlib_stub_libs_current",
+        ],
+        "//conditions:default": [
+            ":barlib",
+            ":quxlib",
+        ],
+    })`,
+		}),
+	})
+}
+
 func TestCcLibraryEscapeLdflags(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		ModuleTypeUnderTest:        "cc_library",
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 3750804..0f1a8b2 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -90,6 +90,7 @@
 }
 
 func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+	t.Helper()
 	bp2buildSetup := func(ctx *android.TestContext) {
 		registerModuleTypes(ctx)
 		ctx.RegisterForBazelConversion()
@@ -98,6 +99,7 @@
 }
 
 func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+	t.Helper()
 	apiBp2BuildSetup := func(ctx *android.TestContext) {
 		registerModuleTypes(ctx)
 		ctx.RegisterForApiBazelConversion()
diff --git a/cc/cc.go b/cc/cc.go
index 306e483..8b3f456 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1205,6 +1205,8 @@
 	return c
 }
 
+// UseVndk() returns true if this module is built against VNDK.
+// This means the vendor and product variants of a module.
 func (c *Module) UseVndk() bool {
 	return c.Properties.VndkVersion != ""
 }
@@ -1298,6 +1300,9 @@
 	return false
 }
 
+// IsVndk() returns true if this module has a vndk variant.
+// Note that IsVndk() returns true for all variants of vndk-enabled libraries. Not only vendor variant,
+// but also platform and product variants of vndk-enabled libraries return true for IsVndk().
 func (c *Module) IsVndk() bool {
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		return vndkdep.isVndk()
@@ -1376,6 +1381,13 @@
 	return false
 }
 
+func (c *Module) IsStubsImplementationRequired() bool {
+	if lib := c.library; lib != nil {
+		return lib.isStubsImplementationRequired()
+	}
+	return false
+}
+
 // If this is a stubs library, ImplementationModuleName returns the name of the module that contains
 // the implementation.  If it is an implementation library it returns its own name.
 func (c *Module) ImplementationModuleName(ctx android.BaseModuleContext) string {
diff --git a/cc/library.go b/cc/library.go
index b639930..0729ff4 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -71,6 +71,12 @@
 		// List versions to generate stubs libs for. The version name "current" is always
 		// implicitly added.
 		Versions []string
+
+		// Whether to not require the implementation of the library to be installed if a
+		// client of the stubs is installed. Defaults to true; set to false if the
+		// implementation is made available by some other means, e.g. in a Microdroid
+		// virtual machine.
+		Implementation_installable *bool
 	}
 
 	// set the name of the output
@@ -1339,6 +1345,7 @@
 	buildStubs() bool
 	setBuildStubs(isLatest bool)
 	hasStubsVariants() bool
+	isStubsImplementationRequired() bool
 	setStubsVersion(string)
 	stubsVersion() string
 
@@ -2298,6 +2305,10 @@
 		len(library.Properties.Stubs.Versions) > 0
 }
 
+func (library *libraryDecorator) isStubsImplementationRequired() bool {
+	return BoolDefault(library.Properties.Stubs.Implementation_installable, true)
+}
+
 func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
 	if !library.hasStubsVariants() {
 		return nil
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index c420567..82db634 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -132,21 +132,21 @@
 	*android_bundle_proto.ApkDescription
 }
 
-func (m apkDescriptionMatcher) matches(config TargetConfig) bool {
-	return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config)
+func (m apkDescriptionMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
+	return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config, allAbisMustMatch)
 }
 
 type apkTargetingMatcher struct {
 	*android_bundle_proto.ApkTargeting
 }
 
-func (m apkTargetingMatcher) matches(config TargetConfig) bool {
+func (m apkTargetingMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
 	return m.ApkTargeting == nil ||
 		(abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
 			languageTargetingMatcher{m.LanguageTargeting}.matches(config) &&
 			screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
 			sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
-			multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config))
+			multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config, allAbisMustMatch))
 }
 
 type languageTargetingMatcher struct {
@@ -215,33 +215,27 @@
 		}
 	}
 
-	m = append(multiAbiValue{}, m...)
-	sort.Slice(m, sortAbis(m))
-	other = append(multiAbiValue{}, other...)
-	sort.Slice(other, sortAbis(other))
+	sortedM := append(multiAbiValue{}, m...)
+	sort.Slice(sortedM, sortAbis(sortedM))
+	sortedOther := append(multiAbiValue{}, other...)
+	sort.Slice(sortedOther, sortAbis(sortedOther))
 
-	for i := 0; i < min(len(m), len(other)); i++ {
-		if multiAbiPriorities[m[i].Alias] > multiAbiPriorities[other[i].Alias] {
+	for i := 0; i < min(len(sortedM), len(sortedOther)); i++ {
+		if multiAbiPriorities[sortedM[i].Alias] > multiAbiPriorities[sortedOther[i].Alias] {
 			return 1
 		}
-		if multiAbiPriorities[m[i].Alias] < multiAbiPriorities[other[i].Alias] {
+		if multiAbiPriorities[sortedM[i].Alias] < multiAbiPriorities[sortedOther[i].Alias] {
 			return -1
 		}
 	}
 
-	if len(m) == len(other) {
-		return 0
-	}
-	if len(m) > len(other) {
-		return 1
-	}
-	return -1
+	return len(sortedM) - len(sortedOther)
 }
 
 // this logic should match the logic in bundletool at
 // https://github.com/google/bundletool/blob/ae0fc0162fd80d92ef8f4ef4527c066f0106942f/src/main/java/com/android/tools/build/bundletool/device/MultiAbiMatcher.java#L43
 // (note link is the commit at time of writing; but logic should always match the latest)
-func (t multiAbiTargetingMatcher) matches(config TargetConfig) bool {
+func (t multiAbiTargetingMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
 	if t.MultiAbiTargeting == nil {
 		return true
 	}
@@ -250,12 +244,19 @@
 	}
 
 	multiAbiIsValid := func(m multiAbiValue) bool {
+		numValid := 0
 		for _, abi := range m {
-			if _, ok := config.abis[abi.Alias]; !ok {
-				return false
+			if _, ok := config.abis[abi.Alias]; ok {
+				numValid += 1
 			}
 		}
-		return true
+		if numValid == 0 {
+			return false
+		} else if numValid > 0 && !allAbisMustMatch {
+			return true
+		} else {
+			return numValid == len(m)
+		}
 	}
 
 	// ensure that the current value is valid for our config
@@ -264,6 +265,7 @@
 	for _, multiAbi := range multiAbiSet {
 		if multiAbiIsValid(multiAbi.GetAbi()) {
 			valueSetContainsViableAbi = true
+			break
 		}
 	}
 
@@ -362,13 +364,13 @@
 	*android_bundle_proto.VariantTargeting
 }
 
-func (m variantTargetingMatcher) matches(config TargetConfig) bool {
+func (m variantTargetingMatcher) matches(config TargetConfig, allAbisMustMatch bool) bool {
 	if m.VariantTargeting == nil {
 		return true
 	}
 	return sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
 		abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
-		multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config) &&
+		multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config, allAbisMustMatch) &&
 		screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
 		textureCompressionFormatTargetingMatcher{m.TextureCompressionFormatTargeting}.matches(config)
 }
@@ -380,30 +382,42 @@
 
 // Return all entries matching target configuration
 func selectApks(toc Toc, targetConfig TargetConfig) SelectionResult {
-	var result SelectionResult
-	for _, variant := range (*toc).GetVariant() {
-		if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig)) {
-			continue
-		}
-		for _, as := range variant.GetApkSet() {
-			if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
+	checkMatching := func(allAbisMustMatch bool) SelectionResult {
+		var result SelectionResult
+		for _, variant := range (*toc).GetVariant() {
+			if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig, allAbisMustMatch)) {
 				continue
 			}
-			for _, apkdesc := range as.GetApkDescription() {
-				if (apkDescriptionMatcher{apkdesc}).matches(targetConfig) {
-					result.entries = append(result.entries, apkdesc.GetPath())
-					// TODO(asmundak): As it turns out, moduleName which we get from
-					// the ModuleMetadata matches the module names of the generated
-					// entry paths just by coincidence, only for the split APKs. We
-					// need to discuss this with bundletool folks.
-					result.moduleName = as.GetModuleMetadata().GetName()
+			for _, as := range variant.GetApkSet() {
+				if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
+					continue
+				}
+				for _, apkdesc := range as.GetApkDescription() {
+					if (apkDescriptionMatcher{apkdesc}).matches(targetConfig, allAbisMustMatch) {
+						result.entries = append(result.entries, apkdesc.GetPath())
+						// TODO(asmundak): As it turns out, moduleName which we get from
+						// the ModuleMetadata matches the module names of the generated
+						// entry paths just by coincidence, only for the split APKs. We
+						// need to discuss this with bundletool folks.
+						result.moduleName = as.GetModuleMetadata().GetName()
+					}
+				}
+				// we allow only a single module, so bail out here if we found one
+				if result.moduleName != "" {
+					return result
 				}
 			}
-			// we allow only a single module, so bail out here if we found one
-			if result.moduleName != "" {
-				return result
-			}
 		}
+		return result
+	}
+	result := checkMatching(true)
+	if result.moduleName == "" {
+		// if there are no matches where all of the ABIs are available in the
+		// TargetConfig, then search again with a looser requirement of at
+		// least one matching ABI
+		// NOTE(b/260130686): this logic diverges from the logic in bundletool
+		// https://github.com/google/bundletool/blob/ae0fc0162fd80d92ef8f4ef4527c066f0106942f/src/main/java/com/android/tools/build/bundletool/device/MultiAbiMatcher.java#L43
+		result = checkMatching(false)
 	}
 	return result
 }
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
index c1d712d..9f52877 100644
--- a/cmd/extract_apks/main_test.go
+++ b/cmd/extract_apks/main_test.go
@@ -744,7 +744,11 @@
 							bp.Abi_X86_64: 0,
 						},
 					},
-					expected: SelectionResult{},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-x86.x86_64.apex",
+						}},
 				},
 				{
 					name: "multi-variant multi-target cross-target",
diff --git a/docs/perf.md b/docs/perf.md
index 694dcf1..5b53c8d 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -42,16 +42,29 @@
 ```
 
 If the elapsed time is much longer than the critical path then additional
-parallelism on the build machine will improve total build times.  If there are
+parallelism on the build machine will improve total build times. If there are
 long individual times listed in the critical path then improving build times
 for those steps or adjusting dependencies so that those steps can run earlier
 in the build graph will improve total build times.
 
 ### Soong
 
-Soong can be traced and profiled using the standard Go tools. It understands
-the `-cpuprofile`, `-trace`, and `-memprofile` command line arguments, but we
-don't currently have an easy way to enable them in the context of a full build.
+Soong proper (i.e., `soong_build` executable that processes the blueprint
+files) can be traced and profiled using the standard Go tools. It understands
+the `-trace`, `-cpuprofile`, and `-memprofile` command line arguments.
+Setting `SOONG_PROFILE_CPU` and/or `SOONG_PROFILE_MEM` environment variables
+for the build enables respective profiling, e.g., running
+
+```shell
+SOONG_PROFILE_CPU=/tmp/foo m ..._
+```
+
+saves CPU profile for each Soong invocation in /tmp/foo._step_ file, where
+_step_ is Soong execution step. The main step is `build`. The others as
+`bp2build_files`, `bp2build_workspace`, `modulegraph`, `queryview`,
+`api_bp2build`, `soong_docs` (not all of them necessarily run during the build).
+The profiles can be inspected with `go tool pprof` from the command line or
+with _Run>Open Profiler Snapshot_ in IntelliJ IDEA.
 
 ### Kati
 
diff --git a/java/app_set.go b/java/app_set.go
index d8c2a8d..0f55b77 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -98,7 +98,7 @@
 	"x86_64":  "X86_64",
 }
 
-func SupportedAbis(ctx android.ModuleContext) []string {
+func SupportedAbis(ctx android.ModuleContext, excludeNativeBridgeAbis bool) []string {
 	abiName := func(targetIdx int, deviceArch string) string {
 		if abi, found := TargetCpuAbi[deviceArch]; found {
 			return abi
@@ -109,6 +109,9 @@
 
 	var result []string
 	for i, target := range ctx.Config().Targets[android.Android] {
+		if target.NativeBridge == android.NativeBridgeEnabled && excludeNativeBridgeAbis {
+			continue
+		}
 		result = append(result, abiName(i, target.Arch.ArchType.String()))
 	}
 	return result
@@ -135,7 +138,7 @@
 			ImplicitOutputs: android.WritablePaths{as.packedOutput, as.apkcertsFile},
 			Inputs:          android.Paths{as.prebuilt.SingleSourcePath(ctx)},
 			Args: map[string]string{
-				"abis":              strings.Join(SupportedAbis(ctx), ","),
+				"abis":              strings.Join(SupportedAbis(ctx, false), ","),
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
 				"screen-densities":  screenDensities,
 				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0adaf99..77cbe9c 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -269,8 +269,10 @@
 				targets = append(targets, target)
 			}
 		}
-		if isSystemServerJar && !d.isSDKLibrary {
-			// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
+		if isSystemServerJar && moduleName(ctx) != "com.android.location.provider" {
+			// If the module is a system server jar, only preopt for the primary arch because the jar can
+			// only be loaded by system server. "com.android.location.provider" is a special case because
+			// it's also used by apps as a shared library.
 			targets = targets[:1]
 		}
 	}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 2173dae..8a291ad 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -304,6 +304,9 @@
 		flags = append(flags, "-I"+src.String())
 	}
 
+	minSdkVersion := j.MinSdkVersion(ctx).ApiLevel.FinalOrFutureInt()
+	flags = append(flags, fmt.Sprintf("--min_sdk_version=%v", minSdkVersion))
+
 	return strings.Join(flags, " "), deps
 }
 
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index 6e5d912..186c3aa 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -88,7 +88,7 @@
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
-		expected = "libjni.dylib"
+		expected = "lib64/libjni.dylib"
 	}
 
 	fooJniFilePaths := foo.jniFilePaths
diff --git a/java/java.go b/java/java.go
index 25b6349..6eeb95d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -764,6 +764,9 @@
 	// The list of permitted packages that need to be passed to the prebuilts as they are used to
 	// create the updatable-bcp-packages.txt file.
 	PermittedPackages []string
+
+	// The value of the min_sdk_version property, translated into a number where possible.
+	MinSdkVersion *string `supported_build_releases:"Tiramisu+"`
 }
 
 func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -774,6 +777,13 @@
 	p.AidlIncludeDirs = j.AidlIncludeDirs()
 
 	p.PermittedPackages = j.PermittedPackagesForUpdatableBootJars()
+
+	// If the min_sdk_version was set then add the canonical representation of the API level to the
+	// snapshot.
+	if j.deviceProperties.Min_sdk_version != nil {
+		canonical := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.ApiLevel.String())
+		p.MinSdkVersion = proptools.StringPtr(canonical)
+	}
 }
 
 func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -792,6 +802,10 @@
 		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 	}
 
+	if p.MinSdkVersion != nil {
+		propertySet.AddProperty("min_sdk_version", *p.MinSdkVersion)
+	}
+
 	if len(p.PermittedPackages) > 0 {
 		propertySet.AddProperty("permitted_packages", p.PermittedPackages)
 	}
diff --git a/java/java_test.go b/java/java_test.go
index 62c2845..ff15783 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1370,6 +1370,39 @@
 	}
 }
 
+func TestAidlFlagsMinSdkVersionDroidstubs(t *testing.T) {
+	bpTemplate := `
+	droidstubs {
+		name: "foo-stubs",
+		srcs: ["foo.aidl"],
+		%s
+		system_modules: "none",
+	}
+	`
+	testCases := []struct {
+		desc                  string
+		sdkVersionBp          string
+		minSdkVersionExpected string
+	}{
+		{
+			desc:                  "sdk_version not set, module compiles against private platform APIs",
+			sdkVersionBp:          ``,
+			minSdkVersionExpected: "10000",
+		},
+		{
+			desc:                  "sdk_version set to none, module does not build against an SDK",
+			sdkVersionBp:          `sdk_version: "none",`,
+			minSdkVersionExpected: "10000",
+		},
+	}
+	for _, tc := range testCases {
+		ctx := prepareForJavaTest.RunTestWithBp(t, fmt.Sprintf(bpTemplate, tc.sdkVersionBp))
+		aidlCmd := ctx.ModuleForTests("foo-stubs", "android_common").Rule("aidl").RuleParams.Command
+		expected := "--min_sdk_version=" + tc.minSdkVersionExpected
+		android.AssertStringDoesContain(t, "aidl command conatins incorrect min_sdk_version for testCse: "+tc.desc, aidlCmd, expected)
+	}
+}
+
 func TestAidlEnforcePermissions(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 1b64130..92ecd5e 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -358,6 +358,7 @@
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+    min_sdk_version: "2",
     permitted_packages: ["mybootlib"],
 }
 
@@ -877,6 +878,7 @@
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+    min_sdk_version: "1",
     permitted_packages: ["mybootlib"],
 }
 
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 51903ce3..2ade146 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -352,6 +352,73 @@
 	})
 }
 
+func TestSnapshotWithJavaLibrary_MinSdkVersion(t *testing.T) {
+	runTest := func(t *testing.T, targetBuildRelease, minSdkVersion, expectedMinSdkVersion string) {
+		result := android.GroupFixturePreparers(
+			prepareForSdkTestWithJava,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.Platform_version_active_codenames = []string{"S", "Tiramisu", "Unfinalized"}
+			}),
+			android.FixtureMergeEnv(map[string]string{
+				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
+			}),
+		).RunTestWithBp(t, fmt.Sprintf(`
+		sdk {
+			name: "mysdk",
+			java_header_libs: ["mylib"],
+		}
+
+		java_library {
+			name: "mylib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			min_sdk_version: "%s",
+		}
+	`, minSdkVersion))
+
+		expectedMinSdkVersionLine := ""
+		if expectedMinSdkVersion != "" {
+			expectedMinSdkVersionLine = fmt.Sprintf("    min_sdk_version: %q,\n", expectedMinSdkVersion)
+		}
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(fmt.Sprintf(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mylib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/mylib.jar"],
+%s}
+`, expectedMinSdkVersionLine)),
+		)
+	}
+
+	t.Run("min_sdk_version=S in S", func(t *testing.T) {
+		// min_sdk_version was not added to java_import until Tiramisu.
+		runTest(t, "S", "S", "")
+	})
+
+	t.Run("min_sdk_version=S in Tiramisu", func(t *testing.T) {
+		// The canonical form of S is 31.
+		runTest(t, "Tiramisu", "S", "31")
+	})
+
+	t.Run("min_sdk_version=24 in Tiramisu", func(t *testing.T) {
+		// A numerical min_sdk_version is already in canonical form.
+		runTest(t, "Tiramisu", "24", "24")
+	})
+
+	t.Run("min_sdk_version=Unfinalized in latest", func(t *testing.T) {
+		// An unfinalized min_sdk_version has no numeric value yet.
+		runTest(t, "", "Unfinalized", "Unfinalized")
+	})
+}
+
 func TestSnapshotWithJavaSystemserverLibrary(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 1ac405d..2a17cdc 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -120,6 +120,7 @@
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    min_sdk_version: "2",
     permitted_packages: ["mylib"],
 }
 
@@ -181,6 +182,7 @@
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
     jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    min_sdk_version: "2",
     permitted_packages: ["mylib"],
 }
 
diff --git a/ui/build/soong.go b/ui/build/soong.go
index c0bee4e..abaf5ae 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -195,6 +195,12 @@
 
 	allArgs = append(allArgs, commonArgs...)
 	allArgs = append(allArgs, environmentArgs(config, name)...)
+	if profileCpu := os.Getenv("SOONG_PROFILE_CPU"); profileCpu != "" {
+		allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+name)
+	}
+	if profileMem := os.Getenv("SOONG_PROFILE_MEM"); profileMem != "" {
+		allArgs = append(allArgs, "--memprofile", profileMem+"."+name)
+	}
 	allArgs = append(allArgs, "Android.bp")
 
 	return bootstrap.PrimaryBuilderInvocation{