Merge "Add a new `test_options.tags` attribute to customize test execution"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index a932a91..5c79fa2 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -584,5 +584,15 @@
 		"prebuilt_android-support-annotations-nodeps",
 		"prebuilt_android-arch-paging-common-nodeps",
 		"prebuilt_android-arch-room-common-nodeps",
+		// TODO(b/217750501) exclude_dirs property not supported
+		"prebuilt_kotlin-reflect",
+		"prebuilt_kotlin-stdlib",
+		"prebuilt_kotlin-stdlib-jdk7",
+		"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",
 	}
 )
diff --git a/android/arch.go b/android/arch.go
index a0895ed..1952b17 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -619,6 +619,12 @@
 		mctx.ModuleErrorf("%s", err.Error())
 	}
 
+	// If there are no supported targets disable the module.
+	if len(targets) == 0 {
+		base.Disable()
+		return
+	}
+
 	// If the module is using extraMultilib, decode the extraMultilib selection into
 	// a separate list of Targets.
 	var multiTargets []Target
@@ -627,6 +633,7 @@
 		if err != nil {
 			mctx.ModuleErrorf("%s", err.Error())
 		}
+		multiTargets = filterHostCross(multiTargets, targets[0].HostCross)
 	}
 
 	// Recovery is always the primary architecture, filter out any other architectures.
@@ -763,6 +770,18 @@
 	return targets
 }
 
+// filterHostCross takes a list of Targets and a hostCross value, and returns a modified list
+// that contains only Targets that have the specified HostCross.
+func filterHostCross(targets []Target, hostCross bool) []Target {
+	for i := 0; i < len(targets); i++ {
+		if targets[i].HostCross != hostCross {
+			targets = append(targets[:i], targets[i+1:]...)
+			i--
+		}
+	}
+	return targets
+}
+
 // archPropRoot is a struct type used as the top level of the arch-specific properties.  It
 // contains the "arch", "multilib", and "target" property structs.  It is used to split up the
 // property structs to limit how much is allocated when a single arch-specific property group is
@@ -1795,20 +1814,23 @@
 }
 
 // FirstTarget takes a list of Targets and a list of multilib values and returns a list of Targets
-// that contains zero or one Target for each OsType, selecting the one that matches the earliest
-// filter.
+// that contains zero or one Target for each OsType and HostCross, selecting the one that matches
+// the earliest filter.
 func FirstTarget(targets []Target, filters ...string) []Target {
 	// find the first target from each OS
 	var ret []Target
-	hasHost := false
-	set := make(map[OsType]bool)
+	type osHostCross struct {
+		os        OsType
+		hostCross bool
+	}
+	set := make(map[osHostCross]bool)
 
 	for _, filter := range filters {
 		buildTargets := filterMultilibTargets(targets, filter)
 		for _, t := range buildTargets {
-			if _, found := set[t.Os]; !found {
-				hasHost = hasHost || (t.Os.Class == Host)
-				set[t.Os] = true
+			key := osHostCross{t.Os, t.HostCross}
+			if _, found := set[key]; !found {
+				set[key] = true
 				ret = append(ret, t)
 			}
 		}
diff --git a/android/arch_test.go b/android/arch_test.go
index ad2076d..6814f8a 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -259,6 +259,27 @@
 	}
 }
 
+func (m *archTestMultiTargetsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *archTestMultiTargetsModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+}
+
+func archTestMultiTargetsModuleFactory() Module {
+	m := &archTestMultiTargetsModule{}
+	m.AddProperties(&m.props)
+	InitAndroidMultiTargetsArchModule(m, HostAndDeviceSupported, MultilibCommon)
+	return m
+}
+
+type archTestMultiTargetsModule struct {
+	ModuleBase
+	props struct {
+		Deps []string
+	}
+}
+
 func (m *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 }
 
@@ -277,19 +298,27 @@
 	PrepareForTestWithArchMutator,
 	FixtureRegisterWithContext(func(ctx RegistrationContext) {
 		ctx.RegisterModuleType("module", archTestModuleFactory)
+		ctx.RegisterModuleType("multi_targets_module", archTestMultiTargetsModuleFactory)
 	}),
 )
 
 func TestArchMutator(t *testing.T) {
 	var buildOSVariants []string
+	var buildOS64Variants []string
 	var buildOS32Variants []string
+	var buildOSCommonVariant string
+
 	switch runtime.GOOS {
 	case "linux":
 		buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"}
+		buildOS64Variants = []string{"linux_glibc_x86_64"}
 		buildOS32Variants = []string{"linux_glibc_x86"}
+		buildOSCommonVariant = "linux_glibc_common"
 	case "darwin":
 		buildOSVariants = []string{"darwin_x86_64"}
+		buildOS64Variants = []string{"darwin_x86_64"}
 		buildOS32Variants = nil
+		buildOSCommonVariant = "darwin_common"
 	}
 
 	bp := `
@@ -312,24 +341,46 @@
 			host_supported: true,
 			compile_multilib: "32",
 		}
+
+		module {
+			name: "first",
+			host_supported: true,
+			compile_multilib: "first",
+		}
+
+		multi_targets_module {
+			name: "multi_targets",
+			host_supported: true,
+		}
 	`
 
 	testCases := []struct {
-		name        string
-		preparer    FixturePreparer
-		fooVariants []string
-		barVariants []string
-		bazVariants []string
-		quxVariants []string
+		name          string
+		preparer      FixturePreparer
+		fooVariants   []string
+		barVariants   []string
+		bazVariants   []string
+		quxVariants   []string
+		firstVariants []string
+
+		multiTargetVariants    []string
+		multiTargetVariantsMap map[string][]string
+
+		goOS string
 	}{
 		{
-			name:        "normal",
-			preparer:    nil,
-			fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
-			barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
-			bazVariants: nil,
-			quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
-		},
+			name:                "normal",
+			preparer:            nil,
+			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			barVariants:         append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
+			bazVariants:         nil,
+			quxVariants:         append(buildOS32Variants, "android_arm_armv7-a-neon"),
+			firstVariants:       append(buildOS64Variants, "android_arm64_armv8-a"),
+			multiTargetVariants: []string{buildOSCommonVariant, "android_common"},
+			multiTargetVariantsMap: map[string][]string{
+				buildOSCommonVariant: buildOS64Variants,
+				"android_common":     {"android_arm64_armv8-a"},
+			}},
 		{
 			name: "host-only",
 			preparer: FixtureModifyConfig(func(config Config) {
@@ -337,10 +388,33 @@
 				config.BuildOSCommonTarget = Target{}
 				config.Targets[Android] = nil
 			}),
-			fooVariants: nil,
-			barVariants: buildOSVariants,
-			bazVariants: nil,
-			quxVariants: buildOS32Variants,
+			fooVariants:         nil,
+			barVariants:         buildOSVariants,
+			bazVariants:         nil,
+			quxVariants:         buildOS32Variants,
+			firstVariants:       buildOS64Variants,
+			multiTargetVariants: []string{buildOSCommonVariant},
+			multiTargetVariantsMap: map[string][]string{
+				buildOSCommonVariant: buildOS64Variants,
+			},
+		},
+		{
+			name: "same arch host and host cross",
+			preparer: FixtureModifyConfig(func(config Config) {
+				modifyTestConfigForMusl(config)
+				modifyTestConfigForMuslArm64HostCross(config)
+			}),
+			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			barVariants:         []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			bazVariants:         nil,
+			quxVariants:         []string{"linux_musl_x86", "android_arm_armv7-a-neon"},
+			firstVariants:       []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"},
+			multiTargetVariants: []string{"linux_musl_common", "android_common"},
+			multiTargetVariantsMap: map[string][]string{
+				"linux_musl_common": {"linux_musl_x86_64"},
+				"android_common":    {"android_arm64_armv8-a"},
+			},
+			goOS: "linux",
 		},
 	}
 
@@ -356,8 +430,21 @@
 		return ret
 	}
 
+	moduleMultiTargets := func(ctx *TestContext, name string, variant string) []string {
+		var ret []string
+		targets := ctx.ModuleForTests(name, variant).Module().MultiTargets()
+		for _, t := range targets {
+			ret = append(ret, t.String())
+		}
+		return ret
+	}
+
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			if tt.goOS != runtime.GOOS {
+				t.Skipf("requries runtime.GOOS %s", tt.goOS)
+			}
+
 			result := GroupFixturePreparers(
 				prepareForArchTest,
 				// Test specific preparer
@@ -381,6 +468,20 @@
 			if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !reflect.DeepEqual(w, g) {
 				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
 			}
+			if g, w := enabledVariants(ctx, "first"), tt.firstVariants; !reflect.DeepEqual(w, g) {
+				t.Errorf("want first variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			if g, w := enabledVariants(ctx, "multi_targets"), tt.multiTargetVariants; !reflect.DeepEqual(w, g) {
+				t.Fatalf("want multi_target variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
+			for _, variant := range tt.multiTargetVariants {
+				targets := moduleMultiTargets(ctx, "multi_targets", variant)
+				if g, w := targets, tt.multiTargetVariantsMap[variant]; !reflect.DeepEqual(w, g) {
+					t.Errorf("want ctx.MultiTarget() for %q:\n%q\ngot:\n%q\n", variant, w, g)
+				}
+			}
 		})
 	}
 }
diff --git a/android/bazel.go b/android/bazel.go
index aff6116..71eb036 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -231,16 +231,18 @@
 	// when they have the same type as one listed.
 	moduleTypeAlwaysConvert map[string]bool
 
-	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
+	// Per-module denylist to always opt modules out of bp2build conversion.
 	moduleDoNotConvert map[string]bool
 
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
 	ccLibraryStaticOnly map[string]bool
+}
 
-	// Per-module denylist to opt modules out of mixed builds. Such modules will
-	// still be generated via bp2build.
-	mixedBuildsDisabled map[string]bool
+// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
+// generate a static version of itself based on the current global configuration.
+func (a bp2BuildConversionAllowlist) GenerateCcLibraryStaticOnly(moduleName string) bool {
+	return a.ccLibraryStaticOnly[moduleName]
 }
 
 // NewBp2BuildAllowlist creates a new, empty bp2BuildConversionAllowlist
@@ -253,7 +255,6 @@
 		map[string]bool{},
 		map[string]bool{},
 		map[string]bool{},
-		map[string]bool{},
 	}
 }
 
@@ -329,43 +330,24 @@
 	return a
 }
 
-// SetMixedBuildsDisabledList copies the entries from mixedBuildsDisabled into the allowlist
-func (a bp2BuildConversionAllowlist) SetMixedBuildsDisabledList(mixedBuildsDisabled []string) bp2BuildConversionAllowlist {
-	if a.mixedBuildsDisabled == nil {
-		a.mixedBuildsDisabled = map[string]bool{}
-	}
-	for _, m := range mixedBuildsDisabled {
-		a.mixedBuildsDisabled[m] = true
-	}
-
-	return a
-}
-
 var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
 var bp2buildAllowlist OncePer
 
-func getBp2BuildAllowList() bp2BuildConversionAllowlist {
+func GetBp2BuildAllowList() bp2BuildConversionAllowlist {
 	return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
 		return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
 			SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
 			SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
 			SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
 			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
-			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList).
-			SetMixedBuildsDisabledList(allowlists.MixedBuildsDisabledList)
+			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList)
 	}).(bp2BuildConversionAllowlist)
 }
 
-// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
-// generate a static version of itself based on the current global configuration.
-func GenerateCcLibraryStaticOnly(moduleName string) bool {
-	return getBp2BuildAllowList().ccLibraryStaticOnly[moduleName]
-}
-
 // ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
 // added to the build symlink forest based on the current global configuration.
 func ShouldKeepExistingBuildFileForDir(dir string) bool {
-	return shouldKeepExistingBuildFileForDir(getBp2BuildAllowList(), dir)
+	return shouldKeepExistingBuildFileForDir(GetBp2BuildAllowList(), dir)
 }
 
 func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, dir string) bool {
@@ -405,20 +387,10 @@
 	if !ctx.Module().Enabled() {
 		return false
 	}
-	if !ctx.Config().BazelContext.BazelEnabled() {
-		return false
-	}
 	if !convertedToBazel(ctx, ctx.Module()) {
 		return false
 	}
-
-	if GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
-		// Don't use partially-converted cc_library targets in mixed builds,
-		// since mixed builds would generally rely on both static and shared
-		// variants of a cc_library.
-		return false
-	}
-	return !getBp2BuildAllowList().mixedBuildsDisabled[ctx.Module().Name()]
+	return ctx.Config().BazelContext.BazelAllowlisted(ctx.Module().Name())
 }
 
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index a5fa043..d87f988 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,6 +27,7 @@
 	"strings"
 	"sync"
 
+	"android/soong/android/allowlists"
 	"android/soong/bazel/cquery"
 	"android/soong/shared"
 
@@ -142,8 +143,11 @@
 	// queued in the BazelContext.
 	InvokeBazel(config Config) error
 
-	// Returns true if bazel is enabled for the given configuration.
-	BazelEnabled() bool
+	// Returns true if Bazel handling is enabled for the module with the given name.
+	// Note that this only implies "bazel mixed build" allowlisting. The caller
+	// should independently verify the module is eligible for Bazel handling
+	// (for example, that it is MixedBuildBuildable).
+	BazelAllowlisted(moduleName string) bool
 
 	// Returns the bazel output base (the root directory for all bazel intermediate outputs).
 	OutputBase() string
@@ -183,6 +187,17 @@
 
 	// Depsets which should be used for Bazel's build statements.
 	depsets []bazel.AqueryDepset
+
+	// Per-module allowlist/denylist functionality to control whether analysis of
+	// modules are handled by Bazel. For modules which do not have a Bazel definition
+	// (or do not sufficiently support bazel handling via MixedBuildBuildable),
+	// this allowlist will have no effect, even if the module is explicitly allowlisted here.
+	// Per-module denylist to opt modules out of bazel handling.
+	bazelDisabledModules map[string]bool
+	// Per-module allowlist to opt modules in to bazel handling.
+	bazelEnabledModules map[string]bool
+	// If true, modules are bazel-enabled by default, unless present in bazelDisabledModules.
+	modulesDefaultToBazel bool
 }
 
 var _ BazelContext = &bazelContext{}
@@ -229,7 +244,7 @@
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) BazelEnabled() bool {
+func (m MockBazelContext) BazelAllowlisted(moduleName string) bool {
 	return true
 }
 
@@ -315,7 +330,7 @@
 	return ""
 }
 
-func (n noopBazelContext) BazelEnabled() bool {
+func (n noopBazelContext) BazelAllowlisted(moduleName string) bool {
 	return false
 }
 
@@ -328,9 +343,7 @@
 }
 
 func NewBazelContext(c *config) (BazelContext, error) {
-	// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
-	// are production ready.
-	if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
+	if !c.IsMixedBuildsEnabled() {
 		return noopBazelContext{}, nil
 	}
 
@@ -338,10 +351,26 @@
 	if err != nil {
 		return nil, err
 	}
+
+	// TODO(cparsons): Use a different allowlist depending on prod vs. dev
+	// bazel mode.
+	disabledModules := map[string]bool{}
+	// Don't use partially-converted cc_library targets in mixed builds,
+	// since mixed builds would generally rely on both static and shared
+	// variants of a cc_library.
+	for staticOnlyModule, _ := range GetBp2BuildAllowList().ccLibraryStaticOnly {
+		disabledModules[staticOnlyModule] = true
+	}
+	for _, disabledDevModule := range allowlists.MixedBuildsDisabledList {
+		disabledModules[disabledDevModule] = true
+	}
+
 	return &bazelContext{
-		bazelRunner: &builtinBazelRunner{},
-		paths:       p,
-		requests:    make(map[cqueryKey]bool),
+		bazelRunner:           &builtinBazelRunner{},
+		paths:                 p,
+		requests:              make(map[cqueryKey]bool),
+		modulesDefaultToBazel: true,
+		bazelDisabledModules:  disabledModules,
 	}, nil
 }
 
@@ -386,8 +415,14 @@
 	return p.metricsDir
 }
 
-func (context *bazelContext) BazelEnabled() bool {
-	return true
+func (context *bazelContext) BazelAllowlisted(moduleName string) bool {
+	if context.bazelDisabledModules[moduleName] {
+		return false
+	}
+	if context.bazelEnabledModules[moduleName] {
+		return true
+	}
+	return context.modulesDefaultToBazel
 }
 
 func pwdPrefix() string {
@@ -851,7 +886,7 @@
 
 func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
 	// bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
-	if !ctx.Config().BazelContext.BazelEnabled() {
+	if !ctx.Config().IsMixedBuildsEnabled() {
 		return
 	}
 
diff --git a/android/bazel_test.go b/android/bazel_test.go
index da4a915..98f0a46 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -389,7 +389,7 @@
 }
 
 func TestBp2buildAllowList(t *testing.T) {
-	allowlist := getBp2BuildAllowList()
+	allowlist := GetBp2BuildAllowList()
 	for k, v := range allowlists.Bp2buildDefaultConfig {
 		if allowlist.defaultConfig[k] != v {
 			t.Errorf("bp2build default config of %s: expected: %v, got: %v", k, v, allowlist.defaultConfig[k])
@@ -415,9 +415,4 @@
 			t.Errorf("bp2build cc library static only of %s: expected: true, got: %v", k, allowlist.ccLibraryStaticOnly[k])
 		}
 	}
-	for _, k := range allowlists.MixedBuildsDisabledList {
-		if !allowlist.mixedBuildsDisabled[k] {
-			t.Errorf("bp2build mix build disabled of %s: expected: true, got: %v", k, allowlist.mixedBuildsDisabled[k])
-		}
-	}
 }
diff --git a/android/config.go b/android/config.go
index d3f8ab4..5ca9420 100644
--- a/android/config.go
+++ b/android/config.go
@@ -68,6 +68,38 @@
 	*config
 }
 
+type SoongBuildMode int
+
+// Build modes that soong_build can run as.
+const (
+	// Don't use bazel at all during module analysis.
+	AnalysisNoBazel SoongBuildMode = iota
+
+	// Bp2build mode: Generate BUILD files from blueprint files and exit.
+	Bp2build
+
+	// Generate BUILD files which faithfully represent the dependency graph of
+	// blueprint modules. Individual BUILD targets will not, however, faitfhully
+	// express build semantics.
+	GenerateQueryView
+
+	// Create a JSON representation of the module graph and exit.
+	GenerateModuleGraph
+
+	// Generate a documentation file for module type definitions and exit.
+	GenerateDocFile
+
+	// Use bazel during analysis of many allowlisted build modules. The allowlist
+	// is considered a "developer mode" allowlist, as some modules may be
+	// allowlisted on an experimental basis.
+	BazelDevMode
+
+	// Use bazel during analysis of build modules from an allowlist carefully
+	// curated by the build team to be proven stable.
+	// TODO(cparsons): Implement this mode.
+	BazelProdMode
+)
+
 // SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
@@ -157,7 +189,7 @@
 	fs         pathtools.FileSystem
 	mockBpList string
 
-	runningAsBp2Build              bool
+	BuildMode                      SoongBuildMode
 	bp2buildPackageConfig          bp2BuildConversionAllowlist
 	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
@@ -171,6 +203,12 @@
 
 	OncePer
 
+	// These fields are only used for metrics collection. A module should be added
+	// to these maps only if its implementation supports Bazel handling in mixed
+	// builds. A module being in the "enabled" list indicates that there is a
+	// variant of that module for which bazel-handling actually took place.
+	// A module being in the "disabled" list indicates that there is a variant of
+	// that module for which bazel-handling was denied.
 	mixedBuildsLock           sync.Mutex
 	mixedBuildEnabledModules  map[string]struct{}
 	mixedBuildDisabledModules map[string]struct{}
@@ -346,7 +384,7 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(moduleListFile string, runGoTests bool, outDir, soongOutDir string, availableEnv map[string]string) (Config, error) {
+func NewConfig(moduleListFile string, buildMode SoongBuildMode, runGoTests bool, outDir, soongOutDir string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
@@ -443,8 +481,17 @@
 		config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	}
 
+	// Checking USE_BAZEL_ANALYSIS must be done here instead of in the caller, so
+	// that we can invoke IsEnvTrue (which also registers the env var as a
+	// dependency of the build).
+	// TODO(cparsons): Remove this hack once USE_BAZEL_ANALYSIS is removed.
+	if buildMode == AnalysisNoBazel && config.IsEnvTrue("USE_BAZEL_ANALYSIS") {
+		buildMode = BazelDevMode
+	}
+
+	config.BuildMode = buildMode
 	config.BazelContext, err = NewBazelContext(config)
-	config.bp2buildPackageConfig = getBp2BuildAllowList()
+	config.bp2buildPackageConfig = GetBp2BuildAllowList()
 
 	return Config{config}, err
 }
@@ -479,6 +526,12 @@
 	c.mockBpList = blueprint.MockModuleListFile
 }
 
+// Returns true if "Bazel builds" is enabled. In this mode, part of build
+// analysis is handled by Bazel.
+func (c *config) IsMixedBuildsEnabled() bool {
+	return c.BuildMode == BazelProdMode || c.BuildMode == BazelDevMode
+}
+
 func (c *config) SetAllowMissingDependencies() {
 	c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
 }
@@ -689,7 +742,7 @@
 }
 
 func (c *config) MinSupportedSdkVersion() ApiLevel {
-	return uncheckedFinalApiLevel(19)
+	return uncheckedFinalApiLevel(21)
 }
 
 func (c *config) FinalApiLevels() []ApiLevel {
diff --git a/android/defaults.go b/android/defaults.go
index 03b2efb..7906e94 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -448,7 +448,7 @@
 	}
 
 	for _, defaults := range defaultsList {
-		if ctx.Config().runningAsBp2Build {
+		if ctx.Config().BuildMode == Bp2build {
 			applyNamespacedVariableDefaults(defaults, ctx)
 		}
 
diff --git a/android/module.go b/android/module.go
index 1cf5955..5908233 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2374,9 +2374,6 @@
 }
 
 func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
-	if !ctx.Config().BazelContext.BazelEnabled() {
-		return nil, false
-	}
 	if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
 		if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
 			return mixedBuildMod, true
diff --git a/android/register.go b/android/register.go
index 5832b1b..d4ce5f1 100644
--- a/android/register.go
+++ b/android/register.go
@@ -164,10 +164,6 @@
 	return ctx
 }
 
-func (ctx *Context) SetRunningAsBp2build() {
-	ctx.config.runningAsBp2Build = true
-}
-
 // RegisterForBazelConversion registers an alternate shadow pipeline of
 // singletons, module types and mutators to register for converting Blueprint
 // files to semantically equivalent BUILD files.
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index b25f248..cd36ae0 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -382,7 +382,7 @@
 		defer r.Close()
 
 		mtDef, errs := soongconfig.Parse(r, from)
-		if ctx.Config().runningAsBp2Build {
+		if ctx.Config().BuildMode == Bp2build {
 			ctx.Config().Bp2buildSoongConfigDefinitions.AddVars(*mtDef)
 		}
 
@@ -398,7 +398,7 @@
 		for name, moduleType := range mtDef.ModuleTypes {
 			factory := globalModuleTypes[moduleType.BaseModuleType]
 			if factory != nil {
-				factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
+				factories[name] = configModuleFactory(factory, moduleType, ctx.Config().BuildMode == Bp2build)
 			} else {
 				reportErrors(ctx, from,
 					fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
diff --git a/android/test_config.go b/android/test_config.go
index f36e8ba..0f88d51 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -118,6 +118,11 @@
 	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
 }
 
+func modifyTestConfigForMuslArm64HostCross(config Config) {
+	config.Targets[LinuxMusl] = append(config.Targets[LinuxMusl],
+		Target{config.BuildOS, Arch{ArchType: Arm64}, NativeBridgeDisabled, "", "", true})
+}
+
 // TestArchConfig returns a Config object suitable for using for tests that
 // need to run the arch mutator.
 func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
diff --git a/android/testing.go b/android/testing.go
index b4429ca..ef5e5a9 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -457,7 +457,7 @@
 
 // RegisterForBazelConversion prepares a test context for bp2build conversion.
 func (ctx *TestContext) RegisterForBazelConversion() {
-	ctx.SetRunningAsBp2build()
+	ctx.config.BuildMode = Bp2build
 	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
 }
 
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index d32e619..5cee6ac 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -134,13 +134,13 @@
 sharedLibraries = []
 rootSharedLibraries = []
 
-shared_info_tag = "@_builtins//:common/cc/experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
+shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo"
+
 if shared_info_tag in providers(target):
   shared_info = providers(target)[shared_info_tag]
-  for lib in shared_info.linker_input.libraries:
-    path = lib.dynamic_library.path
-    rootSharedLibraries += [path]
-    sharedLibraries.append(path)
+  path = shared_info.output_file.path
+  sharedLibraries.append(path)
+  rootSharedLibraries += [path]
 else:
   for linker_input in linker_inputs:
     for library in linker_input.libraries:
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index bb59c63..5846f83 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -390,3 +390,24 @@
 		},
 	})
 }
+
+func TestCcLibraryHeadersWholeStaticLibsReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_headers whole_static_libs is reexported",
+		ModuleTypeUnderTest:        "cc_library_headers",
+		ModuleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		whole_static_libs: ["foo_export"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		ExpectedBazelTargets: []string{
+			makeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index ba93439..5954098 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -983,7 +983,7 @@
 
 func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
 	label := android.BazelModuleLabel(ctx, m)
-	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(m.Name()) {
 		label += "_bp2build_cc_library_static"
 	}
 	return label
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 792a8e0..f025700 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4145,7 +4145,7 @@
 		"${config.ArmArmv7ANeonCflags}",
 		"${config.ArmGenericCflags}",
 		"-target",
-		"armv7a-linux-androideabi20",
+		"armv7a-linux-androideabi21",
 	}
 
 	expectedIncludes := []string{
@@ -4176,7 +4176,6 @@
 		"external/foo/lib32",
 		"external/foo/libandroid_arm",
 		"defaults/cc/common/ndk_libc++_shared",
-		"defaults/cc/common/ndk_libandroid_support",
 	}
 
 	conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
@@ -4269,20 +4268,20 @@
 				},
 			},
 			stl: "libc++",
-			sdk_version: "20",
+			sdk_version: "minimum",
 		}
 
 		cc_library_headers {
 			name: "libheader1",
 			export_include_dirs: ["libheader1"],
-			sdk_version: "20",
+			sdk_version: "minimum",
 			stl: "none",
 		}
 
 		cc_library_headers {
 			name: "libheader2",
 			export_include_dirs: ["libheader2"],
-			sdk_version: "20",
+			sdk_version: "minimum",
 			stl: "none",
 		}
 	`, tc.src)
@@ -4306,7 +4305,7 @@
 			cc_library {
 				name: "%s",
 				export_include_dirs: ["%s"],
-				sdk_version: "20",
+				sdk_version: "minimum",
 				stl: "none",
 			}
 		`, lib, lib)
diff --git a/cc/library.go b/cc/library.go
index 621b58c..41dca01 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -282,7 +282,7 @@
 	// For some cc_library modules, their static variants are ready to be
 	// converted, but not their shared variants. For these modules, delegate to
 	// the cc_library_static bp2build converter temporarily instead.
-	if android.GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
+	if android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
 		sharedOrStaticLibraryBp2Build(ctx, m, true)
 		return
 	}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 970d8d1..a683f58 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -126,6 +126,7 @@
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &baseAttributes.includes)
 	linkerAttrs := baseAttributes.linkerAttributes
 	(&linkerAttrs.deps).Append(linkerAttrs.dynamicDeps)
+	(&linkerAttrs.deps).Append(linkerAttrs.wholeArchiveDeps)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
 		Export_includes:          exportedIncludes.Includes,
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index a2d4503..867c36c 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -51,6 +51,9 @@
 	// symbols, etc), default true.
 	Check_elf_files *bool
 
+	// if set, add an extra objcopy --prefix-symbols= step
+	Prefix_symbols *string
+
 	// Optionally provide an import library if this is a Windows PE DLL prebuilt.
 	// This is needed only if this library is linked by other modules in build time.
 	// Only makes sense for the Windows target.
@@ -130,6 +133,13 @@
 
 		in := android.PathForModuleSrc(ctx, srcs[0])
 
+		if String(p.prebuiltLinker.properties.Prefix_symbols) != "" {
+			prefixed := android.PathForModuleOut(ctx, "prefixed", srcs[0])
+			transformBinaryPrefixSymbols(ctx, String(p.prebuiltLinker.properties.Prefix_symbols),
+				in, flagsToBuilderFlags(flags), prefixed)
+			in = prefixed
+		}
+
 		if p.static() {
 			depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
 			ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 53422cd..6930943 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -121,7 +121,21 @@
 }
 
 func newConfig(availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, runGoTests, outDir, soongOutDir, availableEnv)
+	var buildMode android.SoongBuildMode
+
+	if bp2buildMarker != "" {
+		buildMode = android.Bp2build
+	} else if bazelQueryViewDir != "" {
+		buildMode = android.GenerateQueryView
+	} else if moduleGraphFile != "" {
+		buildMode = android.GenerateModuleGraph
+	} else if docFile != "" {
+		buildMode = android.GenerateDocFile
+	} else {
+		buildMode = android.AnalysisNoBazel
+	}
+
+	configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, buildMode, runGoTests, outDir, soongOutDir, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -221,52 +235,40 @@
 // or the actual Soong build for the build.ninja file. Returns the top level
 // output file of the specific activity.
 func doChosenActivity(ctx *android.Context, configuration android.Config, extraNinjaDeps []string, logDir string) string {
-	mixedModeBuild := configuration.BazelContext.BazelEnabled()
-	generateBazelWorkspace := bp2buildMarker != ""
-	generateQueryView := bazelQueryViewDir != ""
-	generateModuleGraphFile := moduleGraphFile != ""
-	generateDocFile := docFile != ""
-
-	if generateBazelWorkspace {
+	if configuration.BuildMode == android.Bp2build {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
 		runBp2Build(configuration, extraNinjaDeps)
 		return bp2buildMarker
-	}
-
-	blueprintArgs := cmdlineArgs
-
-	if mixedModeBuild {
+	} else if configuration.IsMixedBuildsEnabled() {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
 	} else {
 		var stopBefore bootstrap.StopBefore
-		if generateModuleGraphFile {
+		if configuration.BuildMode == android.GenerateModuleGraph {
 			stopBefore = bootstrap.StopBeforeWriteNinja
-		} else if generateQueryView {
-			stopBefore = bootstrap.StopBeforePrepareBuildActions
-		} else if generateDocFile {
+		} else if configuration.BuildMode == android.GenerateQueryView || configuration.BuildMode == android.GenerateDocFile {
 			stopBefore = bootstrap.StopBeforePrepareBuildActions
 		} else {
 			stopBefore = bootstrap.DoEverything
 		}
 
-		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
+		ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, stopBefore, ctx.Context, configuration)
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 		globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
 		ninjaDeps = append(ninjaDeps, globListFiles...)
 
 		// Convert the Soong module graph into Bazel BUILD files.
-		if generateQueryView {
+		if configuration.BuildMode == android.GenerateQueryView {
 			queryviewMarkerFile := bazelQueryViewDir + ".marker"
 			runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
 			writeDepFile(queryviewMarkerFile, *ctx.EventHandler, ninjaDeps)
 			return queryviewMarkerFile
-		} else if generateModuleGraphFile {
+		} else if configuration.BuildMode == android.GenerateModuleGraph {
 			writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
 			writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps)
 			return moduleGraphFile
-		} else if generateDocFile {
+		} else if configuration.BuildMode == android.GenerateDocFile {
 			// TODO: we could make writeDocs() return the list of documentation files
 			// written and add them to the .d file. Then soong_docs would be re-run
 			// whenever one is deleted.
@@ -491,11 +493,6 @@
 		// conversion for Bazel conversion.
 		bp2buildCtx := android.NewContext(configuration)
 
-		// Soong internals like LoadHooks behave differently when running as
-		// bp2build. This is the bit to differentiate between Soong-as-Soong and
-		// Soong-as-bp2build.
-		bp2buildCtx.SetRunningAsBp2build()
-
 		// Propagate "allow misssing dependencies" bit. This is normally set in
 		// newContext(), but we create bp2buildCtx without calling that method.
 		bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 513c606..bfd5cf8 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -84,12 +84,36 @@
     ],
 }
 
+// Same as core-current-stubs-for-system-modules, but android annotations are
+// stripped.
+java_library {
+    name: "core-current-stubs-for-system-modules-no-annotations",
+    visibility: ["//development/sdk"],
+    static_libs: [
+        "core-current-stubs-for-system-modules",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    dists: [
+        {
+            // Legacy dist location for the public file.
+            dest: "core-for-system-modules-no-annotations.jar",
+            targets: dist_targets,
+        },
+        {
+            dest: "system-modules/public/core-for-system-modules-no-annotations.jar",
+            targets: dist_targets,
+        },
+    ],
+    jarjar_rules: "jarjar-strip-annotations-rules.txt",
+}
+
 // Used when compiling higher-level code against core.current.stubs.
 java_system_modules {
     name: "core-public-stubs-system-modules",
     visibility: ["//visibility:public"],
     libs: [
-        "core-current-stubs-for-system-modules",
+        "core-current-stubs-for-system-modules-no-annotations",
     ],
 }
 
diff --git a/ui/build/config.go b/ui/build/config.go
index cbf1986..e140867 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1326,6 +1326,10 @@
 	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja")
 }
 
+func (c *configImpl) SoongVarsFile() string {
+	return filepath.Join(c.SoongOutDir(), "soong.variables")
+}
+
 func (c *configImpl) SoongNinjaFile() string {
 	return filepath.Join(c.SoongOutDir(), "build.ninja")
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index f56964c..adc56ac 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -141,7 +141,6 @@
 	"PLATFORM_VERSION",
 	"TARGET_PRODUCT",
 	"TARGET_BUILD_VARIANT",
-	"TARGET_BUILD_TYPE",
 	"TARGET_BUILD_APPS",
 	"TARGET_BUILD_UNBUNDLED",
 	"TARGET_ARCH",
@@ -150,18 +149,11 @@
 	"TARGET_2ND_ARCH",
 	"TARGET_2ND_ARCH_VARIANT",
 	"TARGET_2ND_CPU_VARIANT",
-	"HOST_ARCH",
-	"HOST_2ND_ARCH",
 	"HOST_OS",
 	"HOST_OS_EXTRA",
 	"HOST_CROSS_OS",
-	"HOST_CROSS_ARCH",
-	"HOST_CROSS_2ND_ARCH",
-	"HOST_BUILD_TYPE",
 	"BUILD_ID",
 	"OUT_DIR",
-	"AUX_OS_VARIANT_LIST",
-	"PRODUCT_SOONG_NAMESPACES",
 	"SOONG_SDK_SNAPSHOT_PREFER",
 	"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE",
 	"SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR",
@@ -236,6 +228,13 @@
 
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
+		"TARGET_BUILD_TYPE",
+		"HOST_ARCH",
+		"HOST_2ND_ARCH",
+		"HOST_CROSS_ARCH",
+		"HOST_CROSS_2ND_ARCH",
+		"HOST_BUILD_TYPE",
+		"PRODUCT_SOONG_NAMESPACES",
 
 		"DEFAULT_WARNING_BUILD_MODULE_TYPES",
 		"DEFAULT_ERROR_BUILD_MODULE_TYPES",
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 519506e..3920ddd 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -502,6 +502,7 @@
 	}
 
 	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+	distFile(ctx, config, config.SoongVarsFile(), "soong")
 
 	if !config.SkipKati() {
 		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")