Merge "Add min_sdk_version to java_import."
diff --git a/android/bazel.go b/android/bazel.go
index cc02152..51ff3cb 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -130,48 +130,48 @@
 
 	// Per-module denylist to always opt modules out.
 	bp2buildModuleDoNotConvertList = []string{
-		"generated_android_ids",
-		"libBionicBenchmarksUtils",
-		"libbionic_spawn_benchmark",
-		"libc_jemalloc_wrapper",
-		"libc_bootstrap",
-		"libc_init_static",
-		"libc_init_dynamic",
-		"libc_tzcode",
-		"libc_freebsd",
-		"libc_freebsd_large_stack",
-		"libc_netbsd",
-		"libc_openbsd_ndk",
-		"libc_openbsd_large_stack",
-		"libc_openbsd",
-		"libc_gdtoa",
-		"libc_fortify",
-		"libc_bionic",
-		"libc_bionic_ndk",
-		"libc_bionic_systrace",
-		"libc_pthread",
-		"libc_syscalls",
-		"libc_aeabi",
-		"libc_ndk",
-		"libc_nopthread",
-		"libc_common",
-		"libc_static_dispatch",
-		"libc_dynamic_dispatch",
-		"libc_common_static",
-		"libc_common_shared",
-		"libc_unwind_static",
-		"libc_nomalloc",
-		"libasync_safe",
-		"libc_malloc_debug_backtrace",
-		"libsystemproperties",
-		"libdl_static",
-		"liblinker_main",
-		"liblinker_malloc",
-		"liblinker_debuggerd_stub",
-		"libbionic_tests_headers_posix",
-		"libc_dns",
-		"note_memtag_heap_async",
-		"note_memtag_heap_sync",
+		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static
+		"libbionic_spawn_benchmark",     // ruperts@, cc_library_static, depends on //system/libbase
+		"libc_jemalloc_wrapper",         // ruperts@, cc_library_static, depends on //external/jemalloc_new
+		"libc_bootstrap",                // ruperts@, cc_library_static
+		"libc_init_static",              // ruperts@, cc_library_static
+		"libc_init_dynamic",             // ruperts@, cc_library_static
+		"libc_tzcode",                   // ruperts@, cc_library_static
+		"libc_freebsd",                  // ruperts@, cc_library_static
+		"libc_freebsd_large_stack",      // ruperts@, cc_library_static
+		"libc_netbsd",                   // ruperts@, cc_library_static
+		"libc_openbsd_ndk",              // ruperts@, cc_library_static
+		"libc_openbsd_large_stack",      // ruperts@, cc_library_static
+		"libc_openbsd",                  // ruperts@, cc_library_static
+		"libc_gdtoa",                    // ruperts@, cc_library_static
+		"libc_fortify",                  // ruperts@, cc_library_static
+		"libc_bionic",                   // ruperts@, cc_library_static
+		"libc_bionic_ndk",               // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
+		"libc_bionic_systrace",          // ruperts@, cc_library_static
+		"libc_pthread",                  // ruperts@, cc_library_static
+		"libc_syscalls",                 // ruperts@, cc_library_static
+		"libc_aeabi",                    // ruperts@, cc_library_static
+		"libc_ndk",                      // ruperts@, cc_library_static, depends on //bionic/libm:libm
+		"libc_nopthread",                // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
+		"libc_common",                   // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
+		"libc_static_dispatch",          // ruperts@, cc_library_static
+		"libc_dynamic_dispatch",         // ruperts@, cc_library_static
+		"libc_common_static",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+		"libc_common_shared",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+		"libc_unwind_static",            // ruperts@, cc_library_static
+		"libc_nomalloc",                 // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+		"libasync_safe",                 // ruperts@, cc_library_static
+		"libc_malloc_debug_backtrace",   // ruperts@, cc_library_static, depends on //system/libbase
+		"libsystemproperties",           // ruperts@, cc_library_static, depends on //system/core/property_service/libpropertyinfoparser
+		"libdl_static",                  // ruperts@, cc_library_static
+		"liblinker_main",                // ruperts@, cc_library_static, depends on //system/libbase
+		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
+		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
+		"libbionic_tests_headers_posix", // ruperts@, cc_library_static
+		"libc_dns",                      // ruperts@, cc_library_static
+		"generated_android_ids",         // cparsons@, genrule
+		"note_memtag_heap_async",        // cparsons@, cc_library_static
+		"note_memtag_heap_sync",         // cparsons@, cc_library_static
 	}
 
 	// Used for quicker lookups
diff --git a/android/fixture.go b/android/fixture.go
index 303c95c..5fc668a 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -26,16 +26,9 @@
 // Fixture
 // =======
 // These determine the environment within which a test can be run. Fixtures are mutable and are
-// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
-// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
-// instances to it to modify the environment.
-//
-// FixtureFactory (deprecated)
-// ===========================
-// These are responsible for creating fixtures. Factories are immutable and are intended to be
-// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
-// preparers that prepare a fixture for running a test. Factories can also be used to create other
-// factories by extending them with additional fixture preparers.
+// created and mutated by FixturePreparer instances. They are created by first creating a base
+// Fixture (which is essentially empty) and then applying FixturePreparer instances to it to modify
+// the environment.
 //
 // FixturePreparer
 // ===============
@@ -169,77 +162,6 @@
 //    PrepareForApex,
 // )
 //
-// // FixtureFactory instances have been deprecated, this remains for informational purposes to
-// // help explain some of the existing code but will be removed along with FixtureFactory.
-//
-// var javaFixtureFactory = android.NewFixtureFactory(
-//    PrepareForIntegrationTestWithJava,
-//    FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-//      ctx.RegisterModuleType("test_module", testModule)
-//    }),
-//    javaMockFS.AddToFixture(),
-//    ...
-// }
-//
-// func TestJavaStuff(t *testing.T) {
-//   result := javaFixtureFactory.RunTest(t,
-//       android.FixtureWithRootAndroidBp(`java_library {....}`),
-//       android.MockFS{...}.AddToFixture(),
-//   )
-//   ... test result ...
-// }
-//
-// package cc
-// var PrepareForTestWithCC = GroupFixturePreparers(
-//    android.PrepareForArchMutator,
-//    android.prepareForPrebuilts,
-//    FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
-//    ...
-// )
-//
-// package apex
-//
-// var PrepareForApex = GroupFixturePreparers(
-//    ...
-// )
-//
-// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
-// android.PrepareForArchMutator) will be automatically deduped.
-//
-// var apexFixtureFactory = android.NewFixtureFactory(
-//    PrepareForJava,
-//    PrepareForCC,
-//    PrepareForApex,
-// )
-
-// Factory for Fixture objects.
-//
-// This is configured with a set of FixturePreparer objects that are used to
-// initialize each Fixture instance this creates.
-//
-// deprecated: Use FixturePreparer instead.
-type FixtureFactory interface {
-	FixturePreparer
-}
-
-// Create a new FixtureFactory that will apply the supplied preparers.
-//
-// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
-// the package level setUp method. It has to be a pointer to the variable as the variable will not
-// have been initialized at the time the factory is created. If it is nil then a test specific
-// temporary directory will be created instead.
-//
-// deprecated: The functionality provided by FixtureFactory will be merged into FixturePreparer
-func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
-	f := &fixtureFactory{
-		buildDirSupplier: buildDirSupplier,
-		compositeFixturePreparer: compositeFixturePreparer{
-			preparers: dedupAndFlattenPreparers(nil, preparers),
-		},
-	}
-	f.initBaseFixturePreparer(f)
-	return f
-}
 
 // A set of mock files to add to the mock file system.
 type MockFS map[string][]byte
@@ -445,15 +367,6 @@
 	// Return the flattened and deduped list of simpleFixturePreparer pointers.
 	list() []*simpleFixturePreparer
 
-	// Creates a copy of this instance and adds some additional preparers.
-	//
-	// Before the preparers are used they are combined with the preparers provided when the factory
-	// was created, any groups of preparers are flattened, and the list is deduped so that each
-	// preparer is only used once. See the file documentation in android/fixture.go for more details.
-	//
-	// deprecated: Use GroupFixturePreparers() instead.
-	Extend(preparers ...FixturePreparer) FixturePreparer
-
 	// Create a Fixture.
 	Fixture(t *testing.T) Fixture
 
@@ -734,17 +647,12 @@
 	b.self = self
 }
 
-func (b *baseFixturePreparer) Extend(preparers ...FixturePreparer) FixturePreparer {
-	all := dedupAndFlattenPreparers(b.self.list(), preparers)
-	return newFixturePreparer(all)
-}
-
 func (b *baseFixturePreparer) Fixture(t *testing.T) Fixture {
 	return createFixture(t, t.TempDir(), b.self.list())
 }
 
 func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer {
-	return b.self.Extend(newSimpleFixturePreparer(func(fixture *fixture) {
+	return GroupFixturePreparers(b.self, newSimpleFixturePreparer(func(fixture *fixture) {
 		fixture.errorHandler = errorHandler
 	}))
 }
@@ -782,46 +690,6 @@
 	return fixture.RunTest()
 }
 
-var _ FixtureFactory = (*fixtureFactory)(nil)
-
-type fixtureFactory struct {
-	compositeFixturePreparer
-
-	buildDirSupplier *string
-}
-
-// Override to preserve the buildDirSupplier.
-func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixturePreparer {
-	// If there is no buildDirSupplier then just use the default implementation.
-	if f.buildDirSupplier == nil {
-		return f.baseFixturePreparer.Extend(preparers...)
-	}
-
-	all := dedupAndFlattenPreparers(f.preparers, preparers)
-
-	// Create a new factory which uses the same buildDirSupplier as the previous one.
-	extendedFactory := &fixtureFactory{
-		buildDirSupplier: f.buildDirSupplier,
-		compositeFixturePreparer: compositeFixturePreparer{
-			preparers: all,
-		},
-	}
-	extendedFactory.initBaseFixturePreparer(extendedFactory)
-	return extendedFactory
-}
-
-func (f *fixtureFactory) Fixture(t *testing.T) Fixture {
-	// If there is no buildDirSupplier then just use the default implementation.
-	if f.buildDirSupplier == nil {
-		return f.baseFixturePreparer.Fixture(t)
-	}
-
-	// Retrieve the buildDir from the supplier.
-	buildDir := *f.buildDirSupplier
-
-	return createFixture(t, buildDir, f.preparers)
-}
-
 type fixture struct {
 	// The preparers used to create this fixture.
 	preparers []*simpleFixturePreparer
@@ -936,10 +804,10 @@
 // that produced this result.
 //
 // e.g. assuming that this result was created by running:
-//     factory.Extend(preparer1, preparer2).RunTest(t, preparer3, preparer4)
+//     GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t)
 //
 // Then this method will be equivalent to running:
-//     GroupFixturePreparers(preparer1, preparer2, preparer3, preparer4)
+//     GroupFixturePreparers(preparer1, preparer2, preparer3)
 //
 // This is intended for use by tests whose output is Android.bp files to verify that those files
 // are valid, e.g. tests of the snapshots produced by the sdk module type.
diff --git a/android/fixture_test.go b/android/fixture_test.go
index 8f04715..5b810e0 100644
--- a/android/fixture_test.go
+++ b/android/fixture_test.go
@@ -41,7 +41,7 @@
 
 	group := GroupFixturePreparers(preparer1, preparer2, preparer1, preparer1Then2)
 
-	extension := group.Extend(preparer4, preparer2)
+	extension := GroupFixturePreparers(group, preparer4, preparer2)
 
 	GroupFixturePreparers(extension, preparer1, preparer2, preparer2Then1, preparer3).Fixture(t)
 
diff --git a/android/mutator.go b/android/mutator.go
index a06e0ee..e25e2e8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -202,7 +202,7 @@
 	RegisterPrebuiltsPostDepsMutators,
 	RegisterVisibilityRuleEnforcer,
 	RegisterLicensesDependencyChecker,
-	RegisterNeverallowMutator,
+	registerNeverallowMutator,
 	RegisterOverridePostDepsMutators,
 }
 
diff --git a/android/neverallow.go b/android/neverallow.go
index 7455e6a..a385bbc 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -42,7 +42,7 @@
 //     counts as a match
 // - it has none of the "Without" properties matched (same rules as above)
 
-func RegisterNeverallowMutator(ctx RegisterMutatorsContext) {
+func registerNeverallowMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
 }
 
@@ -661,6 +661,22 @@
 // Overrides the default neverallow rules for the supplied config.
 //
 // For testing only.
-func SetTestNeverallowRules(config Config, testRules []Rule) {
+func setTestNeverallowRules(config Config, testRules []Rule) {
 	config.Once(neverallowRulesKey, func() interface{} { return testRules })
 }
+
+// Prepares for a test by setting neverallow rules and enabling the mutator.
+//
+// If the supplied rules are nil then the default rules are used.
+func PrepareForTestWithNeverallowRules(testRules []Rule) FixturePreparer {
+	return GroupFixturePreparers(
+		FixtureModifyConfig(func(config Config) {
+			if testRules != nil {
+				setTestNeverallowRules(config, testRules)
+			}
+		}),
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.PostDepsMutators(registerNeverallowMutator)
+		}),
+	)
+}
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index de0197a..268346a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -292,7 +292,6 @@
 		ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
-		ctx.PostDepsMutators(RegisterNeverallowMutator)
 	}),
 )
 
@@ -301,12 +300,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			GroupFixturePreparers(
 				prepareForNeverAllowTest,
-				FixtureModifyConfig(func(config Config) {
-					// If the test has its own rules then use them instead of the default ones.
-					if test.rules != nil {
-						SetTestNeverallowRules(config, test.rules)
-					}
-				}),
+				PrepareForTestWithNeverallowRules(test.rules),
 				test.fs.AddToFixture(),
 			).
 				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a220a06..98b40fd 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -22,6 +22,7 @@
 	"reflect"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -49,18 +50,28 @@
 
 func testApexError(t *testing.T, pattern, bp string, preparers ...android.FixturePreparer) {
 	t.Helper()
-	apexFixtureFactory.Extend(preparers...).
+	android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.GroupFixturePreparers(preparers...),
+	).
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithBp(t, bp)
 }
 
 func testApex(t *testing.T, bp string, preparers ...android.FixturePreparer) *android.TestContext {
 	t.Helper()
-	factory := apexFixtureFactory.Extend(preparers...)
+
+	optionalBpPreparer := android.NullFixturePreparer
 	if bp != "" {
-		factory = factory.Extend(android.FixtureWithRootAndroidBp(bp))
+		optionalBpPreparer = android.FixtureWithRootAndroidBp(bp)
 	}
-	result := factory.RunTest(t)
+
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.GroupFixturePreparers(preparers...),
+		optionalBpPreparer,
+	).RunTest(t)
+
 	return result.TestContext
 }
 
@@ -114,8 +125,16 @@
 	},
 )
 
-var apexFixtureFactory = android.NewFixtureFactory(
-	nil,
+// Legacy preparer used for running tests within the apex package.
+//
+// This includes everything that was needed to run any test in the apex package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testApex...() methods should avoid using this and instead use the
+// various preparers directly, using android.GroupFixturePreparers(...) to group them when
+// necessary.
+//
+// deprecated
+var prepareForApexTest = android.GroupFixturePreparers(
 	// General preparers in alphabetical order as test infrastructure will enforce correct
 	// registration order.
 	android.PrepareForTestWithAndroidBuildComponents,
@@ -801,7 +820,7 @@
 	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stubs for mylib2
-	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3/mylib2.so")
+	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
 	// ... and not linking to the non-stub (impl) variant of mylib2
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
@@ -1279,15 +1298,15 @@
 			name:          "unspecified version links to the latest",
 			minSdkVersion: "",
 			apexVariant:   "apex10000",
-			shouldLink:    "30",
-			shouldNotLink: []string{"29"},
+			shouldLink:    "current",
+			shouldNotLink: []string{"29", "30"},
 		},
 		{
 			name:          "always use the latest",
 			minSdkVersion: "min_sdk_version: \"29\",",
 			apexVariant:   "apex29",
-			shouldLink:    "30",
-			shouldNotLink: []string{"29"},
+			shouldLink:    "current",
+			shouldNotLink: []string{"29", "30"},
 		},
 	}
 	for _, tc := range testcases {
@@ -1354,7 +1373,11 @@
 			}
 
 			mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
-			ensureContains(t, mylibCFlags, "__LIBBAR_API__="+tc.shouldLink)
+			ver := tc.shouldLink
+			if tc.shouldLink == "current" {
+				ver = strconv.Itoa(android.FutureApiLevelInt)
+			}
+			ensureContains(t, mylibCFlags, "__LIBBAR_API__="+ver)
 		})
 	}
 }
@@ -1416,12 +1439,12 @@
 
 	// For dependency to libc
 	// Ensure that mylib is linking with the latest version of stubs
-	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29/libc.so")
+	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_current/libc.so")
 	// ... and not linking to the non-stub (impl) variant
 	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared/libc.so")
 	// ... Cflags from stub is correctly exported to mylib
-	ensureContains(t, mylibCFlags, "__LIBC_API__=29")
-	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")
+	ensureContains(t, mylibCFlags, "__LIBC_API__=10000")
+	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=10000")
 
 	// For dependency to libm
 	// Ensure that mylib is linking with the non-stub (impl) variant
@@ -1527,12 +1550,14 @@
 	}
 	// platform liba is linked to non-stub version
 	expectLink("liba", "shared", "libz", "shared")
-	// liba in myapex is linked to #30
-	expectLink("liba", "shared_apex29", "libz", "shared_30")
+	// liba in myapex is linked to current
+	expectLink("liba", "shared_apex29", "libz", "shared_current")
+	expectNoLink("liba", "shared_apex29", "libz", "shared_30")
 	expectNoLink("liba", "shared_apex29", "libz", "shared_28")
 	expectNoLink("liba", "shared_apex29", "libz", "shared")
-	// liba in otherapex is linked to #30
-	expectLink("liba", "shared_apex30", "libz", "shared_30")
+	// liba in otherapex is linked to current
+	expectLink("liba", "shared_apex30", "libz", "shared_current")
+	expectNoLink("liba", "shared_apex30", "libz", "shared_30")
 	expectNoLink("liba", "shared_apex30", "libz", "shared_28")
 	expectNoLink("liba", "shared_apex30", "libz", "shared")
 }
@@ -1583,7 +1608,8 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_apex10000", "libz", "shared_R")
+	expectLink("libx", "shared_apex10000", "libz", "shared_current")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared_R")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared_29")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
@@ -1629,8 +1655,9 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_apex10000", "libz", "shared_2")
+	expectLink("libx", "shared_apex10000", "libz", "shared_current")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared_1")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared_2")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
@@ -1677,7 +1704,8 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libz", "shared", "libx", "shared_2")
+	expectLink("libz", "shared", "libx", "shared_current")
+	expectNoLink("libz", "shared", "libx", "shared_2")
 	expectNoLink("libz", "shared", "libz", "shared_1")
 	expectNoLink("libz", "shared", "libz", "shared")
 }
@@ -1724,7 +1752,7 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_30")
+	expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_current")
 }
 
 func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
@@ -2106,7 +2134,7 @@
 			private_key: "testkey.pem",
 		}
 
-		// mylib in myapex will link to mylib2#30
+		// mylib in myapex will link to mylib2#current
 		// mylib in otherapex will link to mylib2(non-stub) in otherapex as well
 		cc_library {
 			name: "mylib",
@@ -2140,7 +2168,7 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("mylib", "shared_apex29", "mylib2", "shared_30")
+	expectLink("mylib", "shared_apex29", "mylib2", "shared_current")
 	expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30")
 }
 
@@ -2208,10 +2236,10 @@
 		}
 	`, withSAsActiveCodeNames)
 
-	// ensure libfoo is linked with "S" version of libbar stub
+	// ensure libfoo is linked with current version of libbar stub
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex10000")
 	libFlags := libfoo.Rule("ld").Args["libFlags"]
-	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_T/libbar.so")
+	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_current/libbar.so")
 }
 
 func TestFilesInSubDir(t *testing.T) {
@@ -4513,14 +4541,11 @@
 			if filepath.Base(output) == base {
 				foundLibfooJar = true
 				buildRule := s.Output(output)
-				actual := android.NormalizePathForTesting(buildRule.Input)
-				if actual != bootDexJarPath {
-					t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath)
-				}
+				android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
 			}
 		}
 		if !foundLibfooJar {
-			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs")
+			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().BuildDir(), s.AllOutputs()))
 		}
 	}
 
@@ -4562,8 +4587,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -4669,8 +4694,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -4736,8 +4761,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -4805,8 +4830,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -5912,9 +5937,10 @@
 }
 
 func TestCompatConfig(t *testing.T) {
-	result := apexFixtureFactory.
-		Extend(java.PrepareForTestWithPlatformCompatConfig).
-		RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithPlatformCompatConfig,
+	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -6457,68 +6483,46 @@
 func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
 	t.Helper()
 
-	bp += cc.GatherRequiredDepsForTest(android.Android)
-	bp += java.GatherRequiredDepsForTest()
-
-	fs := map[string][]byte{
-		"a.java":                             nil,
-		"a.jar":                              nil,
-		"build/make/target/product/security": nil,
-		"apex_manifest.json":                 nil,
-		"AndroidManifest.xml":                nil,
+	fs := android.MockFS{
+		"a.java":              nil,
+		"a.jar":               nil,
+		"apex_manifest.json":  nil,
+		"AndroidManifest.xml": nil,
 		"system/sepolicy/apex/myapex-file_contexts":                  nil,
 		"system/sepolicy/apex/some-updatable-apex-file_contexts":     nil,
 		"system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
 		"system/sepolicy/apex/com.android.art.debug-file_contexts":   nil,
 		"framework/aidl/a.aidl":                                      nil,
 	}
-	cc.GatherRequiredFilesForTest(fs)
 
-	for k, v := range filesForSdkLibrary {
-		fs[k] = v
-	}
-	config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
-
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("apex", BundleFactory)
-	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterComponentsMutator)
-	android.RegisterPrebuiltMutators(ctx)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterHiddenApiSingletonComponents(ctx)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-
-	ctx.Register()
-
-	pathCtx := android.PathContextForTesting(config)
-	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	transformDexpreoptConfig(dexpreoptConfig)
-	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
-	// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
-	// product variables.
-	config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
-	config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
-
-	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	if errmsg == "" {
-		android.FailIfErrored(t, errs)
-	} else if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, errmsg, errs)
-	} else {
-		t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
+	errorHandler := android.FixtureExpectsNoErrors
+	if errmsg != "" {
+		errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
 	}
 
-	return ctx
+	result := android.GroupFixturePreparers(
+		cc.PrepareForTestWithCcDefaultModules,
+		java.PrepareForTestWithHiddenApiBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureModifyConfig(func(config android.Config) {
+			pathCtx := android.PathContextForTesting(config)
+			dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
+			transformDexpreoptConfig(dexpreoptConfig)
+			dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
+
+			// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
+			// product variables.
+			config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
+			config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
+		}),
+		fs.AddToFixture(),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
+
+	return result.TestContext
 }
 
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
@@ -6692,45 +6696,33 @@
 		public_key: "testkey.avbpubkey",
 		private_key: "testkey.pem",
 	}`
-	fs := map[string][]byte{
+	fs := android.MockFS{
 		"lib1/src/A.java": nil,
 		"lib2/src/B.java": nil,
 		"system/sepolicy/apex/myapex-file_contexts": nil,
 	}
 
-	config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
-	android.SetTestNeverallowRules(config, rules)
-	updatableBootJars := make([]string, 0, len(apexBootJars))
-	for _, apexBootJar := range apexBootJars {
-		updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+	errorHandler := android.FixtureExpectsNoErrors
+	if errmsg != "" {
+		errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
 	}
-	config.TestProductVariables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
 
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("apex", BundleFactory)
-	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterRequiredBuildComponentsForTest(ctx)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-	ctx.PostDepsMutators(android.RegisterNeverallowMutator)
-
-	ctx.Register()
-
-	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	if errmsg == "" {
-		android.FailIfErrored(t, errs)
-	} else if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, errmsg, errs)
-		return
-	} else {
-		t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
-	}
+	android.GroupFixturePreparers(
+		android.PrepareForTestWithAndroidBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		PrepareForTestWithApexBuildComponents,
+		android.PrepareForTestWithNeverallowRules(rules),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			updatableBootJars := make([]string, 0, len(apexBootJars))
+			for _, apexBootJar := range apexBootJars {
+				updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+			}
+			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
+		}),
+		fs.AddToFixture(),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
 }
 
 func TestApexPermittedPackagesRules(t *testing.T) {
@@ -7462,7 +7454,7 @@
 							t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
 						}
 						cflags := entry.mkEntries.EntryMap["LOCAL_EXPORT_CFLAGS"]
-						expected := "-D__STUBLIB_API__=1"
+						expected := "-D__STUBLIB_API__=10000"
 						if !android.InList(expected, cflags) {
 							t.Errorf("LOCAL_EXPORT_CFLAGS expected to have %q, but got %q", expected, cflags)
 						}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 19596c3..76e75da 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2672,9 +2672,11 @@
 	expected := []string{
 		"android_vendor.VER_arm64_armv8-a_shared_1",
 		"android_vendor.VER_arm64_armv8-a_shared_2",
+		"android_vendor.VER_arm64_armv8-a_shared_current",
 		"android_vendor.VER_arm64_armv8-a_shared",
 		"android_vendor.VER_arm_armv7-a-neon_shared_1",
 		"android_vendor.VER_arm_armv7-a-neon_shared_2",
+		"android_vendor.VER_arm_armv7-a-neon_shared_current",
 		"android_vendor.VER_arm_armv7-a-neon_shared",
 	}
 	checkEquals(t, "variants for llndk stubs", expected, actual)
@@ -3174,10 +3176,12 @@
 		"android_arm64_armv8-a_shared_1",
 		"android_arm64_armv8-a_shared_2",
 		"android_arm64_armv8-a_shared_3",
+		"android_arm64_armv8-a_shared_current",
 		"android_arm_armv7-a-neon_shared",
 		"android_arm_armv7-a-neon_shared_1",
 		"android_arm_armv7-a-neon_shared_2",
 		"android_arm_armv7-a-neon_shared_3",
+		"android_arm_armv7-a-neon_shared_current",
 	}
 	variantsMismatch := false
 	if len(variants) != len(expectedVariants) {
diff --git a/cc/library.go b/cc/library.go
index f459e80..091acfe 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -65,7 +65,8 @@
 		// symbols that are exported for stubs variant of this library.
 		Symbol_file *string `android:"path"`
 
-		// List versions to generate stubs libs for.
+		// List versions to generate stubs libs for. The version name "current" is always
+		// implicitly added.
 		Versions []string
 	}
 
@@ -171,6 +172,8 @@
 
 	// This variant is a stubs lib
 	BuildStubs bool `blueprint:"mutated"`
+	// This variant is the latest version
+	IsLatestVersion bool `blueprint:"mutated"`
 	// Version of the stubs lib
 	StubsVersion string `blueprint:"mutated"`
 	// List of all stubs versions associated with an implementation lib
@@ -775,7 +778,7 @@
 
 type versionedInterface interface {
 	buildStubs() bool
-	setBuildStubs()
+	setBuildStubs(isLatest bool)
 	hasStubsVariants() bool
 	setStubsVersion(string)
 	stubsVersion() string
@@ -1493,7 +1496,7 @@
 			if ctx.isVndk() && !ctx.IsVndkExt() {
 				return
 			}
-		} else if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.directlyInAnyApex() {
+		} else if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
@@ -1609,11 +1612,29 @@
 }
 
 func (library *libraryDecorator) hasStubsVariants() bool {
-	return len(library.Properties.Stubs.Versions) > 0
+	// Just having stubs.symbol_file is enough to create a stub variant. In that case
+	// the stub for the future API level is created.
+	return library.Properties.Stubs.Symbol_file != nil ||
+		len(library.Properties.Stubs.Versions) > 0
 }
 
 func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	return library.Properties.Stubs.Versions
+	if !library.hasStubsVariants() {
+		return nil
+	}
+
+	// Future API level is implicitly added if there isn't
+	vers := library.Properties.Stubs.Versions
+	if inList(android.FutureApiLevel.String(), vers) {
+		return vers
+	}
+	// In some cases, people use the raw value "10000" in the versions property.
+	// We shouldn't add the future API level in that case, otherwise there will
+	// be two identical versions.
+	if inList(strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt()), vers) {
+		return vers
+	}
+	return append(vers, android.FutureApiLevel.String())
 }
 
 func (library *libraryDecorator) setStubsVersion(version string) {
@@ -1624,8 +1645,9 @@
 	return library.MutatedProperties.StubsVersion
 }
 
-func (library *libraryDecorator) setBuildStubs() {
+func (library *libraryDecorator) setBuildStubs(isLatest bool) {
 	library.MutatedProperties.BuildStubs = true
+	library.MutatedProperties.IsLatestVersion = isLatest
 }
 
 func (library *libraryDecorator) setAllStubsVersions(versions []string) {
@@ -1637,8 +1659,7 @@
 }
 
 func (library *libraryDecorator) isLatestStubVersion() bool {
-	versions := library.Properties.Stubs.Versions
-	return versions[len(versions)-1] == library.stubsVersion()
+	return library.MutatedProperties.IsLatestVersion
 }
 
 func (library *libraryDecorator) availableFor(what string) bool {
@@ -1881,7 +1902,8 @@
 			c.stl = nil
 			c.Properties.PreventInstall = true
 			lib := moduleLibraryInterface(m)
-			lib.setBuildStubs()
+			isLatest := i == (len(versions) - 1)
+			lib.setBuildStubs(isLatest)
 
 			if variants[i] != "" {
 				// A non-LLNDK stubs module is hidden from make and has a dependency from the
diff --git a/cc/linker.go b/cc/linker.go
index 6d0d416..21281d2 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -599,21 +599,20 @@
 	_                   = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
 	genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
 		blueprint.RuleParams{
-			Command:     "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
-			CommandDeps: []string{"$genSortedBssSymbolsPath", "${crossCompile}nm"},
+			Command:     "CLANG_BIN=${clangBin} $genSortedBssSymbolsPath ${in} ${out}",
+			CommandDeps: []string{"$genSortedBssSymbolsPath", "${clangBin}/llvm-nm"},
 		},
-		"crossCompile")
+		"clangBin")
 )
 
 func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
-	crossCompile := gccCmd(flags.toolchain, "")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        genSortedBssSymbols,
 		Description: "generate bss symbol order " + symbolOrderingFile.Base(),
 		Output:      symbolOrderingFile,
 		Input:       in,
 		Args: map[string]string{
-			"crossCompile": crossCompile,
+			"clangBin": "${config.ClangBin}",
 		},
 	})
 	return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index fcc80a9..7bd0868 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -387,6 +387,14 @@
 	}
 	defer in.Close()
 
+	// Remove the target before copying.  In most cases the file won't exist, but if there are
+	// duplicate copy rules for a file and the source file was read-only the second copy could
+	// fail.
+	err = os.Remove(to)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+
 	out, err := os.Create(to)
 	if err != nil {
 		return err
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index e2fc78c..3abf978 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -24,7 +24,6 @@
 	"time"
 
 	"android/soong/shared"
-
 	"github.com/google/blueprint/bootstrap"
 
 	"android/soong/android"
@@ -65,16 +64,10 @@
 	return android.NewNameResolver(exportFilter)
 }
 
-// bazelConversionRequested checks that the user is intending to convert
-// Blueprint to Bazel BUILD files.
-func bazelConversionRequested(configuration android.Config) bool {
-	return configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
-}
-
-func newContext(configuration android.Config) *android.Context {
+func newContext(configuration android.Config, prepareBuildActions bool) *android.Context {
 	ctx := android.NewContext(configuration)
 	ctx.Register()
-	if !shouldPrepareBuildActions(configuration) {
+	if !prepareBuildActions {
 		configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
 	}
 	ctx.SetNameInterface(newNameResolver(configuration))
@@ -91,6 +84,84 @@
 	return configuration
 }
 
+// Bazel-enabled mode. Soong runs in two passes.
+// First pass: Analyze the build tree, but only store all bazel commands
+// needed to correctly evaluate the tree in the second pass.
+// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
+// the incorrect results from the first pass, and file I/O is expensive.
+func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
+	configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
+	bootstrap.Main(firstCtx.Context, configuration, false, extraNinjaDeps...)
+	// Invoke bazel commands and save results for second pass.
+	if err := configuration.BazelContext.InvokeBazel(); err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+	// Second pass: Full analysis, using the bazel command results. Output ninja file.
+	secondPassConfig, err := android.ConfigForAdditionalRun(configuration)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+	secondCtx := newContext(secondPassConfig, true)
+	bootstrap.Main(secondCtx.Context, secondPassConfig, false, extraNinjaDeps...)
+}
+
+// Run the code-generation phase to convert BazelTargetModules to BUILD files.
+func runQueryView(configuration android.Config, ctx *android.Context) {
+	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
+	absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
+	if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+}
+
+func runSoongDocs(configuration android.Config, extraNinjaDeps []string) {
+	ctx := newContext(configuration, false)
+	bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...)
+	if err := writeDocs(ctx, configuration, docFile); err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+}
+
+func writeMetrics(configuration android.Config) {
+	metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb")
+	err := android.WriteMetrics(configuration, metricsFile)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
+		os.Exit(1)
+	}
+}
+
+func doChosenActivity(configuration android.Config, extraNinjaDeps []string) {
+	bazelConversionRequested := configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
+	mixedModeBuild := configuration.BazelContext.BazelEnabled()
+	generateQueryView := bazelQueryViewDir != ""
+
+	if bazelConversionRequested {
+		// Run the alternate pipeline of bp2build mutators and singleton to convert
+		// Blueprint to BUILD files before everything else.
+		runBp2Build(configuration, extraNinjaDeps)
+		return
+	}
+
+	ctx := newContext(configuration, !generateQueryView)
+	if mixedModeBuild {
+		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
+	} else {
+		bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...)
+	}
+
+	// Convert the Soong module graph into Bazel BUILD files.
+	if generateQueryView {
+		runQueryView(configuration, ctx)
+		return
+	}
+	writeMetrics(configuration)
+}
+
 func main() {
 	flag.Parse()
 
@@ -101,7 +172,6 @@
 	usedVariablesFile := shared.JoinPath(outDir, "soong.environment.used")
 	// The top-level Blueprints file is passed as the first argument.
 	srcDir := filepath.Dir(flag.Arg(0))
-	var ctx *android.Context
 	configuration := newConfig(srcDir)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
@@ -122,72 +192,17 @@
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
 	}
 
-	bazelConversionRequested := bazelConversionRequested(configuration)
-	if bazelConversionRequested {
-		// Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
-		// before everything else.
-		runBp2Build(srcDir, configuration, extraNinjaDeps)
-	} else if configuration.BazelContext.BazelEnabled() {
-		// Bazel-enabled mode. Soong runs in two passes.
-		// First pass: Analyze the build tree, but only store all bazel commands
-		// needed to correctly evaluate the tree in the second pass.
-		// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
-		// the incorrect results from the first pass, and file I/O is expensive.
-		firstCtx := newContext(configuration)
-		configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
-		bootstrap.Main(firstCtx.Context, configuration, false, extraNinjaDeps...)
-		// Invoke bazel commands and save results for second pass.
-		if err := configuration.BazelContext.InvokeBazel(); err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
-			os.Exit(1)
-		}
-		// Second pass: Full analysis, using the bazel command results. Output ninja file.
-		secondPassConfig, err := android.ConfigForAdditionalRun(configuration)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
-			os.Exit(1)
-		}
-		ctx = newContext(secondPassConfig)
-		bootstrap.Main(ctx.Context, secondPassConfig, false, extraNinjaDeps...)
-	} else {
-		ctx = newContext(configuration)
-		bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...)
+	if docFile != "" {
+		// We don't write an used variables file when generating documentation
+		// because that is done from within the actual builds as a Ninja action and
+		// thus it would overwrite the actual used variables file so this is
+		// special-cased.
+		runSoongDocs(configuration, extraNinjaDeps)
+		return
 	}
 
-	// Convert the Soong module graph into Bazel BUILD files.
-	if !bazelConversionRequested && bazelQueryViewDir != "" {
-		// Run the code-generation phase to convert BazelTargetModules to BUILD files.
-		codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
-		absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
-		if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
-			os.Exit(1)
-		}
-	}
-
-	if !bazelConversionRequested && docFile != "" {
-		if err := writeDocs(ctx, configuration, docFile); err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
-			os.Exit(1)
-		}
-	}
-
-	// TODO(ccross): make this a command line argument.  Requires plumbing through blueprint
-	//  to affect the command line of the primary builder.
-	if !bazelConversionRequested && shouldPrepareBuildActions(configuration) {
-		metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb")
-		err := android.WriteMetrics(configuration, metricsFile)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
-			os.Exit(1)
-		}
-	}
-
-	if docFile == "" {
-		// Let's not overwrite the used variables file when generating
-		// documentation
-		writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
-	}
+	doChosenActivity(configuration, extraNinjaDeps)
+	writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
 }
 
 func writeUsedVariablesFile(path string, configuration android.Config) {
@@ -218,7 +233,7 @@
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
-func runBp2Build(srcDir string, configuration android.Config, extraNinjaDeps []string) {
+func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
 	// Register an alternate set of singletons and mutators for bazel
 	// conversion for Bazel conversion.
 	bp2buildCtx := android.NewContext(configuration)
@@ -233,7 +248,7 @@
 	// configurations or variables, since those will generate different BUILD
 	// files based on how the user has configured their tree.
 	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile())
-	modulePaths, err := bp2buildCtx.ListModulePaths(srcDir)
+	modulePaths, err := bp2buildCtx.ListModulePaths(configuration.SrcDir())
 	if err != nil {
 		panic(err)
 	}
@@ -283,20 +298,3 @@
 		[]byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFileName, extraNinjaDepsString)),
 		0666)
 }
-
-// shouldPrepareBuildActions reads configuration and flags if build actions
-// should be generated.
-func shouldPrepareBuildActions(configuration android.Config) bool {
-	// Generating Soong docs
-	if docFile != "" {
-		return false
-	}
-
-	// Generating a directory for Soong query (queryview)
-	if bazelQueryViewDir != "" {
-		return false
-	}
-
-	// Generating a directory for converted Bazel BUILD files
-	return !bazelConversionRequested(configuration)
-}
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index 20d9622..dbbc1d8 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -43,6 +43,10 @@
 	// Total size of the logical partition
 	Size *string
 
+	// List of partitions for default group. Default group has no size limit and automatically
+	// minimized when creating an image.
+	Default_group []partitionProperties
+
 	// List of groups. A group defines a fixed sized region. It can host one or more logical
 	// partitions and their total size is limited by the size of the group they are in.
 	Groups []groupProperties
@@ -52,7 +56,7 @@
 }
 
 type groupProperties struct {
-	// Name of the partition group
+	// Name of the partition group. Can't be "default"; use default_group instead.
 	Name *string
 
 	// Size of the partition group
@@ -92,8 +96,9 @@
 	// Sparse the filesystem images and calculate their sizes
 	sparseImages := make(map[string]android.OutputPath)
 	sparseImageSizes := make(map[string]android.OutputPath)
-	for _, group := range l.properties.Groups {
-		for _, part := range group.Partitions {
+
+	sparsePartitions := func(partitions []partitionProperties) {
+		for _, part := range partitions {
 			sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
 			pName := proptools.String(part.Name)
 			sparseImages[pName] = sparseImg
@@ -101,6 +106,12 @@
 		}
 	}
 
+	for _, group := range l.properties.Groups {
+		sparsePartitions(group.Partitions)
+	}
+
+	sparsePartitions(l.properties.Default_group)
+
 	cmd := builder.Command().BuiltTool("lpmake")
 
 	size := proptools.String(l.properties.Size)
@@ -123,10 +134,32 @@
 	groupNames := make(map[string]bool)
 	partitionNames := make(map[string]bool)
 
+	addPartitionsToGroup := func(partitions []partitionProperties, gName string) {
+		for _, part := range partitions {
+			pName := proptools.String(part.Name)
+			if pName == "" {
+				ctx.PropertyErrorf("groups.partitions.name", "must be set")
+			}
+			if _, ok := partitionNames[pName]; ok {
+				ctx.PropertyErrorf("groups.partitions.name", "already exists")
+			} else {
+				partitionNames[pName] = true
+			}
+			// Get size of the partition by reading the -size.txt file
+			pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+			cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
+			cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+		}
+	}
+
+	addPartitionsToGroup(l.properties.Default_group, "default")
+
 	for _, group := range l.properties.Groups {
 		gName := proptools.String(group.Name)
 		if gName == "" {
 			ctx.PropertyErrorf("groups.name", "must be set")
+		} else if gName == "default" {
+			ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`)
 		}
 		if _, ok := groupNames[gName]; ok {
 			ctx.PropertyErrorf("group.name", "already exists")
@@ -142,21 +175,7 @@
 		}
 		cmd.FlagWithArg("--group=", gName+":"+gSize)
 
-		for _, part := range group.Partitions {
-			pName := proptools.String(part.Name)
-			if pName == "" {
-				ctx.PropertyErrorf("groups.partitions.name", "must be set")
-			}
-			if _, ok := partitionNames[pName]; ok {
-				ctx.PropertyErrorf("groups.partitions.name", "already exists")
-			} else {
-				partitionNames[pName] = true
-			}
-			// Get size of the partition by reading the -size.txt file
-			pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
-			cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
-			cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
-		}
+		addPartitionsToGroup(group.Partitions, gName)
 	}
 
 	l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 2ee456d..3f1e9f3 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -670,7 +670,8 @@
 			cmd: "cat $(in) > $(out)",
 		}
        `
-	result := prepareForGenRuleTest.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
 		android.FixtureModifyConfigAndContext(
 			func(config android.Config, ctx *android.TestContext) {
 				config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
diff --git a/java/Android.bp b/java/Android.bp
index b6c14ac..8334b85 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -50,6 +50,7 @@
         "kotlin.go",
         "lint.go",
         "legacy_core_platform_api_usage.go",
+        "platform_bootclasspath.go",
         "platform_compat_config.go",
         "plugin.go",
         "prebuilt_apis.go",
@@ -79,6 +80,7 @@
         "java_test.go",
         "jdeps_test.go",
         "kotlin_test.go",
+        "platform_bootclasspath_test.go",
         "platform_compat_config_test.go",
         "plugin_test.go",
         "rro_test.go",
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index dc4e8aa..5c449e5 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -39,7 +39,8 @@
 	prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
 
 func TestHiddenAPISingleton(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
@@ -56,7 +57,8 @@
 }
 
 func TestHiddenAPIIndexSingleton(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("bar"),
 		fixtureSetBootJarsProductVariable("platform:foo", "platform:bar"),
@@ -115,7 +117,8 @@
 			" replaced by the prebuilt module \"prebuilt_foo\" but unfortunately it does not provide a" +
 			" suitable boot dex jar"
 
-	hiddenApiFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
 		RunTestWithBp(t, `
@@ -134,7 +137,8 @@
 }
 
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_import {
@@ -151,7 +155,8 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
@@ -178,7 +183,8 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
@@ -236,7 +242,8 @@
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			result := hiddenApiFixtureFactory.Extend(
+			result := android.GroupFixturePreparers(
+				hiddenApiFixtureFactory,
 				tc.preparer,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
@@ -286,7 +293,8 @@
 	// Where to find the prebuilt hiddenapi files:
 	prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
 
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 		fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
 	).RunTestWithBp(t, `
diff --git a/java/java.go b/java/java.go
index a7fbed2..26e5091 100644
--- a/java/java.go
+++ b/java/java.go
@@ -33,12 +33,12 @@
 )
 
 func init() {
-	RegisterJavaBuildComponents(android.InitRegistrationContext)
+	registerJavaBuildComponents(android.InitRegistrationContext)
 
 	RegisterJavaSdkMemberTypes()
 }
 
-func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
+func registerJavaBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_defaults", DefaultsFactory)
 
 	ctx.RegisterModuleType("java_library", LibraryFactory)
diff --git a/java/java_test.go b/java/java_test.go
index 3aafdda..fdf7579 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -74,23 +74,6 @@
 	return result.TestContext, result.Config
 }
 
-// testJavaErrorWithConfig is a legacy way of running tests of java modules that expect errors.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
-	t.Helper()
-	// This must be done on the supplied config and not as part of the fixture because any changes to
-	// the fixture's config will be ignored when RunTestWithConfig replaces it.
-	pathCtx := android.PathContextForTesting(config)
-	dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-	result := prepareForJavaTest.
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
-		RunTestWithConfig(t, config)
-	return result.TestContext, result.Config
-}
-
 // testJavaWithFS runs tests using the prepareForJavaTest
 //
 // See testJava for an explanation as to how to stop using this deprecated method.
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
new file mode 100644
index 0000000..5507077
--- /dev/null
+++ b/java/platform_bootclasspath.go
@@ -0,0 +1,74 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"android/soong/dexpreopt"
+)
+
+func init() {
+	registerPlatformBootclasspathBuildComponents(android.InitRegistrationContext)
+}
+
+func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
+}
+
+type platformBootclasspathModule struct {
+	android.ModuleBase
+}
+
+func platformBootclasspathFactory() android.Module {
+	m := &platformBootclasspathModule{}
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if SkipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+	dexpreopt.RegisterToolDeps(ctx)
+}
+
+func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Nothing to do if skipping the dexpreopt of boot image jars.
+	if SkipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+	// GenerateSingletonBuildActions method as it cannot create it for itself.
+	dexpreopt.GetGlobalSoongConfig(ctx)
+
+	imageConfig := b.getImageConfig(ctx)
+	if imageConfig == nil {
+		return
+	}
+
+	// Construct the boot image info from the config.
+	info := BootImageInfo{imageConfig: imageConfig}
+
+	// Make it available for other modules.
+	ctx.SetProvider(BootImageInfoProvider, info)
+}
+
+func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
+	return defaultBootImageConfig(ctx)
+}
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
new file mode 100644
index 0000000..1c81cfd
--- /dev/null
+++ b/java/platform_bootclasspath_test.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
+)
+
+// Contains some simple tests for platform_bootclasspath.
+
+var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+	dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+func TestPlatformBootclasspath(t *testing.T) {
+	prepareForTestWithPlatformBootclasspath.
+		RunTestWithBp(t, `
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`)
+}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index e1ec41b..2b18465 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -402,14 +402,16 @@
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 8 -target 8
 			t.Run("REL + Java language level 8", func(t *testing.T) {
-				result := fixtureFactory.Extend(prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
+				result := android.GroupFixturePreparers(
+					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
 
 				checkClasspath(t, result, true /* isJava8 */)
 			})
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 9 -target 9
 			t.Run("REL + Java language level 9", func(t *testing.T) {
-				result := fixtureFactory.Extend(prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
+				result := android.GroupFixturePreparers(
+					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
 
 				checkClasspath(t, result, false /* isJava8 */)
 			})
diff --git a/java/testing.go b/java/testing.go
index 221ceb1..80c107d 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -160,28 +160,6 @@
 	)
 }
 
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
-	bp += GatherRequiredDepsForTest()
-
-	mockFS := android.MockFS{}
-
-	cc.GatherRequiredFilesForTest(mockFS)
-
-	for k, v := range fs {
-		mockFS[k] = v
-	}
-
-	if env == nil {
-		env = make(map[string]string)
-	}
-	if env["ANDROID_JAVA8_HOME"] == "" {
-		env["ANDROID_JAVA8_HOME"] = "jdk8"
-	}
-	config := android.TestArchConfig(buildDir, env, bp, mockFS)
-
-	return config
-}
-
 func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
 	fs := make(map[string][]byte)
 	for _, level := range apiLevels {
@@ -200,19 +178,6 @@
 	return fs
 }
 
-// Register build components provided by this package that are needed by tests.
-//
-// In particular this must register all the components that are used in the `Android.bp` snippet
-// returned by GatherRequiredDepsForTest()
-//
-// deprecated: Use test fixtures instead, e.g. PrepareForTestWithJavaBuildComponents
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
-	registerRequiredBuildComponentsForTest(ctx)
-
-	// Make sure that any tool related module types needed by dexpreopt have been registered.
-	dexpreopt.RegisterToolModulesForTest(ctx)
-}
-
 // registerRequiredBuildComponentsForTest registers the build components used by
 // PrepareForTestWithJavaDefaultModules.
 //
@@ -228,7 +193,8 @@
 	RegisterDexpreoptBootJarsComponents(ctx)
 	RegisterDocsBuildComponents(ctx)
 	RegisterGenRuleBuildComponents(ctx)
-	RegisterJavaBuildComponents(ctx)
+	registerJavaBuildComponents(ctx)
+	registerPlatformBootclasspathBuildComponents(ctx)
 	RegisterPrebuiltApisBuildComponents(ctx)
 	RegisterRuntimeResourceOverlayBuildComponents(ctx)
 	RegisterSdkLibraryBuildComponents(ctx)
@@ -236,23 +202,6 @@
 	RegisterSystemModulesBuildComponents(ctx)
 }
 
-// Gather the module definitions needed by tests that depend upon code from this package.
-//
-// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
-//
-// deprecated: Use test fixtures instead, e.g. PrepareForTestWithJavaDefaultModules
-func GatherRequiredDepsForTest() string {
-	bp := gatherRequiredDepsForTest()
-
-	// For class loader context and <uses-library> tests.
-	bp += dexpreopt.CompatLibDefinitionsForTest()
-
-	// Make sure that any tools needed for dexpreopting are defined.
-	bp += dexpreopt.BpToolModulesForTest()
-
-	return bp
-}
-
 // gatherRequiredDepsForTest gathers the module definitions used by
 // PrepareForTestWithJavaDefaultModules.
 //
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index e90564f..bd3bfb1 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -18,7 +18,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/cc"
 )
 
 func TestClippy(t *testing.T) {
@@ -45,15 +44,6 @@
 			clippy_lints: "none",
 		}`
 
-	bp = bp + GatherRequiredDepsForTest()
-	bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-
-	fs := map[string][]byte{
-		// Reuse the same blueprint file for subdirectories.
-		"external/Android.bp": []byte(bp),
-		"hardware/Android.bp": []byte(bp),
-	}
-
 	var clippyLintTests = []struct {
 		modulePath string
 		fooFlags   string
@@ -66,29 +56,22 @@
 	for _, tc := range clippyLintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
-			ctx := CreateTestContext(config)
-			ctx.Register()
-			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
-			android.FailIfErrored(t, errs)
-			_, errs = ctx.PrepareBuildActions(config)
-			android.FailIfErrored(t, errs)
+			result := android.GroupFixturePreparers(
+				prepareForRustTest,
+				// Test with the blueprint file in different directories.
+				android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
+			).RunTest(t)
 
-			r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-			if r.Args["clippyFlags"] != tc.fooFlags {
-				t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["clippyFlags"], tc.fooFlags)
-			}
+			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			android.AssertStringEquals(t, "libfoo flags", tc.fooFlags, r.Args["clippyFlags"])
 
-			r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-			if r.Args["clippyFlags"] != "${config.ClippyDefaultLints}" {
-				t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["clippyFlags"], "${config.ClippyDefaultLints}")
-			}
+			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			android.AssertStringEquals(t, "libbar flags", "${config.ClippyDefaultLints}", r.Args["clippyFlags"])
 
-			r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
 			if r.Rule != nil {
 				t.Errorf("libfoobar is setup to use clippy when explicitly disabled: clippyFlags=%q", r.Args["clippyFlags"])
 			}
-
 		})
 	}
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 3ed086f..c752762 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -19,7 +19,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/cc"
 )
 
 // Test that feature flags are being correctly generated.
@@ -132,15 +131,6 @@
 			lints: "none",
 		}`
 
-	bp = bp + GatherRequiredDepsForTest()
-	bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-
-	fs := map[string][]byte{
-		// Reuse the same blueprint file for subdirectories.
-		"external/Android.bp": []byte(bp),
-		"hardware/Android.bp": []byte(bp),
-	}
-
 	var lintTests = []struct {
 		modulePath string
 		fooFlags   string
@@ -153,29 +143,20 @@
 	for _, tc := range lintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
-			ctx := CreateTestContext(config)
-			ctx.Register()
-			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
-			android.FailIfErrored(t, errs)
-			_, errs = ctx.PrepareBuildActions(config)
-			android.FailIfErrored(t, errs)
+			result := android.GroupFixturePreparers(
+				prepareForRustTest,
+				// Test with the blueprint file in different directories.
+				android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
+			).RunTest(t)
 
-			r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			if !strings.Contains(r.Args["rustcFlags"], tc.fooFlags) {
-				t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["rustcFlags"], tc.fooFlags)
-			}
+			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			android.AssertStringDoesContain(t, "libfoo flags", r.Args["rustcFlags"], tc.fooFlags)
 
-			r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			if !strings.Contains(r.Args["rustcFlags"], "${config.RustDefaultLints}") {
-				t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["rustcFlags"], "${config.RustDefaultLints}")
-			}
+			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			android.AssertStringDoesContain(t, "libbar flags", r.Args["rustcFlags"], "${config.RustDefaultLints}")
 
-			r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			if !strings.Contains(r.Args["rustcFlags"], "${config.RustAllowAllLints}") {
-				t.Errorf("Incorrect flags for libfoobar: %q, want %q", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
-			}
-
+			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			android.AssertStringDoesContain(t, "libfoobar flags", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
 		})
 	}
 }
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 7af4635..09d30db 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -28,9 +28,10 @@
 // testProjectJson run the generation of rust-project.json. It returns the raw
 // content of the generated file.
 func testProjectJson(t *testing.T, bp string) []byte {
-	result := prepareForRustTest.
-		Extend(android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"})).
-		RunTestWithBp(t, bp)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}),
+	).RunTestWithBp(t, bp)
 
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
diff --git a/rust/testing.go b/rust/testing.go
index 5be71c9..75adcfc 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -17,13 +17,12 @@
 import (
 	"android/soong/android"
 	"android/soong/cc"
-	"android/soong/genrule"
 )
 
 // Preparer that will define all cc module types and a limited set of mutators and singletons that
 // make those module types usable.
 var PrepareForTestWithRustBuildComponents = android.GroupFixturePreparers(
-	android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+	android.FixtureRegisterWithContext(registerRequiredBuildComponentsForTest),
 )
 
 // The directory in which rust test default modules will be defined.
@@ -197,7 +196,7 @@
 	return bp
 }
 
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
@@ -231,14 +230,3 @@
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 }
-
-func CreateTestContext(config android.Config) *android.TestContext {
-	ctx := android.NewTestArchContext(config)
-	android.RegisterPrebuiltMutators(ctx)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	genrule.RegisterGenruleBuildComponents(ctx)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	RegisterRequiredBuildComponentsForTest(ctx)
-
-	return ctx
-}
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
index 244ed0d..a9b61a1 100755
--- a/scripts/gen_sorted_bss_symbols.sh
+++ b/scripts/gen_sorted_bss_symbols.sh
@@ -18,11 +18,11 @@
 # their sizes.
 # Inputs:
 #  Environment:
-#   CROSS_COMPILE: prefix added to nm tools
+#   CLANG_BIN: path to the clang bin directory
 #  Arguments:
 #   $1: Input ELF file
 #   $2: Output symbol ordering file
 
 set -o pipefail
 
-${CROSS_COMPILE}nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
+${CLANG_BIN}/llvm-nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 907f239..8168fbf 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -74,7 +74,7 @@
   return parser.parse_args()
 
 
-def enforce_uses_libraries(manifest, required, optional, relax, is_apk = False):
+def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
   """Verify that the <uses-library> tags in the manifest match those provided
   by the build system.
 
@@ -86,26 +86,36 @@
     is_apk:   if the manifest comes from an APK or an XML file
   """
   if is_apk:
-    manifest_required, manifest_optional = extract_uses_libs_apk(manifest)
+    manifest_required, manifest_optional, tags = extract_uses_libs_apk(manifest)
   else:
-    manifest_required, manifest_optional = extract_uses_libs_xml(manifest)
+    manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest)
 
-  err = []
-  if manifest_required != required:
-    err.append('Expected required <uses-library> tags "%s", got "%s"' %
-               (', '.join(required), ', '.join(manifest_required)))
+  if manifest_required == required and manifest_optional == optional:
+    return None
 
-  if manifest_optional != optional:
-    err.append('Expected optional <uses-library> tags "%s", got "%s"' %
-               (', '.join(optional), ', '.join(manifest_optional)))
+  errmsg = ''.join([
+    'mismatch in the <uses-library> tags between the build system and the '
+      'manifest:\n',
+    '\t- required libraries in build system: [%s]\n' % ', '.join(required),
+    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_required),
+    '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
+    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_optional),
+    '\t- tags in the manifest (%s):\n' % path,
+    '\t\t%s\n' % '\t\t'.join(tags),
+      'note: the following options are available:\n',
+    '\t- to temporarily disable the check on command line, rebuild with ',
+      'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
+      'and disable AOT-compilation in dexpreopt)\n',
+    '\t- to temporarily disable the check for the whole product, set ',
+      'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
+    '\t- to fix the check, make build system properties coherent with the '
+      'manifest\n',
+    '\t- see build/make/Changes.md for details\n'])
 
-  if err:
-    errmsg = '\n'.join(err)
-    if not relax:
-      raise ManifestMismatchError(errmsg)
-    return errmsg
+  if not relax:
+    raise ManifestMismatchError(errmsg)
 
-  return None
+  return errmsg
 
 
 def extract_uses_libs_apk(badging):
@@ -115,14 +125,19 @@
 
   required = []
   optional = []
+  lines = []
   for match in re.finditer(pattern, badging):
+    lines.append(match.group(0))
     libname = match.group(2)
     if match.group(1) == None:
       required.append(libname)
     else:
       optional.append(libname)
 
-  return first_unique_elements(required), first_unique_elements(optional)
+  required = first_unique_elements(required)
+  optional = first_unique_elements(optional)
+  tags = first_unique_elements(lines)
+  return required, optional, tags
 
 
 def extract_uses_libs_xml(xml):
@@ -143,7 +158,15 @@
   required = [uses_library_name(x) for x in libs if uses_library_required(x)]
   optional = [uses_library_name(x) for x in libs if not uses_library_required(x)]
 
-  return first_unique_elements(required), first_unique_elements(optional)
+  # render <uses-library> tags as XML for a pretty error message
+  tags = []
+  for lib in libs:
+    tags.append(lib.toprettyxml())
+
+  required = first_unique_elements(required)
+  optional = first_unique_elements(optional)
+  tags = first_unique_elements(tags)
+  return required, optional, tags
 
 
 def first_unique_elements(l):
@@ -278,7 +301,7 @@
       # in the manifest. Raise an exception on mismatch, unless the script was
       # passed a special parameter to suppress exceptions.
       errmsg = enforce_uses_libraries(manifest, required, optional,
-        args.enforce_uses_libraries_relax, is_apk)
+        args.enforce_uses_libraries_relax, is_apk, args.input)
 
       # Create a status file that is empty on success, or contains an error
       # message on failure. When exceptions are suppressed, dexpreopt command
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 635ba9d..7159bdd 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -49,9 +49,9 @@
     try:
       relax = False
       manifest_check.enforce_uses_libraries(doc, uses_libraries,
-        optional_uses_libraries, relax, is_apk=False)
+        optional_uses_libraries, relax, False, 'path/to/X/AndroidManifest.xml')
       manifest_check.enforce_uses_libraries(apk, uses_libraries,
-        optional_uses_libraries, relax, is_apk=True)
+        optional_uses_libraries, relax, True, 'path/to/X/X.apk')
       return True
     except manifest_check.ManifestMismatchError:
       return False
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index a886a18..b19fcc5 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -2407,6 +2407,7 @@
             "1",
             "2",
             "3",
+            "current",
         ],
     },
     arch: {
@@ -2461,6 +2462,7 @@
             "1",
             "2",
             "3",
+            "current",
         ],
     },
     target: {
@@ -2500,6 +2502,7 @@
             "1",
             "2",
             "3",
+            "current",
         ],
     },
     target: {