Merge "Split functions for compiling boot jars to allow future code reuse."
diff --git a/android/android_test.go b/android/android_test.go
index 2a697fb..fb82e37 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -22,5 +22,3 @@
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
-
-var emptyTestFixtureFactory = NewFixtureFactory(nil)
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 230b1ec..8eda9b2 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -141,14 +141,14 @@
 // bp module and then returns the config and the custom module called "foo".
 func buildContextAndCustomModuleFoo(t *testing.T, bp string) (*TestContext, *customModule) {
 	t.Helper()
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		// Enable androidmk Singleton
 		PrepareForTestWithAndroidMk,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("custom", customModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	module := result.ModuleForTests("foo", "").Module().(*customModule)
 	return result.TestContext, module
diff --git a/android/apex.go b/android/apex.go
index 0d5cac8..a5ff442 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -140,9 +140,24 @@
 	// DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
 	// this module. For example, a static lib dependency usually returns true here, while a
 	// shared lib dependency to a stub library returns false.
+	//
+	// This method must not be called directly without first ignoring dependencies whose tags
+	// implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps()
+	// are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use
+	// IsDepInSameApex instead.
 	DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
 }
 
+func IsDepInSameApex(ctx BaseModuleContext, module, dep Module) bool {
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	if _, ok := depTag.(ExcludeFromApexContentsTag); ok {
+		// The tag defines a dependency that never requires the child module to be part of the same
+		// apex as the parent.
+		return false
+	}
+	return module.(DepIsInSameApex).DepIsInSameApex(ctx, dep)
+}
+
 // ApexModule is the interface that a module type is expected to implement if the module has to be
 // built differently depending on whether the module is destined for an APEX or not (i.e., installed
 // to one of the regular partitions).
@@ -260,6 +275,10 @@
 //
 // Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant
 // from being created for the module.
+//
+// At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which
+// implement this interface do not define dependencies onto members of an sdk_snapshot. If that
+// changes then sdk.sdkRequirementsMutator will need fixing.
 type ExcludeFromApexContentsTag interface {
 	blueprint.DependencyTag
 
diff --git a/android/arch_test.go b/android/arch_test.go
index 09cb523..633ddaa 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -358,12 +358,12 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
-			result := emptyTestFixtureFactory.RunTest(t,
+			result := GroupFixturePreparers(
 				prepareForArchTest,
 				// Test specific preparer
 				OptionalFixturePreparer(tt.preparer),
 				FixtureWithRootAndroidBp(bp),
-			)
+			).RunTest(t)
 			ctx := result.TestContext
 
 			if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
@@ -441,7 +441,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
-			result := emptyTestFixtureFactory.RunTest(t,
+			result := GroupFixturePreparers(
 				prepareForArchTest,
 				// Test specific preparer
 				OptionalFixturePreparer(tt.preparer),
@@ -455,7 +455,7 @@
 					}
 				}),
 				FixtureWithRootAndroidBp(bp),
-			)
+			).RunTest(t)
 
 			ctx := result.TestContext
 
diff --git a/android/config.go b/android/config.go
index 19706f5..e335de0 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1005,6 +1005,10 @@
 	return ioutil.ReadFile(absolutePath(path.String()))
 }
 
+func (c *deviceConfig) WithDexpreopt() bool {
+	return c.config.productVariables.WithDexpreopt
+}
+
 func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
 	return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
 }
@@ -1389,7 +1393,10 @@
 }
 
 func (c *deviceConfig) BoardSepolicyVers() string {
-	return String(c.config.productVariables.BoardSepolicyVers)
+	if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
+		return ver
+	}
+	return c.PlatformSepolicyVersion()
 }
 
 func (c *deviceConfig) BoardReqdMaskPolicy() []string {
diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go
index d30ff69..b8a176e 100644
--- a/android/csuite_config_test.go
+++ b/android/csuite_config_test.go
@@ -19,14 +19,14 @@
 )
 
 func TestCSuiteConfig(t *testing.T) {
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		PrepareForTestWithArchMutator,
 		FixtureRegisterWithContext(registerCSuiteBuildComponents),
 		FixtureWithRootAndroidBp(`
 			csuite_config { name: "plain"}
 			csuite_config { name: "with_manifest", test_config: "manifest.xml" }
 		`),
-	)
+	).RunTest(t)
 
 	variants := result.ModuleVariantsForTests("plain")
 	if len(variants) > 1 {
diff --git a/android/defaults_test.go b/android/defaults_test.go
index b33cb52..a7542ab 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -83,10 +83,10 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		prepareForDefaultsTest,
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	foo := result.Module("foo", "").(*defaultsTestModule)
 
@@ -114,11 +114,11 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		prepareForDefaultsTest,
 		PrepareForTestWithAllowMissingDependencies,
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	missingDefaults := result.ModuleForTests("missing_defaults", "").Output("out")
 	missingTransitiveDefaults := result.ModuleForTests("missing_transitive_defaults", "").Output("out")
diff --git a/android/deptag_test.go b/android/deptag_test.go
index 7f55896..eb4fa89 100644
--- a/android/deptag_test.go
+++ b/android/deptag_test.go
@@ -80,13 +80,13 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		PrepareForTestWithArchMutator,
 		FixtureWithRootAndroidBp(bp),
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory)
 		}),
-	)
+	).RunTest(t)
 
 	config := result.Config
 
diff --git a/android/env.go b/android/env.go
index 289d803..725a145 100644
--- a/android/env.go
+++ b/android/env.go
@@ -18,12 +18,16 @@
 	"android/soong/shared"
 )
 
-// This file supports dependencies on environment variables.  During build manifest generation,
-// any dependency on an environment variable is added to a list.  During the singleton phase
-// a JSON file is written containing the current value of all used environment variables.
-// The next time the top-level build script is run, it uses the soong_env executable to
-// compare the contents of the environment variables, rewriting the file if necessary to cause
-// a manifest regeneration.
+// This file supports dependencies on environment variables.  During build
+// manifest generation, any dependency on an environment variable is added to a
+// list.  At the end of the build, a JSON file called soong.environment.used is
+// written containing the current value of all used environment variables. The
+// next time the top-level build script is run, soong_ui parses the compare the
+// contents of the used environment variables, then, if they changed, deletes
+// soong.environment.used to cause a rebuild.
+//
+// The dependency of build.ninja on soong.environment.used is declared in
+// build.ninja.d
 
 var originalEnv map[string]string
 
@@ -34,30 +38,3 @@
 		panic(err)
 	}
 }
-
-func EnvSingleton() Singleton {
-	return &envSingleton{}
-}
-
-type envSingleton struct{}
-
-func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) {
-	envDeps := ctx.Config().EnvDeps()
-
-	envFile := PathForOutput(ctx, "soong.environment.used")
-	if ctx.Failed() {
-		return
-	}
-
-	data, err := shared.EnvFileContents(envDeps)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-
-	err = WriteFileToOutputDir(envFile, data, 0666)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-
-	ctx.AddNinjaFileDeps(envFile.String())
-}
diff --git a/android/fixture.go b/android/fixture.go
index 6e4819c..6c9ea6b 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -30,8 +30,8 @@
 // first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
 // instances to it to modify the environment.
 //
-// FixtureFactory
-// ==============
+// 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
@@ -43,7 +43,9 @@
 // intended to be immutable and able to prepare multiple Fixture objects simultaneously without
 // them sharing any data.
 //
-// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
+// They provide the basic capabilities for running tests too.
+//
+// FixturePreparers are only ever applied once per test fixture. Prior to application the list of
 // FixturePreparers are flattened and deduped while preserving the order they first appear in the
 // list. This makes it easy to reuse, group and combine FixturePreparers together.
 //
@@ -119,14 +121,58 @@
 // Some files to use in tests in the java package.
 //
 // var javaMockFS = android.MockFS{
-//		"api/current.txt":        nil,
-//		"api/removed.txt":        nil,
+//    "api/current.txt":        nil,
+//    "api/removed.txt":        nil,
 //    ...
 // }
 //
-// A package private factory for use for testing java within the java package.
+// A package private preparer for use for testing java within the java package.
 //
-// var javaFixtureFactory = NewFixtureFactory(
+// var prepareForJavaTest = android.GroupFixturePreparers(
+//    PrepareForIntegrationTestWithJava,
+//    FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+//      ctx.RegisterModuleType("test_module", testModule)
+//    }),
+//    javaMockFS.AddToFixture(),
+//    ...
+// }
+//
+// func TestJavaStuff(t *testing.T) {
+//   result := android.GroupFixturePreparers(t,
+//       prepareForJavaTest,
+//       android.FixtureWithRootAndroidBp(`java_library {....}`),
+//       android.MockFS{...}.AddToFixture(),
+//   ).RunTest(t)
+//   ... test result ...
+// }
+//
+// package cc
+// var PrepareForTestWithCC = android.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 prepareForApexTest = android.GroupFixturePreparers(
+//    PrepareForJava,
+//    PrepareForCC,
+//    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)
@@ -146,7 +192,7 @@
 // package cc
 // var PrepareForTestWithCC = GroupFixturePreparers(
 //    android.PrepareForArchMutator,
-//	  android.prepareForPrebuilts,
+//    android.prepareForPrebuilts,
 //    FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
 //    ...
 // )
@@ -170,52 +216,10 @@
 //
 // 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 {
-
-	// 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.
-	Extend(preparers ...FixturePreparer) FixtureFactory
-
-	// Create a Fixture.
-	Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
-
-	// ExtendWithErrorHandler creates a new FixtureFactory that will use the supplied error handler
-	// to check the errors (may be 0) reported by the test.
-	//
-	// The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
-	// errors are reported.
-	ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory
-
-	// Run the test, checking any errors reported and returning a TestResult instance.
-	//
-	// Shorthand for Fixture(t, preparers...).RunTest()
-	RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
-
-	// Run the test with the supplied Android.bp file.
-	//
-	// Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
-	RunTestWithBp(t *testing.T, bp string) *TestResult
-
-	// RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
-	// the test fixture.
-	//
-	// In order to allow the Config object to be customized separately to the TestContext a lot of
-	// existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
-	// from the test and then have the TestContext created and configured automatically. e.g.
-	// testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
-	//
-	// This method allows those methods to be migrated to use the test fixture pattern without
-	// requiring that every test that uses those methods be migrated at the same time. That allows
-	// those tests to benefit from correctness in the order of registration quickly.
-	//
-	// This method discards the config (along with its mock file system, product variables,
-	// environment, etc.) that may have been set up by FixturePreparers.
-	//
-	// deprecated
-	RunTestWithConfig(t *testing.T, config Config) *TestResult
+	FixturePreparer
 }
 
 // Create a new FixtureFactory that will apply the supplied preparers.
@@ -224,14 +228,17 @@
 // 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 {
-	return &fixtureFactory{
+	f := &fixtureFactory{
 		buildDirSupplier: buildDirSupplier,
-		preparers:        dedupAndFlattenPreparers(nil, preparers),
-
-		// Set the default error handler.
-		errorHandler: FixtureExpectsNoErrors,
+		compositeFixturePreparer: compositeFixturePreparer{
+			preparers: dedupAndFlattenPreparers(nil, preparers),
+		},
 	}
+	f.initBaseFixturePreparer(f)
+	return f
 }
 
 // A set of mock files to add to the mock file system.
@@ -416,7 +423,8 @@
 // Before preparing the fixture the list of preparers is flattened by replacing each
 // instance of GroupFixturePreparers with its contents.
 func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
-	return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
+	all := dedupAndFlattenPreparers(nil, preparers)
+	return newFixturePreparer(all)
 }
 
 // NullFixturePreparer is a preparer that does nothing.
@@ -432,20 +440,57 @@
 	}
 }
 
-type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
-
-// FixturePreparer is an opaque interface that can change a fixture.
+// FixturePreparer provides the ability to create, modify and then run tests within a fixture.
 type FixturePreparer interface {
-	// visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
-	visit(simpleFixturePreparerVisitor)
-}
+	// Return the flattened and deduped list of simpleFixturePreparer pointers.
+	list() []*simpleFixturePreparer
 
-type fixturePreparers []FixturePreparer
+	// 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
 
-func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
-	for _, p := range f {
-		p.visit(visitor)
-	}
+	// Create a Fixture.
+	Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
+
+	// ExtendWithErrorHandler creates a new FixturePreparer that will use the supplied error handler
+	// to check the errors (may be 0) reported by the test.
+	//
+	// The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
+	// errors are reported.
+	ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer
+
+	// Run the test, checking any errors reported and returning a TestResult instance.
+	//
+	// Shorthand for Fixture(t, preparers...).RunTest()
+	RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
+
+	// Run the test with the supplied Android.bp file.
+	//
+	// Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
+	RunTestWithBp(t *testing.T, bp string) *TestResult
+
+	// RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
+	// the test fixture.
+	//
+	// In order to allow the Config object to be customized separately to the TestContext a lot of
+	// existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
+	// from the test and then have the TestContext created and configured automatically. e.g.
+	// testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
+	//
+	// This method allows those methods to be migrated to use the test fixture pattern without
+	// requiring that every test that uses those methods be migrated at the same time. That allows
+	// those tests to benefit from correctness in the order of registration quickly.
+	//
+	// This method discards the config (along with its mock file system, product variables,
+	// environment, etc.) that may have been set up by FixturePreparers.
+	//
+	// deprecated
+	RunTestWithConfig(t *testing.T, config Config) *TestResult
 }
 
 // dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
@@ -458,48 +503,73 @@
 // preparers - a list of additional unflattened, undeduped preparers that will be applied after the
 //             base preparers.
 //
-// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
-func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
-	var list []*simpleFixturePreparer
+// Returns a deduped and flattened list of the preparers starting with the ones in base with any
+// additional ones from the preparers list added afterwards.
+func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers []FixturePreparer) []*simpleFixturePreparer {
+	if len(preparers) == 0 {
+		return base
+	}
+
+	list := make([]*simpleFixturePreparer, len(base))
 	visited := make(map[*simpleFixturePreparer]struct{})
 
 	// Mark the already flattened and deduped preparers, if any, as having been seen so that
-	// duplicates of these in the additional preparers will be discarded.
-	for _, s := range base {
+	// duplicates of these in the additional preparers will be discarded. Add them to the output
+	// list.
+	for i, s := range base {
 		visited[s] = struct{}{}
+		list[i] = s
 	}
 
-	preparers.visit(func(preparer *simpleFixturePreparer) {
-		if _, seen := visited[preparer]; !seen {
-			visited[preparer] = struct{}{}
-			list = append(list, preparer)
+	for _, p := range preparers {
+		for _, s := range p.list() {
+			if _, seen := visited[s]; !seen {
+				visited[s] = struct{}{}
+				list = append(list, s)
+			}
 		}
-	})
+	}
+
 	return list
 }
 
 // compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
 type compositeFixturePreparer struct {
+	baseFixturePreparer
+	// The flattened and deduped list of simpleFixturePreparer pointers encapsulated within this
+	// composite preparer.
 	preparers []*simpleFixturePreparer
 }
 
-func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
-	for _, p := range c.preparers {
-		p.visit(visitor)
+func (c *compositeFixturePreparer) list() []*simpleFixturePreparer {
+	return c.preparers
+}
+
+func newFixturePreparer(preparers []*simpleFixturePreparer) FixturePreparer {
+	if len(preparers) == 1 {
+		return preparers[0]
 	}
+	p := &compositeFixturePreparer{
+		preparers: preparers,
+	}
+	p.initBaseFixturePreparer(p)
+	return p
 }
 
 // simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
 type simpleFixturePreparer struct {
+	baseFixturePreparer
 	function func(fixture *fixture)
 }
 
-func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
-	visitor(s)
+func (s *simpleFixturePreparer) list() []*simpleFixturePreparer {
+	return []*simpleFixturePreparer{s}
 }
 
 func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
-	return &simpleFixturePreparer{function: preparer}
+	p := &simpleFixturePreparer{function: preparer}
+	p.initBaseFixturePreparer(p)
+	return p
 }
 
 // FixtureErrorHandler determines how to respond to errors reported by the code under test.
@@ -635,82 +705,66 @@
 	NinjaDeps []string
 }
 
-var _ FixtureFactory = (*fixtureFactory)(nil)
+func createFixture(t *testing.T, buildDir string, base []*simpleFixturePreparer, extra []FixturePreparer) Fixture {
+	all := dedupAndFlattenPreparers(base, extra)
 
-type fixtureFactory struct {
-	buildDirSupplier *string
-	preparers        []*simpleFixturePreparer
-	errorHandler     FixtureErrorHandler
-}
-
-func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
-	// Create a new slice to avoid accidentally sharing the preparers slice from this factory with
-	// the extending factories.
-	var all []*simpleFixturePreparer
-	all = append(all, f.preparers...)
-	all = append(all, dedupAndFlattenPreparers(f.preparers, preparers)...)
-	// Copy the existing factory.
-	extendedFactory := &fixtureFactory{}
-	*extendedFactory = *f
-	// Use the extended list of preparers.
-	extendedFactory.preparers = all
-	return extendedFactory
-}
-
-func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
-	var buildDir string
-	if f.buildDirSupplier == nil {
-		// Create a new temporary directory for this run. It will be automatically cleaned up when the
-		// test finishes.
-		buildDir = t.TempDir()
-	} else {
-		// Retrieve the buildDir from the supplier.
-		buildDir = *f.buildDirSupplier
-	}
 	config := TestConfig(buildDir, nil, "", nil)
 	ctx := NewTestContext(config)
 	fixture := &fixture{
-		factory:      f,
-		t:            t,
-		config:       config,
-		ctx:          ctx,
-		mockFS:       make(MockFS),
-		errorHandler: f.errorHandler,
+		preparers: all,
+		t:         t,
+		config:    config,
+		ctx:       ctx,
+		mockFS:    make(MockFS),
+		// Set the default error handler.
+		errorHandler: FixtureExpectsNoErrors,
 	}
 
-	for _, preparer := range f.preparers {
-		preparer.function(fixture)
-	}
-
-	for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
+	for _, preparer := range all {
 		preparer.function(fixture)
 	}
 
 	return fixture
 }
 
-func (f *fixtureFactory) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory {
-	newFactory := &fixtureFactory{}
-	*newFactory = *f
-	newFactory.errorHandler = errorHandler
-	return newFactory
+type baseFixturePreparer struct {
+	self FixturePreparer
 }
 
-func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
+func (b *baseFixturePreparer) initBaseFixturePreparer(self FixturePreparer) {
+	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, preparers ...FixturePreparer) Fixture {
+	return createFixture(t, t.TempDir(), b.self.list(), preparers)
+}
+
+func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer {
+	return b.self.Extend(newSimpleFixturePreparer(func(fixture *fixture) {
+		fixture.errorHandler = errorHandler
+	}))
+}
+
+func (b *baseFixturePreparer) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
 	t.Helper()
-	fixture := f.Fixture(t, preparers...)
+	fixture := b.self.Fixture(t, preparers...)
 	return fixture.RunTest()
 }
 
-func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
+func (b *baseFixturePreparer) RunTestWithBp(t *testing.T, bp string) *TestResult {
 	t.Helper()
-	return f.RunTest(t, FixtureWithRootAndroidBp(bp))
+	return b.RunTest(t, FixtureWithRootAndroidBp(bp))
 }
 
-func (f *fixtureFactory) RunTestWithConfig(t *testing.T, config Config) *TestResult {
+func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *TestResult {
 	t.Helper()
 	// Create the fixture as normal.
-	fixture := f.Fixture(t).(*fixture)
+	fixture := b.self.Fixture(t).(*fixture)
 
 	// Discard the mock filesystem as otherwise that will override the one in the config.
 	fixture.mockFS = nil
@@ -729,9 +783,49 @@
 	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, preparers ...FixturePreparer) Fixture {
+	// If there is no buildDirSupplier then just use the default implementation.
+	if f.buildDirSupplier == nil {
+		return f.baseFixturePreparer.Fixture(t, preparers...)
+	}
+
+	// Retrieve the buildDir from the supplier.
+	buildDir := *f.buildDirSupplier
+
+	return createFixture(t, buildDir, f.preparers, preparers)
+}
+
 type fixture struct {
-	// The factory used to create this fixture.
-	factory *fixtureFactory
+	// The preparers used to create this fixture.
+	preparers []*simpleFixturePreparer
 
 	// The gotest state of the go test within which this was created.
 	t *testing.T
@@ -839,6 +933,21 @@
 	return result
 }
 
+// Preparer will return a FixturePreparer encapsulating all the preparers used to create the fixture
+// that produced this result.
+//
+// e.g. assuming that this result was created by running:
+//     factory.Extend(preparer1, preparer2).RunTest(t, preparer3, preparer4)
+//
+// Then this method will be equivalent to running:
+//     GroupFixturePreparers(preparer1, preparer2, preparer3, preparer4)
+//
+// 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.
+func (r *TestResult) Preparer() FixturePreparer {
+	return newFixturePreparer(r.fixture.preparers)
+}
+
 // Module returns the module with the specific name and of the specified variant.
 func (r *TestResult) Module(name string, variant string) Module {
 	return r.ModuleForTests(name, variant).Module()
diff --git a/android/fixture_test.go b/android/fixture_test.go
index 209bee6..681a034 100644
--- a/android/fixture_test.go
+++ b/android/fixture_test.go
@@ -39,10 +39,9 @@
 
 	preparer2Then1 := GroupFixturePreparers(preparer2, preparer1)
 
-	buildDir := "build"
-	factory := NewFixtureFactory(&buildDir, preparer1, preparer2, preparer1, preparer1Then2)
+	group := GroupFixturePreparers(preparer1, preparer2, preparer1, preparer1Then2)
 
-	extension := factory.Extend(preparer4, preparer2)
+	extension := group.Extend(preparer4, preparer2)
 
 	extension.Fixture(t, preparer1, preparer2, preparer2Then1, preparer3)
 
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
index 83e83ce..1f09568 100644
--- a/android/license_kind_test.go
+++ b/android/license_kind_test.go
@@ -97,13 +97,14 @@
 func TestLicenseKind(t *testing.T) {
 	for _, test := range licenseKindTests {
 		t.Run(test.name, func(t *testing.T) {
-			licenseTestFixtureFactory.
-				Extend(
-					FixtureRegisterWithContext(func(ctx RegistrationContext) {
-						ctx.RegisterModuleType("mock_license", newMockLicenseModule)
-					}),
-					test.fs.AddToFixture(),
-				).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+			GroupFixturePreparers(
+				prepareForLicenseTest,
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+				}),
+				test.fs.AddToFixture(),
+			).
+				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
 				RunTest(t)
 		})
 	}
diff --git a/android/license_test.go b/android/license_test.go
index a564827..2b09a4f 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -5,7 +5,7 @@
 )
 
 // Common test set up for license tests.
-var licenseTestFixtureFactory = emptyTestFixtureFactory.Extend(
+var prepareForLicenseTest = GroupFixturePreparers(
 	// General preparers in alphabetical order.
 	PrepareForTestWithDefaults,
 	prepareForTestWithLicenses,
@@ -179,7 +179,8 @@
 	for _, test := range licenseTests {
 		t.Run(test.name, func(t *testing.T) {
 			// Customize the common license text fixture factory.
-			licenseTestFixtureFactory.Extend(
+			GroupFixturePreparers(
+				prepareForLicenseTest,
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("rule", newMockRuleModule)
 				}),
diff --git a/android/licenses_test.go b/android/licenses_test.go
index a581932..913dc88 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -470,7 +470,8 @@
 	for _, test := range licensesTests {
 		t.Run(test.name, func(t *testing.T) {
 			// Customize the common license text fixture factory.
-			result := licenseTestFixtureFactory.Extend(
+			result := GroupFixturePreparers(
+				prepareForLicenseTest,
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
 					ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
diff --git a/android/module_test.go b/android/module_test.go
index 99bf30a..9ac9291 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -179,11 +179,9 @@
 		}
 	`
 
-	emptyTestFixtureFactory.
+	prepareForModuleTests.
 		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)).
-		RunTest(t,
-			prepareForModuleTests,
-			FixtureWithRootAndroidBp(bp))
+		RunTestWithBp(t, bp)
 }
 
 func TestValidateCorrectBuildParams(t *testing.T) {
@@ -268,9 +266,7 @@
 		"\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
 	}
 
-	emptyTestFixtureFactory.
+	prepareForModuleTests.
 		ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
-		RunTest(t,
-			prepareForModuleTests,
-			FixtureWithRootAndroidBp(bp))
+		RunTestWithBp(t, bp)
 }
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 46d26d1..21eebd2 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -65,7 +65,7 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		PrepareForTestWithAllowMissingDependencies,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
@@ -74,7 +74,7 @@
 			})
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	foo := result.ModuleForTests("foo", "").Module().(*mutatorTestModule)
 
@@ -90,7 +90,7 @@
 
 	var moduleStrings []string
 
-	emptyTestFixtureFactory.RunTest(t,
+	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 
 			ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
@@ -128,7 +128,7 @@
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	want := []string{
 		// Initial name.
@@ -187,7 +187,7 @@
 
 	finalGot := map[string]int{}
 
-	emptyTestFixtureFactory.RunTest(t,
+	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			dep1Tag := struct {
 				blueprint.BaseDependencyTag
@@ -224,7 +224,7 @@
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	finalWant := map[string]int{
 		"common_dep_1{variant:a}":                   1,
@@ -249,7 +249,7 @@
 		}
 	}
 
-	emptyTestFixtureFactory.RunTest(t,
+	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
 				ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
@@ -265,5 +265,5 @@
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(`test {name: "foo"}`),
-	)
+	).RunTest(t)
 }
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 1caf5a8..08e221a 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -633,21 +633,21 @@
 }
 
 func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []error) {
-	result := emptyTestFixtureFactory.
+	result := GroupFixturePreparers(
+		FixtureModifyContext(func(ctx *TestContext) {
+			ctx.RegisterModuleType("test_module", newTestModule)
+			ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
+			ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
+			ctx.PreArchMutators(RegisterNamespaceMutator)
+			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+				ctx.BottomUp("rename", renameMutator)
+			})
+		}),
+		bps.AddToFixture(),
+	).
 		// Ignore errors for now so tests can check them later.
 		ExtendWithErrorHandler(FixtureIgnoreErrors).
-		RunTest(t,
-			FixtureModifyContext(func(ctx *TestContext) {
-				ctx.RegisterModuleType("test_module", newTestModule)
-				ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
-				ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
-				ctx.PreArchMutators(RegisterNamespaceMutator)
-				ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-					ctx.BottomUp("rename", renameMutator)
-				})
-			}),
-			bps.AddToFixture(),
-		)
+		RunTest(t)
 
 	return result.TestContext, result.Errs
 }
@@ -697,7 +697,7 @@
 		testModule, ok := candidate.(*testModule)
 		if ok {
 			if testModule.properties.Id == id {
-				module = TestingModule{testModule}
+				module = newTestingModule(ctx.config, testModule)
 			}
 		}
 	}
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 5ac97e7..b8ef0f5 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -299,18 +299,17 @@
 func TestNeverallow(t *testing.T) {
 	for _, test := range neverallowTests {
 		t.Run(test.name, func(t *testing.T) {
-			emptyTestFixtureFactory.
-				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
-				RunTest(t,
-					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)
-						}
-					}),
-					test.fs.AddToFixture(),
-				)
+			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)
+					}
+				}),
+				test.fs.AddToFixture(),
+			).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+				RunTest(t)
 		})
 	}
 }
diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go
index 7e5864d..947c257 100644
--- a/android/ninja_deps_test.go
+++ b/android/ninja_deps_test.go
@@ -57,13 +57,13 @@
 		"test_ninja_deps/exists": nil,
 	}
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
 			ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
 		}),
 		fs.AddToFixture(),
-	)
+	).RunTest(t)
 
 	// Verify that the ninja file has a dependency on the test_ninja_deps directory.
 	if g, w := result.NinjaDeps, "test_ninja_deps"; !InList(w, g) {
diff --git a/android/package_test.go b/android/package_test.go
index d5b4db4..3bd30cc 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -61,13 +61,13 @@
 func TestPackage(t *testing.T) {
 	for _, test := range packageTests {
 		t.Run(test.name, func(t *testing.T) {
-			emptyTestFixtureFactory.
+			GroupFixturePreparers(
+				PrepareForTestWithArchMutator,
+				PrepareForTestWithPackageModule,
+				test.fs.AddToFixture(),
+			).
 				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
-				RunTest(t,
-					PrepareForTestWithArchMutator,
-					PrepareForTestWithPackageModule,
-					test.fs.AddToFixture(),
-				)
+				RunTest(t)
 		})
 	}
 }
diff --git a/android/packaging.go b/android/packaging.go
index 9b901ce..72c0c17 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -59,7 +59,8 @@
 	packagingBase() *PackagingBase
 
 	// AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
-	// When adding the dependencies, depTag is used as the tag.
+	// When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to
+	// be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
 	AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
 
 	// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
@@ -167,6 +168,24 @@
 	return ret
 }
 
+// PackagingItem is a marker interface for dependency tags.
+// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
+type PackagingItem interface {
+	// IsPackagingItem returns true if the dep is to be packaged
+	IsPackagingItem() bool
+}
+
+// DepTag provides default implementation of PackagingItem interface.
+// PackagingBase-derived modules can define their own dependency tag by embedding this, which
+// can be passed to AddDeps() or AddDependencies().
+type PackagingItemAlwaysDepTag struct {
+}
+
+// IsPackagingItem returns true if the dep is to be packaged
+func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
+	return true
+}
+
 // See PackageModule.AddDeps
 func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
 	for _, t := range p.getSupportedTargets(ctx) {
@@ -182,16 +201,15 @@
 // See PackageModule.CopyDepsToZip
 func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
 	m := make(map[string]PackagingSpec)
-	ctx.WalkDeps(func(child Module, parent Module) bool {
-		if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
-			return false
+	ctx.VisitDirectDeps(func(child Module) {
+		if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
+			return
 		}
-		for _, ps := range child.PackagingSpecs() {
+		for _, ps := range child.TransitivePackagingSpecs() {
 			if _, ok := m[ps.relPathInPackage]; !ok {
 				m[ps.relPathInPackage] = ps
 			}
 		}
-		return true
 	})
 
 	builder := NewRuleBuilder(pctx, ctx)
diff --git a/android/packaging_test.go b/android/packaging_test.go
index eb7f26f..f91dc5d 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -56,7 +56,9 @@
 type packageTestModule struct {
 	ModuleBase
 	PackagingBase
-
+	properties struct {
+		Install_deps []string `android:`
+	}
 	entries []string
 }
 
@@ -64,6 +66,7 @@
 	module := &packageTestModule{}
 	InitPackageModule(module)
 	InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+	module.AddProperties(&module.properties)
 	return module
 }
 
@@ -71,11 +74,18 @@
 	module := &packageTestModule{}
 	InitPackageModule(module)
 	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+	module.AddProperties(&module.properties)
 	return module
 }
 
+type packagingDepTag struct {
+	blueprint.BaseDependencyTag
+	PackagingItemAlwaysDepTag
+}
+
 func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
-	m.AddDeps(ctx, installDepTag{})
+	m.AddDeps(ctx, packagingDepTag{})
+	ctx.AddDependency(ctx.Module(), installDepTag{}, m.properties.Install_deps...)
 }
 
 func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -96,14 +106,14 @@
 		moduleFactory = packageTestModuleFactory
 	}
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		PrepareForTestWithArchMutator,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("component", componentTestModuleFactory)
 			ctx.RegisterModuleType("package_module", moduleFactory)
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	p := result.Module("package", archVariant).(*packageTestModule)
 	actual := p.entries
@@ -337,4 +347,21 @@
 			},
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
+
+	runPackagingTest(t, multiTarget,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			install_deps: ["bar"],
+		}
+		`, []string{"lib64/foo"})
 }
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index 8726ea7..568f868 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -157,14 +157,14 @@
 				}
 			`
 
-			result := emptyTestFixtureFactory.RunTest(t,
+			result := GroupFixturePreparers(
 				PrepareForTestWithArchMutator,
 				PrepareForTestWithFilegroup,
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
 				}),
 				FixtureWithRootAndroidBp(bp),
-			)
+			).RunTest(t)
 
 			m := result.Module("foo", "android_arm64_armv8-a").(*pathDepsMutatorTestModule)
 
diff --git a/android/paths.go b/android/paths.go
index f648c55..babf48c 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -511,6 +511,9 @@
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
+	if aModule, ok := module.(Module); ok && !aModule.Enabled() {
+		return nil, missingDependencyError{[]string{moduleName}}
+	}
 	if outProducer, ok := module.(OutputFileProducer); ok {
 		outputFiles, err := outProducer.OutputFiles(tag)
 		if err != nil {
diff --git a/android/paths_test.go b/android/paths_test.go
index c5fc10e..465ea3b 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1005,14 +1005,14 @@
 				"foo/src_special/$": nil,
 			}
 
-			result := emptyTestFixtureFactory.RunTest(t,
+			result := GroupFixturePreparers(
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
 					ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
 					ctx.RegisterModuleType("filegroup", FileGroupFactory)
 				}),
 				mockFS.AddToFixture(),
-			)
+			).RunTest(t)
 
 			m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
 
@@ -1203,13 +1203,13 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		PrepareForTestWithAllowMissingDependencies,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
 
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 32af5df..ced37fe 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -284,7 +284,7 @@
 				t.Errorf("windows is assumed to be disabled by default")
 			}
 
-			result := emptyTestFixtureFactory.Extend(
+			result := GroupFixturePreparers(
 				PrepareForTestWithArchMutator,
 				PrepareForTestWithPrebuilts,
 				PrepareForTestWithOverrides,
diff --git a/android/register.go b/android/register.go
index c9e66e9..aeaa6ff 100644
--- a/android/register.go
+++ b/android/register.go
@@ -206,7 +206,6 @@
 
 		// Register env and ninjadeps last so that they can track all used environment variables and
 		// Ninja file dependencies stored in the config.
-		singleton{false, "env", EnvSingleton},
 		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
 	)
 
@@ -263,8 +262,9 @@
 //   ctx := android.NewTestContext(config)
 //   RegisterBuildComponents(ctx)
 var InitRegistrationContext RegistrationContext = &initRegistrationContext{
-	moduleTypes:    make(map[string]ModuleFactory),
-	singletonTypes: make(map[string]SingletonFactory),
+	moduleTypes:       make(map[string]ModuleFactory),
+	singletonTypes:    make(map[string]SingletonFactory),
+	preSingletonTypes: make(map[string]SingletonFactory),
 }
 
 // Make sure the TestContext implements RegistrationContext.
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 3415aed..9cd60a2 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -542,11 +542,11 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		prepareForRuleBuilderTest,
 		FixtureWithRootAndroidBp(bp),
 		fs.AddToFixture(),
-	)
+	).RunTest(t)
 
 	check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
 		t.Helper()
@@ -562,46 +562,44 @@
 		AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat)
 
 		wantImplicits := append([]string{"bar"}, extraImplicits...)
-		AssertArrayString(t, "Implicits", wantImplicits, params.Implicits.Strings())
+		AssertPathsRelativeToTopEquals(t, "Implicits", wantImplicits, params.Implicits)
 
-		AssertStringEquals(t, "Output", wantOutput, params.Output.String())
+		AssertPathRelativeToTopEquals(t, "Output", wantOutput, params.Output)
 
 		if len(params.ImplicitOutputs) != 0 {
 			t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
 		}
 
-		AssertStringEquals(t, "Depfile", wantDepfile, params.Depfile.String())
+		AssertPathRelativeToTopEquals(t, "Depfile", wantDepfile, params.Depfile)
 
 		if params.Deps != blueprint.DepsGCC {
 			t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
 		}
 	}
 
-	buildDir := result.Config.BuildDir()
-
 	t.Run("module", func(t *testing.T) {
-		outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
-		check(t, result.ModuleForTests("foo", "").Rule("rule"),
+		outFile := "out/soong/.intermediates/foo/gen/foo"
+		check(t, result.ModuleForTests("foo", "").Rule("rule").RelativeToTop(),
 			"cp bar "+outFile,
 			outFile, outFile+".d", true, nil, nil)
 	})
 	t.Run("sbox", func(t *testing.T) {
-		outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
+		outDir := "out/soong/.intermediates/foo_sbox"
 		outFile := filepath.Join(outDir, "gen/foo_sbox")
 		depFile := filepath.Join(outDir, "gen/foo_sbox.d")
 		manifest := filepath.Join(outDir, "sbox.textproto")
-		sbox := filepath.Join(buildDir, "host", result.Config.PrebuiltOS(), "bin/sbox")
-		sandboxPath := shared.TempDirForOutDir(buildDir)
+		sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
+		sandboxPath := shared.TempDirForOutDir("out/soong")
 
 		cmd := `rm -rf ` + outDir + `/gen && ` +
 			sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
 
-		check(t, result.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
+		check(t, result.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox").RelativeToTop(),
 			cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
 	})
 	t.Run("singleton", func(t *testing.T) {
-		outFile := filepath.Join(buildDir, "singleton/gen/baz")
-		check(t, result.SingletonForTests("rule_builder_test").Rule("rule"),
+		outFile := filepath.Join("out/soong/singleton/gen/baz")
+		check(t, result.SingletonForTests("rule_builder_test").Rule("rule").RelativeToTop(),
 			"cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
 	})
 }
@@ -651,10 +649,10 @@
 		},
 	}
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		prepareForRuleBuilderTest,
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index 41dd4bb..eb5554c 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -56,11 +56,10 @@
 			name: "test_singleton_module",
 		}
 	`
-	result := emptyTestFixtureFactory.
-		RunTest(t,
-			prepareForSingletonModuleTest,
-			FixtureWithRootAndroidBp(bp),
-		)
+	result := GroupFixturePreparers(
+		prepareForSingletonModuleTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
 
 	ops := result.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
 	wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
@@ -78,19 +77,16 @@
 		}
 	`
 
-	emptyTestFixtureFactory.
+	prepareForSingletonModuleTest.
 		ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
 			`\QDuplicate SingletonModule "test_singleton_module", previously used in\E`,
-		})).RunTest(t,
-		prepareForSingletonModuleTest,
-		FixtureWithRootAndroidBp(bp),
-	)
+		})).RunTestWithBp(t, bp)
 }
 
 func TestUnusedSingletonModule(t *testing.T) {
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		prepareForSingletonModuleTest,
-	)
+	).RunTest(t)
 
 	singleton := result.SingletonForTests("test_singleton_module").Singleton()
 	sm := singleton.(*singletonModuleSingletonAdaptor).sm
@@ -113,17 +109,16 @@
 		}
 	`
 
-	emptyTestFixtureFactory.
+	GroupFixturePreparers(
+		prepareForSingletonModuleTest,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+				ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+			})
+		}),
+	).
 		ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
 			`\QGenerateAndroidBuildActions already called for variant\E`,
 		})).
-		RunTest(t,
-			prepareForSingletonModuleTest,
-			FixtureRegisterWithContext(func(ctx RegistrationContext) {
-				ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-					ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
-				})
-			}),
-			FixtureWithRootAndroidBp(bp),
-		)
+		RunTestWithBp(t, bp)
 }
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index a72b160..8f252d9 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -278,7 +278,7 @@
 
 		for _, tc := range testCases {
 			t.Run(tc.name, func(t *testing.T) {
-				result := emptyTestFixtureFactory.RunTest(t,
+				result := GroupFixturePreparers(
 					tc.preparer,
 					PrepareForTestWithDefaults,
 					FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -291,7 +291,7 @@
 					}),
 					fs.AddToFixture(),
 					FixtureWithRootAndroidBp(bp),
-				)
+				).RunTest(t)
 
 				foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
 				AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
diff --git a/android/testing.go b/android/testing.go
index af360fa..0dfe38c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -401,9 +401,6 @@
 	globalOrder.mutatorOrder.enforceOrdering(mutators)
 	mutators.registerAll(ctx.Context)
 
-	// Register the env singleton with this context before sorting.
-	ctx.RegisterSingletonType("env", EnvSingleton)
-
 	// Ensure that the singletons used in the test are in the same order as they are used at runtime.
 	globalOrder.singletonOrder.enforceOrdering(ctx.singletons)
 	ctx.singletons.registerAll(ctx.Context)
@@ -479,7 +476,7 @@
 		}
 	}
 
-	return TestingModule{module}
+	return newTestingModule(ctx.config, module)
 }
 
 func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
@@ -499,8 +496,8 @@
 		n := ctx.SingletonName(s)
 		if n == name {
 			return TestingSingleton{
-				singleton: s.(*singletonAdaptor).Singleton,
-				provider:  s.(testBuildProvider),
+				baseTestingComponent: newBaseTestingComponent(ctx.config, s.(testBuildProvider)),
+				singleton:            s.(*singletonAdaptor).Singleton,
 			}
 		}
 		allSingletonNames = append(allSingletonNames, n)
@@ -522,62 +519,170 @@
 type TestingBuildParams struct {
 	BuildParams
 	RuleParams blueprint.RuleParams
+
+	config Config
 }
 
-func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams {
+// RelativeToTop creates a new instance of this which has had any usages of the current test's
+// temporary and test specific build directory replaced with a path relative to the notional top.
+//
+// The parts of this structure which are changed are:
+// * BuildParams
+//   * Args
+//   * Path instances are intentionally not modified, use AssertPathRelativeToTopEquals or
+//     AssertPathsRelativeToTopEquals instead which do something similar.
+//
+// * RuleParams
+//   * Command
+//   * Depfile
+//   * Rspfile
+//   * RspfileContent
+//   * SymlinkOutputs
+//   * CommandDeps
+//   * CommandOrderOnly
+//
+// See PathRelativeToTop for more details.
+func (p TestingBuildParams) RelativeToTop() TestingBuildParams {
+	// If this is not a valid params then just return it back. That will make it easy to use with the
+	// Maybe...() methods.
+	if p.Rule == nil {
+		return p
+	}
+	if p.config.config == nil {
+		panic("cannot call RelativeToTop() on a TestingBuildParams previously returned by RelativeToTop()")
+	}
+	// Take a copy of the build params and replace any args that contains test specific temporary
+	// paths with paths relative to the top.
+	bparams := p.BuildParams
+	bparams.Args = normalizeStringMapRelativeToTop(p.config, bparams.Args)
+
+	// Ditto for any fields in the RuleParams.
+	rparams := p.RuleParams
+	rparams.Command = normalizeStringRelativeToTop(p.config, rparams.Command)
+	rparams.Depfile = normalizeStringRelativeToTop(p.config, rparams.Depfile)
+	rparams.Rspfile = normalizeStringRelativeToTop(p.config, rparams.Rspfile)
+	rparams.RspfileContent = normalizeStringRelativeToTop(p.config, rparams.RspfileContent)
+	rparams.SymlinkOutputs = normalizeStringArrayRelativeToTop(p.config, rparams.SymlinkOutputs)
+	rparams.CommandDeps = normalizeStringArrayRelativeToTop(p.config, rparams.CommandDeps)
+	rparams.CommandOrderOnly = normalizeStringArrayRelativeToTop(p.config, rparams.CommandOrderOnly)
+
 	return TestingBuildParams{
 		BuildParams: bparams,
-		RuleParams:  provider.RuleParamsForTests()[bparams.Rule],
+		RuleParams:  rparams,
 	}
 }
 
-func maybeBuildParamsFromRule(provider testBuildProvider, rule string) (TestingBuildParams, []string) {
+// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
+type baseTestingComponent struct {
+	config   Config
+	provider testBuildProvider
+}
+
+func newBaseTestingComponent(config Config, provider testBuildProvider) baseTestingComponent {
+	return baseTestingComponent{config, provider}
+}
+
+// A function that will normalize a string containing paths, e.g. ninja command, by replacing
+// any references to the test specific temporary build directory that changes with each run to a
+// fixed path relative to a notional top directory.
+//
+// This is similar to StringPathRelativeToTop except that assumes the string is a single path
+// containing at most one instance of the temporary build directory at the start of the path while
+// this assumes that there can be any number at any position.
+func normalizeStringRelativeToTop(config Config, s string) string {
+	// The buildDir usually looks something like: /tmp/testFoo2345/001
+	//
+	// Replace any usage of the buildDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+	// "out/soong".
+	outSoongDir := filepath.Clean(config.buildDir)
+	re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
+	s = re.ReplaceAllString(s, "out/soong")
+
+	// Replace any usage of the buildDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+	// "out". This must come after the previous replacement otherwise this would replace
+	// "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
+	outDir := filepath.Dir(outSoongDir)
+	re = regexp.MustCompile(`\Q` + outDir + `\E\b`)
+	s = re.ReplaceAllString(s, "out")
+
+	return s
+}
+
+// normalizeStringArrayRelativeToTop creates a new slice constructed by applying
+// normalizeStringRelativeToTop to each item in the slice.
+func normalizeStringArrayRelativeToTop(config Config, slice []string) []string {
+	newSlice := make([]string, len(slice))
+	for i, s := range slice {
+		newSlice[i] = normalizeStringRelativeToTop(config, s)
+	}
+	return newSlice
+}
+
+// normalizeStringMapRelativeToTop creates a new map constructed by applying
+// normalizeStringRelativeToTop to each value in the map.
+func normalizeStringMapRelativeToTop(config Config, m map[string]string) map[string]string {
+	newMap := map[string]string{}
+	for k, v := range m {
+		newMap[k] = normalizeStringRelativeToTop(config, v)
+	}
+	return newMap
+}
+
+func (b baseTestingComponent) newTestingBuildParams(bparams BuildParams) TestingBuildParams {
+	return TestingBuildParams{
+		config:      b.config,
+		BuildParams: bparams,
+		RuleParams:  b.provider.RuleParamsForTests()[bparams.Rule],
+	}
+}
+
+func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
 	var searchedRules []string
-	for _, p := range provider.BuildParamsForTests() {
+	for _, p := range b.provider.BuildParamsForTests() {
 		searchedRules = append(searchedRules, p.Rule.String())
 		if strings.Contains(p.Rule.String(), rule) {
-			return newTestingBuildParams(provider, p), searchedRules
+			return b.newTestingBuildParams(p), searchedRules
 		}
 	}
 	return TestingBuildParams{}, searchedRules
 }
 
-func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
-	p, searchRules := maybeBuildParamsFromRule(provider, rule)
+func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
+	p, searchRules := b.maybeBuildParamsFromRule(rule)
 	if p.Rule == nil {
 		panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
 	}
 	return p
 }
 
-func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
-	for _, p := range provider.BuildParamsForTests() {
+func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) TestingBuildParams {
+	for _, p := range b.provider.BuildParamsForTests() {
 		if strings.Contains(p.Description, desc) {
-			return newTestingBuildParams(provider, p)
+			return b.newTestingBuildParams(p)
 		}
 	}
 	return TestingBuildParams{}
 }
 
-func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
-	p := maybeBuildParamsFromDescription(provider, desc)
+func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
+	p := b.maybeBuildParamsFromDescription(desc)
 	if p.Rule == nil {
 		panic(fmt.Errorf("couldn't find description %q", desc))
 	}
 	return p
 }
 
-func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
+func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) {
 	var searchedOutputs []string
-	for _, p := range provider.BuildParamsForTests() {
+	for _, p := range b.provider.BuildParamsForTests() {
 		outputs := append(WritablePaths(nil), p.Outputs...)
 		outputs = append(outputs, p.ImplicitOutputs...)
 		if p.Output != nil {
 			outputs = append(outputs, p.Output)
 		}
 		for _, f := range outputs {
-			if f.String() == file || f.Rel() == file {
-				return newTestingBuildParams(provider, p), nil
+			if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file {
+				return b.newTestingBuildParams(p), nil
 			}
 			searchedOutputs = append(searchedOutputs, f.Rel())
 		}
@@ -585,18 +690,18 @@
 	return TestingBuildParams{}, searchedOutputs
 }
 
-func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams {
-	p, searchedOutputs := maybeBuildParamsFromOutput(provider, file)
+func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
+	p, searchedOutputs := b.maybeBuildParamsFromOutput(file)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
-			file, searchedOutputs))
+		panic(fmt.Errorf("couldn't find output %q.\nall outputs:\n    %s\n",
+			file, strings.Join(searchedOutputs, "\n    ")))
 	}
 	return p
 }
 
-func allOutputs(provider testBuildProvider) []string {
+func (b baseTestingComponent) allOutputs() []string {
 	var outputFullPaths []string
-	for _, p := range provider.BuildParamsForTests() {
+	for _, p := range b.provider.BuildParamsForTests() {
 		outputs := append(WritablePaths(nil), p.Outputs...)
 		outputs = append(outputs, p.ImplicitOutputs...)
 		if p.Output != nil {
@@ -607,64 +712,78 @@
 	return outputFullPaths
 }
 
+// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
+// BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeRule(rule string) TestingBuildParams {
+	r, _ := b.maybeBuildParamsFromRule(rule)
+	return r
+}
+
+// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
+func (b baseTestingComponent) Rule(rule string) TestingBuildParams {
+	return b.buildParamsFromRule(rule)
+}
+
+// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
+// BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
+	return b.maybeBuildParamsFromDescription(desc)
+}
+
+// Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
+// found.
+func (b baseTestingComponent) Description(desc string) TestingBuildParams {
+	return b.buildParamsFromDescription(desc)
+}
+
+// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string.  Returns an empty BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeOutput(file string) TestingBuildParams {
+	p, _ := b.maybeBuildParamsFromOutput(file)
+	return p
+}
+
+// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string.  Panics if no rule is found.
+func (b baseTestingComponent) Output(file string) TestingBuildParams {
+	return b.buildParamsFromOutput(file)
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (b baseTestingComponent) AllOutputs() []string {
+	return b.allOutputs()
+}
+
 // TestingModule is wrapper around an android.Module that provides methods to find information about individual
 // ctx.Build parameters for verification in tests.
 type TestingModule struct {
+	baseTestingComponent
 	module Module
 }
 
+func newTestingModule(config Config, module Module) TestingModule {
+	return TestingModule{
+		newBaseTestingComponent(config, module),
+		module,
+	}
+}
+
 // Module returns the Module wrapped by the TestingModule.
 func (m TestingModule) Module() Module {
 	return m.module
 }
 
-// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
-// BuildParams if no rule is found.
-func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
-	r, _ := maybeBuildParamsFromRule(m.module, rule)
-	return r
-}
-
-// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
-func (m TestingModule) Rule(rule string) TestingBuildParams {
-	return buildParamsFromRule(m.module, rule)
-}
-
-// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
-// BuildParams if no rule is found.
-func (m TestingModule) MaybeDescription(desc string) TestingBuildParams {
-	return maybeBuildParamsFromDescription(m.module, desc)
-}
-
-// Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
-// found.
-func (m TestingModule) Description(desc string) TestingBuildParams {
-	return buildParamsFromDescription(m.module, desc)
-}
-
-// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string.  Returns an empty BuildParams if no rule is found.
-func (m TestingModule) MaybeOutput(file string) TestingBuildParams {
-	p, _ := maybeBuildParamsFromOutput(m.module, file)
-	return p
-}
-
-// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string.  Panics if no rule is found.
-func (m TestingModule) Output(file string) TestingBuildParams {
-	return buildParamsFromOutput(m.module, file)
-}
-
-// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
-func (m TestingModule) AllOutputs() []string {
-	return allOutputs(m.module)
+// VariablesForTestsRelativeToTop returns a copy of the Module.VariablesForTests() with every value
+// having any temporary build dir usages replaced with paths relative to a notional top.
+func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string {
+	return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
 }
 
 // TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
 // ctx.Build parameters for verification in tests.
 type TestingSingleton struct {
+	baseTestingComponent
 	singleton Singleton
-	provider  testBuildProvider
 }
 
 // Singleton returns the Singleton wrapped by the TestingSingleton.
@@ -672,48 +791,6 @@
 	return s.singleton
 }
 
-// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
-// BuildParams if no rule is found.
-func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
-	r, _ := maybeBuildParamsFromRule(s.provider, rule)
-	return r
-}
-
-// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
-func (s TestingSingleton) Rule(rule string) TestingBuildParams {
-	return buildParamsFromRule(s.provider, rule)
-}
-
-// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
-// BuildParams if no rule is found.
-func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams {
-	return maybeBuildParamsFromDescription(s.provider, desc)
-}
-
-// Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
-// found.
-func (s TestingSingleton) Description(desc string) TestingBuildParams {
-	return buildParamsFromDescription(s.provider, desc)
-}
-
-// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string.  Returns an empty BuildParams if no rule is found.
-func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams {
-	p, _ := maybeBuildParamsFromOutput(s.provider, file)
-	return p
-}
-
-// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string.  Panics if no rule is found.
-func (s TestingSingleton) Output(file string) TestingBuildParams {
-	return buildParamsFromOutput(s.provider, file)
-}
-
-// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
-func (s TestingSingleton) AllOutputs() []string {
-	return allOutputs(s.provider)
-}
-
 func FailIfErrored(t *testing.T, errs []error) {
 	t.Helper()
 	if len(errs) > 0 {
diff --git a/android/variable.go b/android/variable.go
index 776a5c7..2ab51c7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -341,6 +341,8 @@
 
 	DexpreoptGlobalConfig *string `json:",omitempty"`
 
+	WithDexpreopt bool `json:",omitempty"`
+
 	ManifestPackageNameOverrides []string `json:",omitempty"`
 	CertificateOverrides         []string `json:",omitempty"`
 	PackageNameOverrides         []string `json:",omitempty"`
diff --git a/android/variable_test.go b/android/variable_test.go
index d16e458..928bca6 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -182,7 +182,7 @@
 		}
 	`
 
-	emptyTestFixtureFactory.RunTest(t,
+	GroupFixturePreparers(
 		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
 			variables.Eng = proptools.BoolPtr(true)
 		}),
@@ -204,7 +204,7 @@
 			})
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 }
 
 var testProductVariableDefaultsProperties = struct {
@@ -288,7 +288,7 @@
 		}
 	`
 
-	result := emptyTestFixtureFactory.RunTest(t,
+	result := GroupFixturePreparers(
 		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
 			variables.Eng = boolPtr(true)
 		}),
@@ -299,7 +299,7 @@
 			ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
 		}),
 		FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	foo := result.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
 
diff --git a/android/visibility_test.go b/android/visibility_test.go
index fdf18ce..ffd7909 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1142,7 +1142,7 @@
 func TestVisibility(t *testing.T) {
 	for _, test := range visibilityTests {
 		t.Run(test.name, func(t *testing.T) {
-			result := emptyTestFixtureFactory.Extend(
+			result := GroupFixturePreparers(
 				// General preparers in alphabetical order as test infrastructure will enforce correct
 				// registration order.
 				PrepareForTestWithArchMutator,
diff --git a/apex/OWNERS b/apex/OWNERS
index fee739b..8e4ba5c 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,4 +1 @@
 per-file * = jiyong@google.com
-
-per-file allowed_deps.txt = set noparent
-per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,jham@google.com
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
deleted file mode 100644
index 154b9aa..0000000
--- a/apex/allowed_deps.txt
+++ /dev/null
@@ -1,652 +0,0 @@
-# A list of allowed dependencies for all updatable modules.
-#
-# The list tracks all direct and transitive dependencies that end up within any
-# of the updatable binaries; specifically excluding external dependencies
-# required to compile those binaries. This prevents potential regressions in
-# case a new dependency is not aware of the different functional and
-# non-functional requirements being part of an updatable module, for example
-# setting correct min_sdk_version.
-#
-# To update the list, run:
-# repo-root$ build/soong/scripts/update-apex-allowed-deps.sh
-#
-# See go/apex-allowed-deps-error for more details.
-# TODO(b/157465465): introduce automated quality signals and remove this list.
-
-adbd(minSdkVersion:(no version))
-android.hardware.cas.native@1.0(minSdkVersion:29)
-android.hardware.cas@1.0(minSdkVersion:29)
-android.hardware.common-ndk_platform(minSdkVersion:29)
-android.hardware.common-unstable-ndk_platform(minSdkVersion:29)
-android.hardware.common-V2-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.allocator@2.0(minSdkVersion:29)
-android.hardware.graphics.allocator@3.0(minSdkVersion:29)
-android.hardware.graphics.allocator@4.0(minSdkVersion:29)
-android.hardware.graphics.bufferqueue@1.0(minSdkVersion:29)
-android.hardware.graphics.bufferqueue@2.0(minSdkVersion:29)
-android.hardware.graphics.common-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.common-unstable-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.common-V2-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.common@1.0(minSdkVersion:29)
-android.hardware.graphics.common@1.1(minSdkVersion:29)
-android.hardware.graphics.common@1.2(minSdkVersion:29)
-android.hardware.graphics.mapper@2.0(minSdkVersion:29)
-android.hardware.graphics.mapper@2.1(minSdkVersion:29)
-android.hardware.graphics.mapper@3.0(minSdkVersion:29)
-android.hardware.graphics.mapper@4.0(minSdkVersion:29)
-android.hardware.media.bufferpool@2.0(minSdkVersion:29)
-android.hardware.media.c2@1.0(minSdkVersion:29)
-android.hardware.media.c2@1.1(minSdkVersion:29)
-android.hardware.media.omx@1.0(minSdkVersion:29)
-android.hardware.media@1.0(minSdkVersion:29)
-android.hardware.neuralnetworks-V1-ndk_platform(minSdkVersion:30)
-android.hardware.neuralnetworks@1.0(minSdkVersion:30)
-android.hardware.neuralnetworks@1.1(minSdkVersion:30)
-android.hardware.neuralnetworks@1.2(minSdkVersion:30)
-android.hardware.neuralnetworks@1.3(minSdkVersion:30)
-android.hardware.tetheroffload.config-V1.0-java(minSdkVersion:current)
-android.hardware.tetheroffload.control-V1.0-java(minSdkVersion:current)
-android.hidl.allocator@1.0(minSdkVersion:29)
-android.hidl.base-V1.0-java(minSdkVersion:current)
-android.hidl.memory.token@1.0(minSdkVersion:29)
-android.hidl.memory@1.0(minSdkVersion:29)
-android.hidl.safe_union@1.0(minSdkVersion:29)
-android.hidl.token@1.0(minSdkVersion:29)
-android.hidl.token@1.0-utils(minSdkVersion:29)
-android.net.ipsec.ike(minSdkVersion:30)
-android.net.ipsec.ike(minSdkVersion:current)
-android.net.ipsec.ike.xml(minSdkVersion:(no version))
-androidx-constraintlayout_constraintlayout(minSdkVersion:14)
-androidx-constraintlayout_constraintlayout-solver(minSdkVersion:24)
-androidx.activity_activity(minSdkVersion:14)
-androidx.activity_activity-ktx(minSdkVersion:14)
-androidx.annotation_annotation(minSdkVersion:24)
-androidx.annotation_annotation(minSdkVersion:current)
-androidx.appcompat_appcompat(minSdkVersion:14)
-androidx.appcompat_appcompat-resources(minSdkVersion:14)
-androidx.arch.core_core-common(minSdkVersion:24)
-androidx.arch.core_core-common(minSdkVersion:current)
-androidx.arch.core_core-runtime(minSdkVersion:14)
-androidx.asynclayoutinflater_asynclayoutinflater(minSdkVersion:14)
-androidx.autofill_autofill(minSdkVersion:14)
-androidx.cardview_cardview(minSdkVersion:14)
-androidx.collection_collection(minSdkVersion:24)
-androidx.collection_collection(minSdkVersion:current)
-androidx.collection_collection-ktx(minSdkVersion:24)
-androidx.coordinatorlayout_coordinatorlayout(minSdkVersion:14)
-androidx.core_core(minSdkVersion:14)
-androidx.core_core-ktx(minSdkVersion:14)
-androidx.cursoradapter_cursoradapter(minSdkVersion:14)
-androidx.customview_customview(minSdkVersion:14)
-androidx.documentfile_documentfile(minSdkVersion:14)
-androidx.drawerlayout_drawerlayout(minSdkVersion:14)
-androidx.dynamicanimation_dynamicanimation(minSdkVersion:14)
-androidx.fragment_fragment(minSdkVersion:14)
-androidx.fragment_fragment-ktx(minSdkVersion:14)
-androidx.interpolator_interpolator(minSdkVersion:14)
-androidx.leanback_leanback(minSdkVersion:17)
-androidx.leanback_leanback-preference(minSdkVersion:21)
-androidx.legacy_legacy-preference-v14(minSdkVersion:14)
-androidx.legacy_legacy-support-core-ui(minSdkVersion:14)
-androidx.legacy_legacy-support-core-utils(minSdkVersion:14)
-androidx.legacy_legacy-support-v13(minSdkVersion:14)
-androidx.legacy_legacy-support-v4(minSdkVersion:14)
-androidx.lifecycle_lifecycle-common(minSdkVersion:24)
-androidx.lifecycle_lifecycle-common(minSdkVersion:current)
-androidx.lifecycle_lifecycle-common-java8(minSdkVersion:24)
-androidx.lifecycle_lifecycle-extensions(minSdkVersion:14)
-androidx.lifecycle_lifecycle-livedata(minSdkVersion:14)
-androidx.lifecycle_lifecycle-livedata-core(minSdkVersion:14)
-androidx.lifecycle_lifecycle-livedata-core-ktx(minSdkVersion:14)
-androidx.lifecycle_lifecycle-process(minSdkVersion:14)
-androidx.lifecycle_lifecycle-runtime(minSdkVersion:14)
-androidx.lifecycle_lifecycle-runtime-ktx(minSdkVersion:14)
-androidx.lifecycle_lifecycle-service(minSdkVersion:14)
-androidx.lifecycle_lifecycle-viewmodel(minSdkVersion:14)
-androidx.lifecycle_lifecycle-viewmodel-ktx(minSdkVersion:14)
-androidx.lifecycle_lifecycle-viewmodel-savedstate(minSdkVersion:14)
-androidx.loader_loader(minSdkVersion:14)
-androidx.localbroadcastmanager_localbroadcastmanager(minSdkVersion:14)
-androidx.media_media(minSdkVersion:14)
-androidx.navigation_navigation-common(minSdkVersion:14)
-androidx.navigation_navigation-common-ktx(minSdkVersion:14)
-androidx.navigation_navigation-fragment(minSdkVersion:14)
-androidx.navigation_navigation-fragment-ktx(minSdkVersion:14)
-androidx.navigation_navigation-runtime(minSdkVersion:14)
-androidx.navigation_navigation-runtime-ktx(minSdkVersion:14)
-androidx.navigation_navigation-ui(minSdkVersion:14)
-androidx.navigation_navigation-ui-ktx(minSdkVersion:14)
-androidx.preference_preference(minSdkVersion:14)
-androidx.print_print(minSdkVersion:14)
-androidx.recyclerview_recyclerview(minSdkVersion:14)
-androidx.recyclerview_recyclerview-selection(minSdkVersion:14)
-androidx.savedstate_savedstate(minSdkVersion:14)
-androidx.slidingpanelayout_slidingpanelayout(minSdkVersion:14)
-androidx.swiperefreshlayout_swiperefreshlayout(minSdkVersion:14)
-androidx.transition_transition(minSdkVersion:14)
-androidx.vectordrawable_vectordrawable(minSdkVersion:14)
-androidx.vectordrawable_vectordrawable-animated(minSdkVersion:14)
-androidx.versionedparcelable_versionedparcelable(minSdkVersion:14)
-androidx.viewpager_viewpager(minSdkVersion:14)
-apache-commons-compress(minSdkVersion:current)
-art.module.public.api.stubs(minSdkVersion:(no version))
-bcm_object(minSdkVersion:29)
-bionic_libc_platform_headers(minSdkVersion:29)
-boringssl_self_test(minSdkVersion:29)
-bouncycastle_ike_digests(minSdkVersion:current)
-bpf_syscall_wrappers(minSdkVersion:30)
-brotli-java(minSdkVersion:current)
-captiveportal-lib(minSdkVersion:29)
-car-ui-lib(minSdkVersion:28)
-car-ui-lib-overlayable(minSdkVersion:28)
-CellBroadcastApp(minSdkVersion:29)
-CellBroadcastServiceModule(minSdkVersion:29)
-codecs_g711dec(minSdkVersion:29)
-com.google.android.material_material(minSdkVersion:14)
-conscrypt(minSdkVersion:29)
-conscrypt.module.platform.api.stubs(minSdkVersion:(no version))
-conscrypt.module.public.api.stubs(minSdkVersion:(no version))
-core-lambda-stubs(minSdkVersion:(no version))
-core.current.stubs(minSdkVersion:(no version))
-crtbegin_dynamic(minSdkVersion:16)
-crtbegin_dynamic(minSdkVersion:apex_inherit)
-crtbegin_dynamic1(minSdkVersion:16)
-crtbegin_dynamic1(minSdkVersion:apex_inherit)
-crtbegin_so(minSdkVersion:16)
-crtbegin_so(minSdkVersion:apex_inherit)
-crtbegin_so1(minSdkVersion:16)
-crtbegin_so1(minSdkVersion:apex_inherit)
-crtbrand(minSdkVersion:16)
-crtbrand(minSdkVersion:apex_inherit)
-crtend_android(minSdkVersion:16)
-crtend_android(minSdkVersion:apex_inherit)
-crtend_so(minSdkVersion:16)
-crtend_so(minSdkVersion:apex_inherit)
-datastallprotosnano(minSdkVersion:29)
-derive_classpath(minSdkVersion:30)
-derive_sdk(minSdkVersion:30)
-derive_sdk(minSdkVersion:current)
-derive_sdk_prefer32(minSdkVersion:30)
-derive_sdk_prefer32(minSdkVersion:current)
-dnsresolver_aidl_interface-lateststable-ndk_platform(minSdkVersion:29)
-dnsresolver_aidl_interface-unstable-ndk_platform(minSdkVersion:29)
-dnsresolver_aidl_interface-V7-ndk_platform(minSdkVersion:29)
-dnsresolver_aidl_interface-V8-ndk_platform(minSdkVersion:29)
-DocumentsUI-res-lib(minSdkVersion:29)
-exoplayer2-extractor(minSdkVersion:16)
-exoplayer2-extractor-annotation-stubs(minSdkVersion:16)
-ExtServices(minSdkVersion:current)
-ExtServices-core(minSdkVersion:current)
-flatbuffer_headers(minSdkVersion:(no version))
-fmtlib(minSdkVersion:29)
-fmtlib_ndk(minSdkVersion:29)
-framework-mediaprovider(minSdkVersion:30)
-framework-permission(minSdkVersion:30)
-framework-permission(minSdkVersion:current)
-framework-permission-s(minSdkVersion:30)
-framework-permission-s-shared(minSdkVersion:30)
-framework-sdkextensions(minSdkVersion:30)
-framework-sdkextensions(minSdkVersion:current)
-framework-statsd(minSdkVersion:30)
-framework-statsd(minSdkVersion:current)
-framework-tethering(minSdkVersion:30)
-framework-tethering(minSdkVersion:current)
-gemmlowp_headers(minSdkVersion:(no version))
-GoogleCellBroadcastApp(minSdkVersion:29)
-GoogleCellBroadcastServiceModule(minSdkVersion:29)
-GoogleExtServices(minSdkVersion:current)
-GooglePermissionController(minSdkVersion:30)
-guava(minSdkVersion:current)
-gwp_asan_headers(minSdkVersion:(no version))
-i18n.module.public.api.stubs(minSdkVersion:(no version))
-iconloader(minSdkVersion:21)
-ike-internals(minSdkVersion:current)
-InProcessTethering(minSdkVersion:30)
-InProcessTethering(minSdkVersion:current)
-ipmemorystore-aidl-interfaces-java(minSdkVersion:29)
-ipmemorystore-aidl-interfaces-unstable-java(minSdkVersion:29)
-ipmemorystore-aidl-interfaces-V10-java(minSdkVersion:29)
-ipmemorystore-aidl-interfaces-V11-java(minSdkVersion:29)
-jni_headers(minSdkVersion:29)
-jsr305(minSdkVersion:14)
-kotlinx-coroutines-android(minSdkVersion:current)
-kotlinx-coroutines-core(minSdkVersion:current)
-legacy.art.module.platform.api.stubs(minSdkVersion:(no version))
-legacy.core.platform.api.stubs(minSdkVersion:(no version))
-legacy.i18n.module.platform.api.stubs(minSdkVersion:(no version))
-libaacextractor(minSdkVersion:29)
-libadb_crypto(minSdkVersion:(no version))
-libadb_pairing_auth(minSdkVersion:(no version))
-libadb_pairing_connection(minSdkVersion:(no version))
-libadb_pairing_server(minSdkVersion:(no version))
-libadb_protos(minSdkVersion:(no version))
-libadb_sysdeps(minSdkVersion:apex_inherit)
-libadb_tls_connection(minSdkVersion:(no version))
-libadbconnection_client(minSdkVersion:(no version))
-libadbconnection_server(minSdkVersion:(no version))
-libadbd(minSdkVersion:(no version))
-libadbd_core(minSdkVersion:(no version))
-libadbd_services(minSdkVersion:(no version))
-liballoc.rust_sysroot(minSdkVersion:29)
-libamrextractor(minSdkVersion:29)
-libapp_processes_protos_lite(minSdkVersion:(no version))
-libarect(minSdkVersion:29)
-libasyncio(minSdkVersion:(no version))
-libatomic(minSdkVersion:(no version))
-libaudio_system_headers(minSdkVersion:29)
-libaudioclient_headers(minSdkVersion:29)
-libaudiofoundation_headers(minSdkVersion:29)
-libaudioutils(minSdkVersion:29)
-libaudioutils_fixedfft(minSdkVersion:29)
-libavcdec(minSdkVersion:29)
-libavcenc(minSdkVersion:29)
-libavservices_minijail(minSdkVersion:29)
-libbacktrace_headers(minSdkVersion:apex_inherit)
-libbacktrace_rs.rust_sysroot(minSdkVersion:29)
-libbacktrace_sys.rust_sysroot(minSdkVersion:29)
-libbase(minSdkVersion:29)
-libbase_headers(minSdkVersion:29)
-libbase_ndk(minSdkVersion:29)
-libbinder_headers(minSdkVersion:29)
-libbinder_headers_platform_shared(minSdkVersion:29)
-libbinderthreadstateutils(minSdkVersion:29)
-libbluetooth-types-header(minSdkVersion:29)
-libbrotli(minSdkVersion:(no version))
-libbuildversion(minSdkVersion:(no version))
-libc(minSdkVersion:(no version))
-libc++(minSdkVersion:apex_inherit)
-libc++_static(minSdkVersion:apex_inherit)
-libc++abi(minSdkVersion:apex_inherit)
-libc++demangle(minSdkVersion:apex_inherit)
-libc_headers(minSdkVersion:apex_inherit)
-libc_headers_arch(minSdkVersion:apex_inherit)
-libcap(minSdkVersion:29)
-libcfg_if(minSdkVersion:29)
-libcfg_if.rust_sysroot(minSdkVersion:29)
-libclang_rt.hwasan-aarch64-android.llndk(minSdkVersion:(no version))
-libcodec2(minSdkVersion:29)
-libcodec2_headers(minSdkVersion:29)
-libcodec2_hidl@1.0(minSdkVersion:29)
-libcodec2_hidl@1.1(minSdkVersion:29)
-libcodec2_internal(minSdkVersion:29)
-libcodec2_soft_aacdec(minSdkVersion:29)
-libcodec2_soft_aacenc(minSdkVersion:29)
-libcodec2_soft_amrnbdec(minSdkVersion:29)
-libcodec2_soft_amrnbenc(minSdkVersion:29)
-libcodec2_soft_amrwbdec(minSdkVersion:29)
-libcodec2_soft_amrwbenc(minSdkVersion:29)
-libcodec2_soft_av1dec_gav1(minSdkVersion:29)
-libcodec2_soft_avcdec(minSdkVersion:29)
-libcodec2_soft_avcenc(minSdkVersion:29)
-libcodec2_soft_common(minSdkVersion:29)
-libcodec2_soft_flacdec(minSdkVersion:29)
-libcodec2_soft_flacenc(minSdkVersion:29)
-libcodec2_soft_g711alawdec(minSdkVersion:29)
-libcodec2_soft_g711mlawdec(minSdkVersion:29)
-libcodec2_soft_gsmdec(minSdkVersion:29)
-libcodec2_soft_h263dec(minSdkVersion:29)
-libcodec2_soft_h263enc(minSdkVersion:29)
-libcodec2_soft_hevcdec(minSdkVersion:29)
-libcodec2_soft_hevcenc(minSdkVersion:29)
-libcodec2_soft_mp3dec(minSdkVersion:29)
-libcodec2_soft_mpeg2dec(minSdkVersion:29)
-libcodec2_soft_mpeg4dec(minSdkVersion:29)
-libcodec2_soft_mpeg4enc(minSdkVersion:29)
-libcodec2_soft_opusdec(minSdkVersion:29)
-libcodec2_soft_opusenc(minSdkVersion:29)
-libcodec2_soft_rawdec(minSdkVersion:29)
-libcodec2_soft_vorbisdec(minSdkVersion:29)
-libcodec2_soft_vp8dec(minSdkVersion:29)
-libcodec2_soft_vp8enc(minSdkVersion:29)
-libcodec2_soft_vp9dec(minSdkVersion:29)
-libcodec2_soft_vp9enc(minSdkVersion:29)
-libcodec2_vndk(minSdkVersion:29)
-libcompiler_builtins.rust_sysroot(minSdkVersion:29)
-libcore.rust_sysroot(minSdkVersion:29)
-libcrypto(minSdkVersion:29)
-libcrypto_static(minSdkVersion:(no version))
-libcrypto_utils(minSdkVersion:(no version))
-libcutils(minSdkVersion:29)
-libcutils_headers(minSdkVersion:29)
-libcutils_sockets(minSdkVersion:29)
-libderive_classpath(minSdkVersion:30)
-libderive_sdk(minSdkVersion:30)
-libdiagnose_usb(minSdkVersion:(no version))
-libdl(minSdkVersion:(no version))
-libdmabufheap(minSdkVersion:29)
-libeigen(minSdkVersion:(no version))
-libfifo(minSdkVersion:29)
-libFLAC(minSdkVersion:29)
-libFLAC-config(minSdkVersion:29)
-libFLAC-headers(minSdkVersion:29)
-libflacextractor(minSdkVersion:29)
-libfmq(minSdkVersion:29)
-libfmq-base(minSdkVersion:29)
-libFraunhoferAAC(minSdkVersion:29)
-libfuse(minSdkVersion:30)
-libfuse_jni(minSdkVersion:30)
-libgav1(minSdkVersion:29)
-libgcc(minSdkVersion:(no version))
-libgcc_stripped(minSdkVersion:(no version))
-libgetopts(minSdkVersion:29)
-libgralloctypes(minSdkVersion:29)
-libgrallocusage(minSdkVersion:29)
-libgsm(minSdkVersion:apex_inherit)
-libgtest_prod(minSdkVersion:apex_inherit)
-libgui_bufferqueue_static(minSdkVersion:29)
-libgui_headers(minSdkVersion:29)
-libhardware(minSdkVersion:29)
-libhardware_headers(minSdkVersion:29)
-libhashbrown.rust_sysroot(minSdkVersion:29)
-libhevcdec(minSdkVersion:29)
-libhevcenc(minSdkVersion:29)
-libhidlbase(minSdkVersion:29)
-libhidlmemory(minSdkVersion:29)
-libhwbinder-impl-internal(minSdkVersion:29)
-libhwbinder_headers(minSdkVersion:29)
-libion(minSdkVersion:29)
-libjavacrypto(minSdkVersion:29)
-libjsoncpp(minSdkVersion:29)
-liblazy_static(minSdkVersion:29)
-liblibc(minSdkVersion:29)
-liblibc.rust_sysroot(minSdkVersion:29)
-libLibGuiProperties(minSdkVersion:29)
-liblibm(minSdkVersion:29)
-liblog(minSdkVersion:(no version))
-liblog_headers(minSdkVersion:29)
-liblog_rust(minSdkVersion:29)
-liblua(minSdkVersion:(no version))
-liblz4(minSdkVersion:(no version))
-libm(minSdkVersion:(no version))
-libmath(minSdkVersion:29)
-libmdnssd(minSdkVersion:(no version))
-libmedia_codecserviceregistrant(minSdkVersion:29)
-libmedia_datasource_headers(minSdkVersion:29)
-libmedia_headers(minSdkVersion:29)
-libmedia_helper_headers(minSdkVersion:29)
-libmedia_midiiowrapper(minSdkVersion:29)
-libmediaparser-jni(minSdkVersion:29)
-libmidiextractor(minSdkVersion:29)
-libminijail(minSdkVersion:29)
-libminijail_gen_constants(minSdkVersion:(no version))
-libminijail_gen_constants_obj(minSdkVersion:29)
-libminijail_gen_syscall(minSdkVersion:(no version))
-libminijail_gen_syscall_obj(minSdkVersion:29)
-libminijail_generated(minSdkVersion:29)
-libmkvextractor(minSdkVersion:29)
-libmodules-utils-build(minSdkVersion:29)
-libmp3extractor(minSdkVersion:29)
-libmp4extractor(minSdkVersion:29)
-libmpeg2dec(minSdkVersion:29)
-libmpeg2extractor(minSdkVersion:29)
-libnativebase_headers(minSdkVersion:29)
-libnativehelper_compat_libc++(minSdkVersion:(no version))
-libnativehelper_header_only(minSdkVersion:29)
-libnativewindow_headers(minSdkVersion:29)
-libnetd_resolv(minSdkVersion:29)
-libnetdbinder_utils_headers(minSdkVersion:29)
-libnetdutils(minSdkVersion:29)
-libnetjniutils(minSdkVersion:29)
-libnetworkstackutilsjni(minSdkVersion:29)
-libneuralnetworks(minSdkVersion:(no version))
-libneuralnetworks_common(minSdkVersion:(no version))
-libneuralnetworks_headers(minSdkVersion:(no version))
-liboggextractor(minSdkVersion:29)
-libonce_cell(minSdkVersion:29)
-libopus(minSdkVersion:29)
-libpanic_unwind.rust_sysroot(minSdkVersion:29)
-libprocessgroup(minSdkVersion:29)
-libprocessgroup_headers(minSdkVersion:29)
-libprocpartition(minSdkVersion:(no version))
-libprofiler_builtins.rust_sysroot(minSdkVersion:29)
-libprotobuf-cpp-lite(minSdkVersion:29)
-libprotobuf-java-lite(minSdkVersion:current)
-libprotobuf-java-nano(minSdkVersion:9)
-libprotoutil(minSdkVersion:(no version))
-libqemu_pipe(minSdkVersion:(no version))
-libquiche_ffi(minSdkVersion:29)
-libring(minSdkVersion:29)
-libring-core(minSdkVersion:29)
-librustc_demangle.rust_sysroot(minSdkVersion:29)
-libruy_static(minSdkVersion:30)
-libsdk_proto(minSdkVersion:30)
-libsfplugin_ccodec_utils(minSdkVersion:29)
-libsonivoxwithoutjet(minSdkVersion:29)
-libspeexresampler(minSdkVersion:29)
-libspin(minSdkVersion:29)
-libssl(minSdkVersion:29)
-libstagefright_amrnb_common(minSdkVersion:29)
-libstagefright_amrnbdec(minSdkVersion:29)
-libstagefright_amrnbenc(minSdkVersion:29)
-libstagefright_amrwbdec(minSdkVersion:29)
-libstagefright_amrwbenc(minSdkVersion:29)
-libstagefright_bufferpool@2.0.1(minSdkVersion:29)
-libstagefright_bufferqueue_helper(minSdkVersion:29)
-libstagefright_enc_common(minSdkVersion:29)
-libstagefright_esds(minSdkVersion:29)
-libstagefright_flacdec(minSdkVersion:29)
-libstagefright_foundation(minSdkVersion:29)
-libstagefright_foundation_headers(minSdkVersion:29)
-libstagefright_foundation_without_imemory(minSdkVersion:29)
-libstagefright_headers(minSdkVersion:29)
-libstagefright_id3(minSdkVersion:29)
-libstagefright_m4vh263dec(minSdkVersion:29)
-libstagefright_m4vh263enc(minSdkVersion:29)
-libstagefright_metadatautils(minSdkVersion:29)
-libstagefright_mp3dec(minSdkVersion:29)
-libstagefright_mp3dec_headers(minSdkVersion:29)
-libstagefright_mpeg2extractor(minSdkVersion:29)
-libstagefright_mpeg2support_nocrypto(minSdkVersion:29)
-libstats_jni(minSdkVersion:(no version))
-libstats_jni(minSdkVersion:30)
-libstatslog_resolv(minSdkVersion:29)
-libstatslog_statsd(minSdkVersion:(no version))
-libstatslog_statsd(minSdkVersion:30)
-libstatspull(minSdkVersion:(no version))
-libstatspull(minSdkVersion:30)
-libstatspush_compat(minSdkVersion:29)
-libstatssocket(minSdkVersion:(no version))
-libstatssocket(minSdkVersion:30)
-libstatssocket_headers(minSdkVersion:29)
-libstd(minSdkVersion:29)
-libsystem_headers(minSdkVersion:apex_inherit)
-libsysutils(minSdkVersion:apex_inherit)
-libterm(minSdkVersion:29)
-libtest(minSdkVersion:29)
-libtetherutilsjni(minSdkVersion:30)
-libtetherutilsjni(minSdkVersion:current)
-libtextclassifier(minSdkVersion:(no version))
-libtextclassifier-java(minSdkVersion:current)
-libtextclassifier_hash_headers(minSdkVersion:(no version))
-libtextclassifier_hash_static(minSdkVersion:(no version))
-libtflite_kernel_utils(minSdkVersion:(no version))
-libtflite_static(minSdkVersion:(no version))
-libui(minSdkVersion:29)
-libui_headers(minSdkVersion:29)
-libunicode_width.rust_sysroot(minSdkVersion:29)
-libuntrusted(minSdkVersion:29)
-libunwind.rust_sysroot(minSdkVersion:29)
-libunwind_llvm(minSdkVersion:apex_inherit)
-libutf(minSdkVersion:(no version))
-libutils(minSdkVersion:apex_inherit)
-libutils_headers(minSdkVersion:apex_inherit)
-libvorbisidec(minSdkVersion:29)
-libvpx(minSdkVersion:29)
-libwatchdog(minSdkVersion:29)
-libwavextractor(minSdkVersion:29)
-libwebm(minSdkVersion:29)
-libyuv(minSdkVersion:29)
-libyuv_static(minSdkVersion:29)
-libzstd(minSdkVersion:(no version))
-media_ndk_headers(minSdkVersion:29)
-media_plugin_headers(minSdkVersion:29)
-MediaProvider(minSdkVersion:30)
-mediaswcodec(minSdkVersion:29)
-metrics-constants-protos(minSdkVersion:29)
-modules-annotation-minsdk(minSdkVersion:29)
-modules-utils-build(minSdkVersion:29)
-modules-utils-build_system(minSdkVersion:29)
-modules-utils-os(minSdkVersion:30)
-ndk_crtbegin_so.19(minSdkVersion:(no version))
-ndk_crtbegin_so.21(minSdkVersion:(no version))
-ndk_crtbegin_so.27(minSdkVersion:(no version))
-ndk_crtend_so.19(minSdkVersion:(no version))
-ndk_crtend_so.21(minSdkVersion:(no version))
-ndk_crtend_so.27(minSdkVersion:(no version))
-ndk_libc++_static(minSdkVersion:(no version))
-ndk_libc++_static(minSdkVersion:16)
-ndk_libc++abi(minSdkVersion:(no version))
-ndk_libc++abi(minSdkVersion:16)
-ndk_libunwind(minSdkVersion:16)
-net-utils-device-common(minSdkVersion:29)
-net-utils-framework-common(minSdkVersion:current)
-netd-client(minSdkVersion:29)
-netd_aidl_interface-java(minSdkVersion:29)
-netd_aidl_interface-lateststable-java(minSdkVersion:29)
-netd_aidl_interface-unstable-java(minSdkVersion:29)
-netd_aidl_interface-V5-java(minSdkVersion:29)
-netd_aidl_interface-V6-java(minSdkVersion:29)
-netd_event_listener_interface-java(minSdkVersion:29)
-netd_event_listener_interface-lateststable-ndk_platform(minSdkVersion:29)
-netd_event_listener_interface-ndk_platform(minSdkVersion:29)
-netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
-netd_event_listener_interface-V1-ndk_platform(minSdkVersion:29)
-netd_event_listener_interface-V2-ndk_platform(minSdkVersion:29)
-netlink-client(minSdkVersion:29)
-networkstack-aidl-interfaces-unstable-java(minSdkVersion:29)
-networkstack-aidl-interfaces-V10-java(minSdkVersion:29)
-networkstack-client(minSdkVersion:29)
-NetworkStackApi29Shims(minSdkVersion:29)
-NetworkStackApi30Shims(minSdkVersion:29)
-NetworkStackApiStableDependencies(minSdkVersion:29)
-NetworkStackApiStableLib(minSdkVersion:29)
-NetworkStackApiStableShims(minSdkVersion:29)
-networkstackprotos(minSdkVersion:29)
-NetworkStackShimsCommon(minSdkVersion:29)
-neuralnetworks_types(minSdkVersion:30)
-neuralnetworks_utils_hal_1_0(minSdkVersion:30)
-neuralnetworks_utils_hal_1_1(minSdkVersion:30)
-neuralnetworks_utils_hal_1_2(minSdkVersion:30)
-neuralnetworks_utils_hal_1_3(minSdkVersion:30)
-neuralnetworks_utils_hal_aidl(minSdkVersion:30)
-neuralnetworks_utils_hal_common(minSdkVersion:30)
-neuralnetworks_utils_hal_service(minSdkVersion:30)
-no_op(minSdkVersion:current)
-note_memtag_heap_async(minSdkVersion:16)
-note_memtag_heap_sync(minSdkVersion:16)
-PermissionController(minSdkVersion:30)
-permissioncontroller-statsd(minSdkVersion:current)
-philox_random(minSdkVersion:(no version))
-philox_random_headers(minSdkVersion:(no version))
-prebuilt_androidx-constraintlayout_constraintlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx-constraintlayout_constraintlayout-solver-nodeps(minSdkVersion:current)
-prebuilt_androidx.activity_activity-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.activity_activity-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.annotation_annotation-nodeps(minSdkVersion:current)
-prebuilt_androidx.appcompat_appcompat-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.appcompat_appcompat-resources-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.arch.core_core-common-nodeps(minSdkVersion:current)
-prebuilt_androidx.arch.core_core-runtime-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.asynclayoutinflater_asynclayoutinflater-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.autofill_autofill-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.cardview_cardview-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.collection_collection-ktx-nodeps(minSdkVersion:current)
-prebuilt_androidx.collection_collection-nodeps(minSdkVersion:current)
-prebuilt_androidx.coordinatorlayout_coordinatorlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.core_core-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.core_core-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.cursoradapter_cursoradapter-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.customview_customview-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.documentfile_documentfile-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.drawerlayout_drawerlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.dynamicanimation_dynamicanimation-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.fragment_fragment-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.fragment_fragment-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.interpolator_interpolator-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.leanback_leanback-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.leanback_leanback-preference-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.legacy_legacy-support-core-ui-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.legacy_legacy-support-core-utils-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.legacy_legacy-support-v13-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-common-java8-nodeps(minSdkVersion:current)
-prebuilt_androidx.lifecycle_lifecycle-common-nodeps(minSdkVersion:current)
-prebuilt_androidx.lifecycle_lifecycle-extensions-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-livedata-core-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-livedata-core-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-livedata-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-process-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-runtime-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-runtime-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-service-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-viewmodel-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-viewmodel-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-viewmodel-savedstate-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.loader_loader-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.localbroadcastmanager_localbroadcastmanager-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.media_media-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-common-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-common-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-fragment-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-fragment-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-runtime-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-runtime-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-ui-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-ui-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.preference_preference-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.print_print-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.recyclerview_recyclerview-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.recyclerview_recyclerview-selection-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.savedstate_savedstate-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.slidingpanelayout_slidingpanelayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.swiperefreshlayout_swiperefreshlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.transition_transition-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.vectordrawable_vectordrawable-animated-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.vectordrawable_vectordrawable-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.versionedparcelable_versionedparcelable-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.viewpager_viewpager-nodeps(minSdkVersion:(no version))
-prebuilt_com.google.android.material_material-nodeps(minSdkVersion:(no version))
-prebuilt_error_prone_annotations(minSdkVersion:(no version))
-prebuilt_kotlin-stdlib(minSdkVersion:current)
-prebuilt_kotlinx-coroutines-android-nodeps(minSdkVersion:(no version))
-prebuilt_kotlinx-coroutines-core-nodeps(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-aarch64-android(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-arm-android(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-i686-android(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-x86_64-android(minSdkVersion:(no version))
-prebuilt_libunwind(minSdkVersion:(no version))
-prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
-server_configurable_flags(minSdkVersion:29)
-service-media-s(minSdkVersion:29)
-service-permission(minSdkVersion:30)
-service-permission(minSdkVersion:current)
-service-permission-shared(minSdkVersion:30)
-service-statsd(minSdkVersion:30)
-service-statsd(minSdkVersion:current)
-SettingsLibActionBarShadow(minSdkVersion:21)
-SettingsLibAppPreference(minSdkVersion:21)
-SettingsLibBarChartPreference(minSdkVersion:21)
-SettingsLibHelpUtils(minSdkVersion:21)
-SettingsLibLayoutPreference(minSdkVersion:21)
-SettingsLibProgressBar(minSdkVersion:21)
-SettingsLibRestrictedLockUtils(minSdkVersion:21)
-SettingsLibSearchWidget(minSdkVersion:21)
-SettingsLibSettingsTheme(minSdkVersion:21)
-SettingsLibUtils(minSdkVersion:21)
-stats_proto(minSdkVersion:29)
-statsd(minSdkVersion:(no version))
-statsd(minSdkVersion:30)
-statsd-aidl-ndk_platform(minSdkVersion:(no version))
-statsd-aidl-ndk_platform(minSdkVersion:30)
-statsprotos(minSdkVersion:29)
-tensorflow_headers(minSdkVersion:(no version))
-Tethering(minSdkVersion:30)
-Tethering(minSdkVersion:current)
-TetheringApiCurrentLib(minSdkVersion:30)
-TetheringApiCurrentLib(minSdkVersion:current)
-TetheringGoogle(minSdkVersion:30)
-TetheringGoogle(minSdkVersion:current)
-textclassifier-statsd(minSdkVersion:current)
-TextClassifierNotificationLibNoManifest(minSdkVersion:29)
-TextClassifierServiceLibNoManifest(minSdkVersion:28)
-updatable-media(minSdkVersion:29)
-xz-java(minSdkVersion:current)
diff --git a/apex/apex.go b/apex/apex.go
index a12f3d2..a67fe1f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -854,12 +854,7 @@
 		if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() {
 			return true
 		}
-		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
-			// The tag defines a dependency that never requires the child module to be part of the same
-			// apex as the parent so it does not need an apex variant created.
-			return false
-		}
-		if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
+		if !android.IsDepInSameApex(mctx, parent, child) {
 			return false
 		}
 		if excludeVndkLibs {
@@ -1003,11 +998,7 @@
 	// If any of the dep is not available to platform, this module is also considered as being
 	// not available to platform even if it has "//apex_available:platform"
 	mctx.VisitDirectDeps(func(child android.Module) {
-		depTag := mctx.OtherModuleDependencyTag(child)
-		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
-			return
-		}
-		if !am.DepIsInSameApex(mctx, child) {
+		if !android.IsDepInSameApex(mctx, am, child) {
 			// if the dependency crosses apex boundary, don't consider it
 			return
 		}
@@ -1872,7 +1863,10 @@
 						// like to record requiredNativeLibs even when
 						// DepIsInSameAPex is false. We also shouldn't do
 						// this for host.
-						if !am.DepIsInSameApex(ctx, am) {
+						//
+						// TODO(jiyong): explain why the same module is passed in twice.
+						// Switching the first am to parent breaks lots of tests.
+						if !android.IsDepInSameApex(ctx, am, am) {
 							return false
 						}
 
@@ -2195,6 +2189,8 @@
 
 			// If `to` is not actually in the same APEX as `from` then it does not need
 			// apex_available and neither do any of its dependencies.
+			//
+			// It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
 			if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
 				// As soon as the dependency graph crosses the APEX boundary, don't go further.
 				return false
@@ -2278,6 +2274,8 @@
 
 		// If `to` is not actually in the same APEX as `from` then it does not need
 		// apex_available and neither do any of its dependencies.
+		//
+		// It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
 		if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
 			// As soon as the dependency graph crosses the APEX boundary, don't go
 			// further.
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index ee9fc81..0ed94af 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -58,8 +58,8 @@
 				echo "ERROR: go/apex-allowed-deps-error";
 				echo "******************************";
 				echo "Detected changes to allowed dependencies in updatable modules.";
-				echo "To fix and update build/soong/apex/allowed_deps.txt, please run:";
-				echo "$$ (croot && build/soong/scripts/update-apex-allowed-deps.sh)";
+				echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
+				echo "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)";
 				echo "Members of mainline-modularization@google.com will review the changes.";
 				echo -e "******************************\n";
 				exit 1;
@@ -81,25 +81,35 @@
 		}
 	})
 
-	allowedDeps := android.ExistentPathForSource(ctx, "build/soong/apex/allowed_deps.txt").Path()
-
+	allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")
 	newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   generateApexDepsInfoFilesRule,
-		Inputs: append(updatableFlatLists, allowedDeps),
-		Output: newAllowedDeps,
-	})
-
 	s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   diffAllowedApexDepsInfoRule,
-		Input:  newAllowedDeps,
-		Output: s.allowedApexDepsInfoCheckResult,
-		Args: map[string]string{
-			"allowed_deps":     allowedDeps.String(),
-			"new_allowed_deps": newAllowedDeps.String(),
-		},
-	})
+
+	if !allowedDepsSource.Valid() {
+		// Unbundled projects may not have packages/modules/common/ checked out; ignore those.
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Touch,
+			Output: s.allowedApexDepsInfoCheckResult,
+		})
+	} else {
+		allowedDeps := allowedDepsSource.Path()
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   generateApexDepsInfoFilesRule,
+			Inputs: append(updatableFlatLists, allowedDeps),
+			Output: newAllowedDeps,
+		})
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   diffAllowedApexDepsInfoRule,
+			Input:  newAllowedDeps,
+			Output: s.allowedApexDepsInfoCheckResult,
+			Args: map[string]string{
+				"allowed_deps":     allowedDeps.String(),
+				"new_allowed_deps": newAllowedDeps.String(),
+			},
+		})
+	}
 
 	ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 7ef1eaa..b159660 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -117,8 +117,6 @@
 	},
 )
 
-var emptyFixtureFactory = android.NewFixtureFactory(&buildDir)
-
 var apexFixtureFactory = android.NewFixtureFactory(
 	&buildDir,
 	// General preparers in alphabetical order as test infrastructure will enforce correct
@@ -1188,7 +1186,7 @@
 )
 
 func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
-	result := emptyFixtureFactory.Extend(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, `
 		cc_library {
 			name: "libc",
 			no_libcrt: true,
@@ -1234,7 +1232,7 @@
 }
 
 func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
-	result := emptyFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
 		prepareForTestOfRuntimeApexWithHwasan,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.SanitizeDevice = []string{"hwaddress"}
diff --git a/bootstrap_test.sh b/bootstrap_test.sh
index 87f5e31..6c5338a 100755
--- a/bootstrap_test.sh
+++ b/bootstrap_test.sh
@@ -235,6 +235,86 @@
   grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
 }
 
+function test_soong_build_rerun_iff_environment_changes() {
+  setup
+
+  mkdir -p cherry
+  cat > cherry/Android.bp <<'EOF'
+bootstrap_go_package {
+  name: "cherry",
+  pkgPath: "android/soong/cherry",
+  deps: [
+    "blueprint",
+    "soong",
+    "soong-android",
+  ],
+  srcs: [
+    "cherry.go",
+  ],
+  pluginFor: ["soong_build"],
+}
+EOF
+
+  cat > cherry/cherry.go <<'EOF'
+package cherry
+
+import (
+  "android/soong/android"
+  "github.com/google/blueprint"
+)
+
+var (
+  pctx = android.NewPackageContext("cherry")
+)
+
+func init() {
+  android.RegisterSingletonType("cherry", CherrySingleton)
+}
+
+func CherrySingleton() android.Singleton {
+  return &cherrySingleton{}
+}
+
+type cherrySingleton struct{}
+
+func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+  cherryRule := ctx.Rule(pctx, "cherry",
+    blueprint.RuleParams{
+      Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}",
+      CommandDeps: []string{},
+      Description: "Cherry",
+    })
+
+  outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt")
+  var deps android.Paths
+
+  ctx.Build(pctx, android.BuildParams{
+    Rule: cherryRule,
+    Output: outputFile,
+    Inputs: deps,
+  })
+}
+EOF
+
+  export CHERRY=TASTY
+  run_soong
+  grep -q "CHERRY IS TASTY" out/soong/build.ninja \
+    || fail "first value of environment variable is not used"
+
+  export CHERRY=RED
+  run_soong
+  grep -q "CHERRY IS RED" out/soong/build.ninja \
+    || fail "second value of environment variable not used"
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$mtime1" != "$mtime2" ]]; then
+    fail "Output Ninja file changed when environment variable did not"
+  fi
+
+}
+
 function test_add_file_to_soong_build() {
   setup
   run_soong
@@ -308,12 +388,28 @@
   grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
 }
 
+function test_null_build_after_docs {
+  setup
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$mtime1" != "$mtime2" ]]; then
+    fail "Output Ninja file changed on null build"
+  fi
+}
+
 test_bazel_smoke
 test_smoke
 test_null_build
+test_null_build_after_docs
 test_soong_build_rebuilt_if_blueprint_changes
 test_add_file_to_glob
 test_add_android_bp
 test_change_android_bp
 test_delete_android_bp
 test_add_file_to_soong_build
+test_soong_build_rerun_iff_environment_changes
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1d4e322..1c058ba 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -70,6 +70,7 @@
     ],
     local_include_dirs = [
         "include",
+        ".",
     ],
     srcs = [
         "a/b/bar.h",
@@ -120,6 +121,7 @@
     ],
     local_include_dirs = [
         "include",
+        ".",
     ],
     srcs = [
         "a/b/c.c",
@@ -156,6 +158,9 @@
     copts = [
         "-fno-addrsig",
     ],
+    local_include_dirs = [
+        ".",
+    ],
     srcs = [
         "x/y/z.c",
     ],
@@ -167,6 +172,37 @@
     deps = [
         ":bar",
     ],
+    local_include_dirs = [
+        ".",
+    ],
+    srcs = [
+        "a/b/c.c",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "cc_object with include_build_dir: false",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			filesystem: map[string]string{
+				"a/b/c.c": "",
+				"x/y/z.c": "",
+			},
+			blueprint: `cc_object {
+    name: "foo",
+    srcs: ["a/b/c.c"],
+    include_build_directory: false,
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "foo",
+    copts = [
+        "-fno-addrsig",
+    ],
     srcs = [
         "a/b/c.c",
     ],
@@ -262,6 +298,9 @@
         "//conditions:default": [
         ],
     }),
+    local_include_dirs = [
+        ".",
+    ],
 )`,
 			},
 		},
@@ -310,6 +349,9 @@
         "//conditions:default": [
         ],
     }),
+    local_include_dirs = [
+        ".",
+    ],
 )`,
 			},
 		},
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index 0bf15db..51fbc15 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -26,8 +26,7 @@
 	os.Exit(m.Run())
 }
 
-var bpfFactory = android.NewFixtureFactory(
-	nil,
+var prepareForBpfTest = android.GroupFixturePreparers(
 	cc.PrepareForTestWithCcDefaultModules,
 	android.FixtureMergeMockFs(
 		map[string][]byte{
@@ -53,7 +52,7 @@
 		}
 	`
 
-	bpfFactory.RunTestWithBp(t, bp)
+	prepareForBpfTest.RunTestWithBp(t, bp)
 
 	// We only verify the above BP configuration is processed successfully since the data property
 	// value is not available for testing from this package.
diff --git a/cc/compiler.go b/cc/compiler.go
index bcad1ad..b09b58e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -256,6 +256,10 @@
 	return []interface{}{&compiler.Properties, &compiler.Proto}
 }
 
+func (compiler *baseCompiler) includeBuildDirectory() bool {
+	return proptools.BoolDefault(compiler.Properties.Include_build_directory, true)
+}
+
 func (compiler *baseCompiler) compilerInit(ctx BaseModuleContext) {}
 
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
@@ -332,8 +336,7 @@
 		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 
-	if compiler.Properties.Include_build_directory == nil ||
-		*compiler.Properties.Include_build_directory {
+	if compiler.includeBuildDirectory() {
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+modulePath)
 		flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+modulePath)
 	}
diff --git a/cc/config/global.go b/cc/config/global.go
index cb7d17d..7e80900 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -144,8 +144,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r412851"
-	ClangDefaultShortVersion = "12.0.3"
+	ClangDefaultVersion      = "clang-r416183"
+	ClangDefaultShortVersion = "12.0.4"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/object.go b/cc/object.go
index f9e6d2d..6bea28b 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -160,6 +160,10 @@
 		}
 	}
 
+	if c, ok := m.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
+		localIncludeDirs = append(localIncludeDirs, ".")
+	}
+
 	var deps bazel.LabelList
 	for _, props := range m.linker.linkerProps() {
 		if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 11d3620..94efa4d 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -17,6 +17,7 @@
 import (
 	"flag"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
@@ -95,11 +96,15 @@
 	android.InitSandbox(topDir)
 	android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
 
+	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}
+	extraNinjaDeps := []string{
+		configuration.ProductVariablesFileName,
+		shared.JoinPath(outDir, "soong.environment.used"),
+	}
 
 	if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
 		configuration.SetAllowMissingDependencies()
@@ -115,15 +120,12 @@
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
 	}
 
-	if bazelConversionRequested(configuration) {
+	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)
-		// Short-circuit and return.
-		return
-	}
-
-	if configuration.BazelContext.BazelEnabled() {
+		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.
@@ -151,7 +153,7 @@
 	}
 
 	// Convert the Soong module graph into Bazel BUILD files.
-	if bazelQueryViewDir != "" {
+	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)
@@ -161,7 +163,7 @@
 		}
 	}
 
-	if docFile != "" {
+	if !bazelConversionRequested && docFile != "" {
 		if err := writeDocs(ctx, configuration, docFile); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
@@ -170,7 +172,7 @@
 
 	// TODO(ccross): make this a command line argument.  Requires plumbing through blueprint
 	//  to affect the command line of the primary builder.
-	if shouldPrepareBuildActions(configuration) {
+	if !bazelConversionRequested && shouldPrepareBuildActions(configuration) {
 		metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb")
 		err := android.WriteMetrics(configuration, metricsFile)
 		if err != nil {
@@ -178,12 +180,32 @@
 			os.Exit(1)
 		}
 	}
+
+	if docFile == "" {
+		// Let's not overwrite the used variables file when generating
+		// documentation
+		writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
+	}
+}
+
+func writeUsedVariablesFile(path string, configuration android.Config) {
+	data, err := shared.EnvFileContents(configuration.EnvDeps())
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+		os.Exit(1)
+	}
+
+	err = ioutil.WriteFile(path, data, 0666)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+		os.Exit(1)
+	}
 }
 
 // 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) {
+func runBp2Build(srcDir string, configuration android.Config, extraNinjaDeps []string) {
 	// Register an alternate set of singletons and mutators for bazel
 	// conversion for Bazel conversion.
 	bp2buildCtx := android.NewContext(configuration)
@@ -198,11 +220,13 @@
 	// configurations or variables, since those will generate different BUILD
 	// files based on how the user has configured their tree.
 	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile())
-	extraNinjaDeps, err := bp2buildCtx.ListModulePaths(srcDir)
+	modulePaths, err := bp2buildCtx.ListModulePaths(srcDir)
 	if err != nil {
 		panic(err)
 	}
 
+	extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+
 	// Run the loading and analysis pipeline to prepare the graph of regular
 	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 	// from the regular Modules.
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index f800c48..9c3db3b 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -26,8 +26,7 @@
 	os.Exit(m.Run())
 }
 
-var prebuiltEtcFixtureFactory = android.NewFixtureFactory(
-	nil,
+var prepareForPrebuiltEtcTest = android.GroupFixturePreparers(
 	android.PrepareForTestWithArchMutator,
 	PrepareForTestWithPrebuiltEtc,
 	android.FixtureMergeMockFs(android.MockFS{
@@ -38,7 +37,7 @@
 )
 
 func TestPrebuiltEtcVariants(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -72,7 +71,7 @@
 }
 
 func TestPrebuiltEtcOutputPath(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -85,7 +84,7 @@
 }
 
 func TestPrebuiltEtcGlob(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "my_foo",
 			src: "foo.*",
@@ -105,7 +104,7 @@
 }
 
 func TestPrebuiltEtcAndroidMk(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo",
 			src: "foo.conf",
@@ -139,7 +138,7 @@
 }
 
 func TestPrebuiltEtcRelativeInstallPathInstallDirPath(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -153,7 +152,7 @@
 }
 
 func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
-	prebuiltEtcFixtureFactory.
+	prepareForPrebuiltEtcTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("relative_install_path is set. Cannot set sub_dir")).
 		RunTestWithBp(t, `
 			prebuilt_etc {
@@ -166,7 +165,7 @@
 }
 
 func TestPrebuiltEtcHost(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc_host {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -181,7 +180,7 @@
 }
 
 func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_usr_share {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -195,7 +194,7 @@
 }
 
 func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_usr_share_host {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -210,7 +209,7 @@
 }
 
 func TestPrebuiltFontInstallDirPath(t *testing.T) {
-	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_font {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -249,7 +248,7 @@
 	}}
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
-			result := prebuiltEtcFixtureFactory.RunTestWithBp(t, tt.config)
+			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
 		})
@@ -283,7 +282,7 @@
 	}}
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
-			result := prebuiltEtcFixtureFactory.RunTestWithBp(t, tt.config)
+			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
 		})
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 8974eba..b2bd6bd 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -91,7 +91,7 @@
 
 var dependencyTag = struct {
 	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag
+	android.PackagingItemAlwaysDepTag
 }{}
 
 func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 199a7df..2ee456d 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -28,8 +28,7 @@
 	os.Exit(m.Run())
 }
 
-var genruleFixtureFactory = android.NewFixtureFactory(
-	nil,
+var prepareForGenRuleTest = android.GroupFixturePreparers(
 	android.PrepareForTestWithArchMutator,
 	android.PrepareForTestWithDefaults,
 
@@ -37,6 +36,7 @@
 	PrepareForTestWithGenRuleBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterModuleType("tool", toolFactory)
+		ctx.RegisterModuleType("output", outputProducerFactory)
 	}),
 	android.FixtureMergeMockFs(android.MockFS{
 		"tool":       nil,
@@ -447,7 +447,8 @@
 				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
 			}
 
-			result := genruleFixtureFactory.Extend(
+			result := android.GroupFixturePreparers(
+				prepareForGenRuleTest,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
 				}),
@@ -523,7 +524,7 @@
 		},
 	}
 
-	result := genruleFixtureFactory.RunTestWithBp(t, testGenruleBp()+bp)
+	result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
@@ -605,7 +606,7 @@
 				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
 			}
 
-			result := genruleFixtureFactory.
+			result := prepareForGenRuleTest.
 				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
 				RunTestWithBp(t, testGenruleBp()+bp)
 
@@ -642,7 +643,7 @@
 				}
 			`
 
-	result := genruleFixtureFactory.RunTestWithBp(t, testGenruleBp()+bp)
+	result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
 
 	gen := result.Module("gen", "").(*Module)
 
@@ -653,6 +654,35 @@
 	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
 }
 
+func TestGenruleAllowMissingDependencies(t *testing.T) {
+	bp := `
+		output {
+			name: "disabled",
+			enabled: false,
+		}
+
+		genrule {
+			name: "gen",
+			srcs: [
+				":disabled",
+			],
+			out: ["out"],
+			cmd: "cat $(in) > $(out)",
+		}
+       `
+	result := prepareForGenRuleTest.Extend(
+		android.FixtureModifyConfigAndContext(
+			func(config android.Config, ctx *android.TestContext) {
+				config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+				ctx.SetAllowMissingDependencies(true)
+			})).RunTestWithBp(t, bp)
+
+	gen := result.ModuleForTests("gen", "").Output("out")
+	if gen.Rule != android.ErrorRule {
+		t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
+	}
+}
+
 func TestGenruleWithBazel(t *testing.T) {
 	bp := `
 		genrule {
@@ -662,11 +692,12 @@
 		}
 	`
 
-	result := genruleFixtureFactory.Extend(android.FixtureModifyConfig(func(config android.Config) {
-		config.BazelContext = android.MockBazelContext{
-			AllFiles: map[string][]string{
-				"//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
-	})).RunTestWithBp(t, testGenruleBp()+bp)
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				AllFiles: map[string][]string{
+					"//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
+		})).RunTestWithBp(t, testGenruleBp()+bp)
 
 	gen := result.Module("foo", "").(*Module)
 
@@ -696,3 +727,24 @@
 }
 
 var _ android.HostToolProvider = (*testTool)(nil)
+
+type testOutputProducer struct {
+	android.ModuleBase
+	outputFile android.Path
+}
+
+func outputProducerFactory() android.Module {
+	module := &testOutputProducer{}
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{t.outputFile}, nil
+}
+
+var _ android.OutputFileProducer = (*testOutputProducer)(nil)
diff --git a/java/aar.go b/java/aar.go
index 554ea67..67b9ef0 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -40,15 +40,14 @@
 
 func init() {
 	RegisterAARBuildComponents(android.InitRegistrationContext)
-
-	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
-	})
 }
 
 func RegisterAARBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_library_import", AARImportFactory)
 	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
+	})
 }
 
 //
diff --git a/java/android_resources.go b/java/android_resources.go
index 4d420cf..6864ebb 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -22,8 +22,11 @@
 )
 
 func init() {
-	android.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+	registerOverlayBuildComponents(android.InitRegistrationContext)
+}
 
+func registerOverlayBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
 }
 
 var androidResourceIgnoreFilenames = []string{
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 3477956..5eaa77b 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -135,7 +135,8 @@
 }
 
 func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo-shared_library", "foo-no_shared_library"),
 	).RunTestWithBp(t, `
@@ -171,7 +172,7 @@
 }
 
 func TestImportSoongDexJar(t *testing.T) {
-	ctx, _ := testJava(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_import {
 			name: "my-java-import",
 			jars: ["a.jar"],
@@ -180,14 +181,10 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
-	expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+	mod := result.Module("my-java-import", "android_common")
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
+	expectedSoongDexJar := "out/soong/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
 	actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
 
-	if len(actualSoongDexJar) != 1 {
-		t.Errorf("LOCAL_SOONG_DEX_JAR incorrect len %d", len(actualSoongDexJar))
-	} else if actualSoongDexJar[0] != expectedSoongDexJar {
-		t.Errorf("LOCAL_SOONG_DEX_JAR mismatch, actual: %s, expected: %s", actualSoongDexJar[0], expectedSoongDexJar)
-	}
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_SOONG_DEX_JAR", result.Config, []string{expectedSoongDexJar}, actualSoongDexJar)
 }
diff --git a/java/app_import_test.go b/java/app_import_test.go
index cae41d0..147ae45 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -252,14 +252,15 @@
 
 	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
 	for _, test := range testCases {
-		config := testAppConfig(nil, bp, nil)
-		config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
-		config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
-		ctx := testContext(config)
+		result := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.AAPTPreferredConfig = test.aaptPreferredConfig
+				variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+			}),
+		).RunTestWithBp(t, bp)
 
-		run(t, ctx, config)
-
-		variant := ctx.ModuleForTests("foo", "android_common")
+		variant := result.ModuleForTests("foo", "android_common")
 		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
 		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
 		if len(matches) != 2 {
@@ -456,12 +457,9 @@
 		t.Errorf("prebuilt framework-res is not preprocessed")
 	}
 
-	expectedInstallPath := buildDir + "/target/product/test_device/system/framework/framework-res.apk"
+	expectedInstallPath := "out/soong/target/product/test_device/system/framework/framework-res.apk"
 
-	if a.dexpreopter.installPath.String() != expectedInstallPath {
-		t.Errorf("prebuilt framework-res installed to incorrect location, actual: %s, expected: %s", a.dexpreopter.installPath, expectedInstallPath)
-
-	}
+	android.AssertPathRelativeToTopEquals(t, "prebuilt framework-res install location", expectedInstallPath, a.dexpreopter.installPath)
 
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
diff --git a/java/app_set_test.go b/java/app_set_test.go
index ab55758..adaf71b 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -96,20 +97,24 @@
 	}
 
 	for _, test := range testCases {
-		config := testAppConfig(nil, bp, nil)
-		config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
-		config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
-		config.Targets[android.Android] = test.targets
-		ctx := testContext(config)
-		run(t, ctx, config)
+		ctx := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+				variables.Platform_sdk_version = &test.sdkVersion
+			}),
+			android.FixtureModifyConfig(func(config android.Config) {
+				config.Targets[android.Android] = test.targets
+			}),
+		).RunTestWithBp(t, bp)
+
 		module := ctx.ModuleForTests("foo", "android_common")
 		const packedSplitApks = "foo.zip"
 		params := module.Output(packedSplitApks)
 		for k, v := range test.expected {
-			if actual := params.Args[k]; actual != v {
-				t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
-					test.name, k, actual, v)
-			}
+			t.Run(test.name, func(t *testing.T) {
+				android.AssertStringEquals(t, fmt.Sprintf("arg value for `%s`", k), v, params.Args[k])
+			})
 		}
 	}
 }
diff --git a/java/app_test.go b/java/app_test.go
index 7168a96..2523533 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -26,25 +26,17 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/genrule"
 )
 
-// testAppConfig is a legacy way of creating a test Config for testing java app modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testAppConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
-	return testConfig(env, bp, fs)
-}
-
-// testApp runs tests using the javaFixtureFactory
+// testApp runs tests using the prepareForJavaTest
 //
 // See testJava for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testApp(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	result := javaFixtureFactory.RunTestWithBp(t, bp)
+	result := prepareForJavaTest.RunTestWithBp(t, bp)
 	return result.TestContext
 }
 
@@ -63,7 +55,8 @@
 
 	for _, moduleType := range []string{"android_app", "android_library"} {
 		t.Run(moduleType, func(t *testing.T) {
-			result := javaFixtureFactory.Extend(
+			result := android.GroupFixturePreparers(
+				prepareForJavaTest,
 				android.FixtureModifyMockFS(func(fs android.MockFS) {
 					for _, file := range resourceFiles {
 						fs[file] = nil
@@ -118,9 +111,9 @@
 	foo := ctx.ModuleForTests("foo", "android_common")
 
 	expectedOutputs := []string{
-		filepath.Join(buildDir, ".intermediates/foo/android_common/foo.apk"),
-		filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v4.apk"),
-		filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v7_hdpi.apk"),
+		"out/soong/.intermediates/foo/android_common/foo.apk",
+		"out/soong/.intermediates/foo/android_common/foo_v4.apk",
+		"out/soong/.intermediates/foo/android_common/foo_v7_hdpi.apk",
 	}
 	for _, expectedOutput := range expectedOutputs {
 		foo.Output(expectedOutput)
@@ -130,9 +123,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	if g, w := outputFiles.Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
-		t.Errorf(`want OutputFiles("") = %q, got %q`, w, g)
-	}
+	android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
 }
 
 func TestPlatformAPIs(t *testing.T) {
@@ -374,8 +365,8 @@
 			if test.expectedError != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
 			}
-			javaFixtureFactory.
-				Extend(FixtureWithPrebuiltApis(map[string][]string{
+			android.GroupFixturePreparers(
+				prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{
 					"29": {"foo"},
 				})).
 				ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, test.bp)
@@ -446,7 +437,7 @@
 		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o":     nil,
 	}
 
-	ctx, _ := testJavaWithConfig(t, testConfig(nil, bp, fs))
+	ctx, _ := testJavaWithFS(t, bp, fs)
 
 	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 	var crtbeginFound, crtendFound bool
@@ -547,7 +538,7 @@
 		},
 	}
 
-	fs := map[string][]byte{
+	fs := android.MockFS{
 		"res/res/values/strings.xml": nil,
 	}
 
@@ -561,11 +552,13 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			config := testConfig(nil, fmt.Sprintf(bp, testCase.prop), fs)
-			ctx := testContext(config)
-			run(t, ctx, config)
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithOverlayBuildComponents,
+				fs.AddToFixture(),
+			).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop))
 
-			module := ctx.ModuleForTests("foo", "android_common")
+			module := result.ModuleForTests("foo", "android_common")
 			resourceList := module.MaybeOutput("aapt2/res.list")
 
 			var resources []string
@@ -575,10 +568,7 @@
 				}
 			}
 
-			if !reflect.DeepEqual(resources, testCase.resources) {
-				t.Errorf("expected resource files %q, got %q",
-					testCase.resources, resources)
-			}
+			android.AssertDeepEquals(t, "resource files", testCase.resources, resources)
 		})
 	}
 }
@@ -624,9 +614,9 @@
 			name: "foo",
 			// lib1 has its own asset. lib3 doesn't have any, but provides lib4's transitively.
 			assetPackages: []string{
-				buildDir + "/.intermediates/foo/android_common/aapt2/package-res.apk",
-				buildDir + "/.intermediates/lib1/android_common/assets.zip",
-				buildDir + "/.intermediates/lib3/android_common/assets.zip",
+				"out/soong/.intermediates/foo/android_common/aapt2/package-res.apk",
+				"out/soong/.intermediates/lib1/android_common/assets.zip",
+				"out/soong/.intermediates/lib3/android_common/assets.zip",
 			},
 		},
 		{
@@ -639,8 +629,8 @@
 		{
 			name: "lib3",
 			assetPackages: []string{
-				buildDir + "/.intermediates/lib3/android_common/aapt2/package-res.apk",
-				buildDir + "/.intermediates/lib4/android_common/assets.zip",
+				"out/soong/.intermediates/lib3/android_common/aapt2/package-res.apk",
+				"out/soong/.intermediates/lib4/android_common/assets.zip",
 			},
 		},
 		{
@@ -661,24 +651,18 @@
 			} else {
 				aapt2link = m.Output("package-res.apk")
 			}
+			aapt2link = aapt2link.RelativeToTop()
 			aapt2Flags := aapt2link.Args["flags"]
 			if test.assetFlag != "" {
-				if !strings.Contains(aapt2Flags, test.assetFlag) {
-					t.Errorf("Can't find asset flag %q in aapt2 link flags %q", test.assetFlag, aapt2Flags)
-				}
+				android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag)
 			} else {
-				if strings.Contains(aapt2Flags, " -A ") {
-					t.Errorf("aapt2 link flags %q contain unexpected asset flag", aapt2Flags)
-				}
+				android.AssertStringDoesNotContain(t, "aapt2 link flags", aapt2Flags, " -A ")
 			}
 
 			// Check asset merge rule.
 			if len(test.assetPackages) > 0 {
 				mergeAssets := m.Output("package-res.apk")
-				if !reflect.DeepEqual(test.assetPackages, mergeAssets.Inputs.Strings()) {
-					t.Errorf("Unexpected mergeAssets inputs: %v, expected: %v",
-						mergeAssets.Inputs.Strings(), test.assetPackages)
-				}
+				android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs)
 			}
 		})
 	}
@@ -750,9 +734,9 @@
 			},
 			overlayFiles: map[string][]string{
 				"foo": {
-					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
-					buildDir + "/.intermediates/lib/android_common/package-res.apk",
-					buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+					"out/soong/.intermediates/lib2/android_common/package-res.apk",
+					"out/soong/.intermediates/lib/android_common/package-res.apk",
+					"out/soong/.intermediates/lib3/android_common/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 					"device/vendor/blah/overlay/foo/res/values/strings.xml",
@@ -763,7 +747,7 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				"lib": {
-					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
@@ -785,9 +769,9 @@
 			},
 			overlayFiles: map[string][]string{
 				"foo": {
-					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
-					buildDir + "/.intermediates/lib/android_common/package-res.apk",
-					buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+					"out/soong/.intermediates/lib2/android_common/package-res.apk",
+					"out/soong/.intermediates/lib/android_common/package-res.apk",
+					"out/soong/.intermediates/lib3/android_common/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 				},
@@ -796,7 +780,7 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				"lib": {
-					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
@@ -827,15 +811,15 @@
 			},
 			overlayFiles: map[string][]string{
 				"foo": {
-					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
-					buildDir + "/.intermediates/lib/android_common/package-res.apk",
-					buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+					"out/soong/.intermediates/lib2/android_common/package-res.apk",
+					"out/soong/.intermediates/lib/android_common/package-res.apk",
+					"out/soong/.intermediates/lib3/android_common/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 				},
 				"bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
 				"lib": {
-					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
@@ -862,7 +846,7 @@
 		"product/vendor/blah/overlay",
 	}
 
-	fs := map[string][]byte{
+	fs := android.MockFS{
 		"foo/res/res/values/strings.xml":                               nil,
 		"bar/res/res/values/strings.xml":                               nil,
 		"lib/res/res/values/strings.xml":                               nil,
@@ -913,18 +897,21 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			config := testAppConfig(nil, bp, fs)
-			config.TestProductVariables.DeviceResourceOverlays = deviceResourceOverlays
-			config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
-			if testCase.enforceRROTargets != nil {
-				config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
-			}
-			if testCase.enforceRROExcludedOverlays != nil {
-				config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
-			}
-
-			ctx := testContext(config)
-			run(t, ctx, config)
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithOverlayBuildComponents,
+				fs.AddToFixture(),
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = deviceResourceOverlays
+					variables.ProductResourceOverlays = productResourceOverlays
+					if testCase.enforceRROTargets != nil {
+						variables.EnforceRROTargets = testCase.enforceRROTargets
+					}
+					if testCase.enforceRROExcludedOverlays != nil {
+						variables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
+					}
+				}),
+			).RunTestWithBp(t, bp)
 
 			resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
 				for _, o := range list {
@@ -942,14 +929,14 @@
 			}
 
 			getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
-				module := ctx.ModuleForTests(moduleName, "android_common")
+				module := result.ModuleForTests(moduleName, "android_common")
 				resourceList := module.MaybeOutput("aapt2/res.list")
 				if resourceList.Rule != nil {
-					resourceFiles = resourceListToFiles(module, resourceList.Inputs.Strings())
+					resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
 				}
 				overlayList := module.MaybeOutput("aapt2/overlay.list")
 				if overlayList.Rule != nil {
-					overlayFiles = resourceListToFiles(module, overlayList.Inputs.Strings())
+					overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs))
 				}
 
 				for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() {
@@ -961,7 +948,7 @@
 					} else {
 						t.Fatalf("Unexpected overlayType %d", d.overlayType)
 					}
-					rroDirs = append(rroDirs, prefix+d.path.String())
+					rroDirs = append(rroDirs, prefix+android.PathRelativeToTop(d.path))
 				}
 
 				return resourceFiles, overlayFiles, rroDirs
@@ -1077,7 +1064,8 @@
 					%s
 				}`, moduleType, test.sdkVersion, platformApiProp)
 
-				result := javaFixtureFactory.Extend(
+				result := android.GroupFixturePreparers(
+					prepareForJavaTest,
 					android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 						variables.Platform_sdk_version = &test.platformSdkInt
 						variables.Platform_sdk_codename = &test.platformSdkCodename
@@ -1145,7 +1133,8 @@
 						vendor: true,
 					}`, moduleType, sdkKind, test.sdkVersion)
 
-					result := javaFixtureFactory.Extend(
+					result := android.GroupFixturePreparers(
+						prepareForJavaTest,
 						android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 							variables.Platform_sdk_version = &test.platformSdkInt
 							variables.Platform_sdk_codename = &test.platformSdkCodename
@@ -1269,13 +1258,19 @@
 			}
 		`
 
-		config := testAppConfig(nil, bp, nil)
-		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+		errorHandler := android.FixtureExpectsNoErrors
 		if enforce {
-			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
-		} else {
-			testJavaWithConfig(t, config)
+			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
 		}
+
+		android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+			}),
+		).
+			ExtendWithErrorHandler(errorHandler).
+			RunTestWithBp(t, bp)
 	}
 }
 
@@ -1609,25 +1604,23 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			config := testAppConfig(nil, test.bp, nil)
-			if test.certificateOverride != "" {
-				config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
-			}
-			ctx := testContext(config)
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					if test.certificateOverride != "" {
+						variables.CertificateOverrides = []string{test.certificateOverride}
+					}
+				}),
+			).RunTestWithBp(t, test.bp)
 
-			run(t, ctx, config)
-			foo := ctx.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests("foo", "android_common")
 
 			signapk := foo.Output("foo.apk")
 			signCertificateFlags := signapk.Args["certificates"]
-			if test.expectedCertificate != signCertificateFlags {
-				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedCertificate, signCertificateFlags)
-			}
+			android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags)
 
 			signFlags := signapk.Args["flags"]
-			if test.expectedLineage != signFlags {
-				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedLineage, signFlags)
-			}
+			android.AssertStringEquals(t, "signing flags", test.expectedLineage, signFlags)
 		})
 	}
 }
@@ -1677,17 +1670,15 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			config := testAppConfig(nil, test.bp, nil)
-			ctx := testContext(config)
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+			).RunTestWithBp(t, test.bp)
 
-			run(t, ctx, config)
-			foo := ctx.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests("foo", "android_common")
 
 			signapk := foo.Output("foo.apk")
 			signFlags := signapk.Args["flags"]
-			if test.expected != signFlags {
-				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expected, signFlags)
-			}
+			android.AssertStringEquals(t, "signing flags", test.expected, signFlags)
 		})
 	}
 }
@@ -1710,8 +1701,8 @@
 			`,
 			packageNameOverride: "",
 			expected: []string{
-				buildDir + "/.intermediates/foo/android_common/foo.apk",
-				buildDir + "/target/product/test_device/system/app/foo/foo.apk",
+				"out/soong/.intermediates/foo/android_common/foo.apk",
+				"out/soong/target/product/test_device/system/app/foo/foo.apk",
 			},
 		},
 		{
@@ -1726,27 +1717,31 @@
 			packageNameOverride: "foo:bar",
 			expected: []string{
 				// The package apk should be still be the original name for test dependencies.
-				buildDir + "/.intermediates/foo/android_common/bar.apk",
-				buildDir + "/target/product/test_device/system/app/bar/bar.apk",
+				"out/soong/.intermediates/foo/android_common/bar.apk",
+				"out/soong/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			config := testAppConfig(nil, test.bp, nil)
-			if test.packageNameOverride != "" {
-				config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
-			}
-			ctx := testContext(config)
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					if test.packageNameOverride != "" {
+						variables.PackageNameOverrides = []string{test.packageNameOverride}
+					}
+				}),
+			).RunTestWithBp(t, test.bp)
 
-			run(t, ctx, config)
-			foo := ctx.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests("foo", "android_common")
+
+			outSoongDir := result.Config.BuildDir()
 
 			outputs := foo.AllOutputs()
 			outputMap := make(map[string]bool)
 			for _, o := range outputs {
-				outputMap[o] = true
+				outputMap[android.StringPathRelativeToTop(outSoongDir, o)] = true
 			}
 			for _, e := range test.expected {
 				if _, exist := outputMap[e]; !exist {
@@ -1771,13 +1766,15 @@
 			sdk_version: "current",
 		}
 		`
-	config := testAppConfig(nil, bp, nil)
-	config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
-	ctx := testContext(config)
 
-	run(t, ctx, config)
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
+		}),
+	).RunTestWithBp(t, bp)
 
-	bar := ctx.ModuleForTests("bar", "android_common")
+	bar := result.ModuleForTests("bar", "android_common")
 	res := bar.Output("package-res.apk")
 	aapt2Flags := res.Args["flags"]
 	e := "--rename-instrumentation-target-package org.dandroid.bp"
@@ -1787,7 +1784,8 @@
 }
 
 func TestOverrideAndroidApp(t *testing.T) {
-	ctx, _ := testJava(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+		t, `
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
@@ -1862,7 +1860,7 @@
 			name:            "foo",
 			moduleName:      "foo",
 			variantName:     "android_common",
-			apkPath:         "/target/product/test_device/system/app/foo/foo.apk",
+			apkPath:         "out/soong/target/product/test_device/system/app/foo/foo.apk",
 			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:     "",
 			overrides:       []string{"qux"},
@@ -1874,7 +1872,7 @@
 			name:            "foo",
 			moduleName:      "bar",
 			variantName:     "android_common_bar",
-			apkPath:         "/target/product/test_device/system/app/bar/bar.apk",
+			apkPath:         "out/soong/target/product/test_device/system/app/bar/bar.apk",
 			certFlag:        "cert/new_cert.x509.pem cert/new_cert.pk8",
 			lineageFlag:     "--lineage lineage.bin",
 			overrides:       []string{"qux", "foo"},
@@ -1886,7 +1884,7 @@
 			name:            "foo",
 			moduleName:      "baz",
 			variantName:     "android_common_baz",
-			apkPath:         "/target/product/test_device/system/app/baz/baz.apk",
+			apkPath:         "out/soong/target/product/test_device/system/app/baz/baz.apk",
 			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:     "",
 			overrides:       []string{"qux", "foo"},
@@ -1898,7 +1896,7 @@
 			name:            "foo",
 			moduleName:      "baz_no_rename_resources",
 			variantName:     "android_common_baz_no_rename_resources",
-			apkPath:         "/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
+			apkPath:         "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
 			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:     "",
 			overrides:       []string{"qux", "foo"},
@@ -1910,7 +1908,7 @@
 			name:            "foo_no_rename_resources",
 			moduleName:      "baz_base_no_rename_resources",
 			variantName:     "android_common_baz_base_no_rename_resources",
-			apkPath:         "/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
+			apkPath:         "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
 			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:     "",
 			overrides:       []string{"qux", "foo_no_rename_resources"},
@@ -1922,7 +1920,7 @@
 			name:            "foo_no_rename_resources",
 			moduleName:      "baz_override_base_rename_resources",
 			variantName:     "android_common_baz_override_base_rename_resources",
-			apkPath:         "/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
+			apkPath:         "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
 			certFlag:        "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			lineageFlag:     "",
 			overrides:       []string{"qux", "foo_no_rename_resources"},
@@ -1932,48 +1930,27 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests(expected.name, expected.variantName)
+		variant := result.ModuleForTests(expected.name, expected.variantName)
 
 		// Check the final apk name
-		outputs := variant.AllOutputs()
-		expectedApkPath := buildDir + expected.apkPath
-		found := false
-		for _, o := range outputs {
-			if o == expectedApkPath {
-				found = true
-				break
-			}
-		}
-		if !found {
-			t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
-		}
+		variant.Output(expected.apkPath)
 
 		// Check the certificate paths
 		signapk := variant.Output(expected.moduleName + ".apk")
 		certFlag := signapk.Args["certificates"]
-		if expected.certFlag != certFlag {
-			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.certFlag, certFlag)
-		}
+		android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag)
 
 		// Check the lineage flags
 		lineageFlag := signapk.Args["flags"]
-		if expected.lineageFlag != lineageFlag {
-			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.lineageFlag, lineageFlag)
-		}
+		android.AssertStringEquals(t, "signing flags", expected.lineageFlag, lineageFlag)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidApp)
-		if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
-			t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
-				expected.overrides, mod.appProperties.Overrides)
-		}
+		android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.appProperties.Overrides)
 
 		// Test Overridable property: Logging_parent
 		logging_parent := mod.aapt.LoggingParent
-		if expected.logging_parent != logging_parent {
-			t.Errorf("Incorrect overrides property value for logging parent, expected: %q, got: %q",
-				expected.logging_parent, logging_parent)
-		}
+		android.AssertStringEquals(t, "overrides property value for logging parent", expected.logging_parent, logging_parent)
 
 		// Check the package renaming flag, if exists.
 		res := variant.Output("package-res.apk")
@@ -2015,15 +1992,15 @@
 		`)
 
 	// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
-	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
-	fooTurbine := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac").RelativeToTop()
+	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], fooTurbine) {
 		t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
 	}
 
 	// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
-	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
-	barTurbine := filepath.Join(buildDir, ".intermediates", "foo", "android_common_bar", "turbine-combined", "foo.jar")
+	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac").RelativeToTop()
+	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
@@ -2089,18 +2066,7 @@
 		variant := ctx.ModuleForTests("foo_test", expected.variantName)
 
 		// Check the final apk name
-		outputs := variant.AllOutputs()
-		expectedApkPath := buildDir + expected.apkPath
-		found := false
-		for _, o := range outputs {
-			if o == expectedApkPath {
-				found = true
-				break
-			}
-		}
-		if !found {
-			t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
-		}
+		variant.Output("out/soong" + expected.apkPath)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidTest)
@@ -2110,8 +2076,8 @@
 		}
 
 		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
-		javac := variant.Rule("javac")
-		turbine := filepath.Join(buildDir, ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+		javac := variant.Rule("javac").RelativeToTop()
+		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
 		if !strings.Contains(javac.Args["classpath"], turbine) {
 			t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
 		}
@@ -2167,7 +2133,7 @@
 			moduleName:  "bar_test",
 			variantName: "android_common",
 			expectedFlags: []string{
-				"--manifest " + buildDir + "/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
+				"--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
 				"--package-name com.android.bar.test",
 			},
 		},
@@ -2175,8 +2141,7 @@
 			moduleName:  "foo_test",
 			variantName: "android_common_baz_test",
 			expectedFlags: []string{
-				"--manifest " + buildDir +
-					"/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
+				"--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
 				"--package-name com.android.baz.test",
 				"--test-file-name baz_test.apk",
 			},
@@ -2185,7 +2150,7 @@
 
 	for _, test := range testCases {
 		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
-		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
+		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml").RelativeToTop()
 
 		if len(test.expectedFlags) > 0 {
 			if params.Rule == nil {
@@ -2369,7 +2334,8 @@
 		}
 	`
 
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2533,7 +2499,17 @@
 }
 
 func TestEmbedNotice(t *testing.T) {
-	ctx, _ := testJavaWithFS(t, cc.GatherRequiredDepsForTest(android.Android)+`
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		cc.PrepareForTestWithCcDefaultModules,
+		genrule.PrepareForTestWithGenRuleBuildComponents,
+		android.MockFS{
+			"APP_NOTICE":     nil,
+			"GENRULE_NOTICE": nil,
+			"LIB_NOTICE":     nil,
+			"TOOL_NOTICE":    nil,
+		}.AddToFixture(),
+	).RunTestWithBp(t, `
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
@@ -2589,15 +2565,10 @@
 			srcs: ["b.java"],
 			notice: "TOOL_NOTICE",
 		}
-	`, map[string][]byte{
-		"APP_NOTICE":     nil,
-		"GENRULE_NOTICE": nil,
-		"LIB_NOTICE":     nil,
-		"TOOL_NOTICE":    nil,
-	})
+	`)
 
 	// foo has NOTICE files to process, and embed_notices is true.
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests("foo", "android_common")
 	// verify merge notices rule.
 	mergeNotices := foo.Rule("mergeNoticesRule")
 	noticeInputs := mergeNotices.Inputs.Strings()
@@ -2615,27 +2586,23 @@
 		t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
 	}
 	// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
-	res := foo.Output("package-res.apk")
+	res := foo.Output("package-res.apk").RelativeToTop()
 	aapt2Flags := res.Args["flags"]
-	e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
-	if !strings.Contains(aapt2Flags, e) {
-		t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
-	}
+	e := "-A out/soong/.intermediates/foo/android_common/NOTICE"
+	android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e)
 
 	// bar has NOTICE files to process, but embed_notices is not set.
-	bar := ctx.ModuleForTests("bar", "android_common")
-	res = bar.Output("package-res.apk")
+	bar := result.ModuleForTests("bar", "android_common")
+	res = bar.Output("package-res.apk").RelativeToTop()
 	aapt2Flags = res.Args["flags"]
-	e = "-A " + buildDir + "/.intermediates/bar/android_common/NOTICE"
-	if strings.Contains(aapt2Flags, e) {
-		t.Errorf("bar shouldn't have the asset dir flag for NOTICE: %q", e)
-	}
+	e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
+	android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
 
 	// baz's embed_notice is true, but it doesn't have any NOTICE files.
-	baz := ctx.ModuleForTests("baz", "android_common")
+	baz := result.ModuleForTests("baz", "android_common")
 	res = baz.Output("package-res.apk")
 	aapt2Flags = res.Args["flags"]
-	e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE"
+	e = "-A out/soong/.intermediates/baz/android_common/NOTICE"
 	if strings.Contains(aapt2Flags, e) {
 		t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
 	}
@@ -2718,7 +2685,8 @@
 	test := func(t *testing.T, bp string, want bool, unbundled bool) {
 		t.Helper()
 
-		result := javaFixtureFactory.Extend(
+		result := android.GroupFixturePreparers(
+			prepareForJavaTest,
 			PrepareForTestWithPrebuiltsOfCurrentApi,
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 				if unbundled {
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index aadf6ad..d78651d 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -43,22 +43,22 @@
 		}
 	`
 
-	result := javaFixtureFactory.
-		Extend(
-			PrepareForTestWithJavaSdkLibraryFiles,
-			FixtureWithLastReleaseApis("foo"),
-			dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar", "platform:baz"),
-		).RunTestWithBp(t, bp)
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar", "platform:baz"),
+	).RunTestWithBp(t, bp)
 
 	dexpreoptBootJars := result.SingletonForTests("dex_bootjars")
 	rule := dexpreoptBootJars.Output(ruleFile)
 
 	for i := range expectedInputs {
-		expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
+		expectedInputs[i] = filepath.Join("out/soong/test_device", expectedInputs[i])
 	}
 
 	for i := range expectedOutputs {
-		expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
+		expectedOutputs[i] = filepath.Join("out/soong/test_device", expectedOutputs[i])
 	}
 
 	inputs := rule.Implicits.Strings()
@@ -69,9 +69,9 @@
 	sort.Strings(outputs)
 	sort.Strings(expectedOutputs)
 
-	android.AssertDeepEquals(t, "inputs", expectedInputs, inputs)
+	android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
 
-	android.AssertDeepEquals(t, "outputs", expectedOutputs, outputs)
+	android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
 }
 
 func TestDexpreoptBootJars(t *testing.T) {
@@ -107,7 +107,7 @@
 func TestDexpreoptBootZip(t *testing.T) {
 	ruleFile := "boot.zip"
 
-	ctx := android.PathContextForTesting(testConfig(nil, "", nil))
+	ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
 	expectedInputs := []string{}
 	for _, target := range ctx.Config().Targets[android.Android] {
 		for _, ext := range []string{".art", ".oat", ".vdex"} {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index f17d436..d6c6a2d 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -35,7 +35,8 @@
 	})
 }
 
-var hiddenApiFixtureFactory = javaFixtureFactory.Extend(PrepareForTestWithHiddenApiBuildComponents)
+var hiddenApiFixtureFactory = android.GroupFixturePreparers(
+	prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
 
 func TestHiddenAPISingleton(t *testing.T) {
 	result := hiddenApiFixtureFactory.Extend(
@@ -49,8 +50,8 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
-	want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
@@ -144,8 +145,8 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
-	want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
@@ -168,11 +169,11 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
-	fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
 
-	prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
+	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/dex/foo.jar"
 	android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
 }
 
@@ -195,11 +196,11 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
-	prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
 
-	fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
 }
 
@@ -243,7 +244,7 @@
 			).RunTest(t)
 
 			hiddenAPI := result.SingletonForTests("hiddenapi")
-			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+			hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
 
@@ -260,7 +261,7 @@
 }
 
 func generateDexedPath(subDir, dex, module string) string {
-	return fmt.Sprintf("%s/.intermediates/%s/android_common/%s/%s.jar", buildDir, subDir, dex, module)
+	return fmt.Sprintf("out/soong/.intermediates/%s/android_common/%s/%s.jar", subDir, dex, module)
 }
 
 func generateDexPath(moduleDir string, module string) string {
@@ -297,8 +298,8 @@
 	`)
 
 	expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
-	expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
-	expectedFlagsCsv := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+	expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
+	expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
 
 	foo := result.ModuleForTests("foo", "android_common")
 
@@ -306,12 +307,12 @@
 	cpRule := hiddenAPI.Rule("Cp")
 	actualCpInput := cpRule.BuildParams.Input
 	actualCpOutput := cpRule.BuildParams.Output
-	encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
+	encodeDexRule := foo.Rule("hiddenAPIEncodeDex").RelativeToTop()
 	actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
 
-	android.AssertStringEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput.String())
+	android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
 
-	android.AssertStringEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput.String())
+	android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput)
 
 	android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv)
 }
diff --git a/java/java_test.go b/java/java_test.go
index 99a96e1..b6f639f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -34,107 +33,31 @@
 	"android/soong/python"
 )
 
-var buildDir string
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_java_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
-}
-
-var emptyFixtureFactory = android.NewFixtureFactory(&buildDir)
-
-// Factory to use to create fixtures for tests in this package.
-var javaFixtureFactory = emptyFixtureFactory.Extend(
+// Legacy preparer used for running tests within the java package.
+//
+// This includes everything that was needed to run any test in the java package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testJava...() methods should avoid using this and instead use the
+// various preparers directly, using android.GroupFixturePreparers(...) to group them when
+// necessary.
+//
+// deprecated
+var prepareForJavaTest = android.GroupFixturePreparers(
 	genrule.PrepareForTestWithGenRuleBuildComponents,
 	// Get the CC build components but not default modules.
 	cc.PrepareForTestWithCcBuildComponents,
 	// Include all the default java modules.
 	PrepareForTestWithJavaDefaultModules,
+	PrepareForTestWithOverlayBuildComponents,
 	python.PrepareForTestWithPythonBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		ctx.RegisterModuleType("java_plugin", PluginFactory)
-
-		ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
 		ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
 	}),
 	dexpreopt.PrepareForTestWithDexpreopt,
 )
 
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
-}
-
-// testConfig is a legacy way of creating a test Config for testing java modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
-	return TestConfig(buildDir, env, bp, fs)
-}
-
-// testContext is a legacy way of creating a TestContext for testing java modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testContext(config android.Config) *android.TestContext {
-
-	ctx := android.NewTestArchContext(config)
-	RegisterRequiredBuildComponentsForTest(ctx)
-	ctx.RegisterModuleType("java_plugin", PluginFactory)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterComponentsMutator)
-
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
-	ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
-
-	android.RegisterPrebuiltMutators(ctx)
-
-	genrule.RegisterGenruleBuildComponents(ctx)
-
-	// Register module types and mutators from cc needed for JNI testing
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
-	})
-
-	return ctx
-}
-
-// run is a legacy way of running tests of java modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func run(t *testing.T, ctx *android.TestContext, config android.Config) {
-	t.Helper()
-
-	pathCtx := android.PathContextForTesting(config)
-	dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-
-	ctx.Register()
-	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
+	os.Exit(m.Run())
 }
 
 // testJavaError is a legacy way of running tests of java modules that expect errors.
@@ -144,8 +67,8 @@
 // deprecated
 func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	result := javaFixtureFactory.
-		Extend(dexpreopt.PrepareForTestWithDexpreopt).
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest, dexpreopt.PrepareForTestWithDexpreopt).
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithBp(t, bp)
 	return result.TestContext, result.Config
@@ -162,84 +85,41 @@
 	// the fixture's config will be ignored when RunTestWithConfig replaces it.
 	pathCtx := android.PathContextForTesting(config)
 	dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-	result := javaFixtureFactory.
+	result := prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithConfig(t, config)
 	return result.TestContext, result.Config
 }
 
-// runWithErrors 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 runWithErrors(t *testing.T, ctx *android.TestContext, config android.Config, pattern string) {
-	ctx.Register()
-	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-	_, errs = ctx.PrepareBuildActions(config)
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
-	return
-}
-
-// testJavaWithFS runs tests using the javaFixtureFactory
+// testJavaWithFS runs tests using the prepareForJavaTest
 //
 // See testJava for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testJavaWithFS(t *testing.T, bp string, fs android.MockFS) (*android.TestContext, android.Config) {
 	t.Helper()
-	result := javaFixtureFactory.Extend(fs.AddToFixture()).RunTestWithBp(t, bp)
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest, fs.AddToFixture()).RunTestWithBp(t, bp)
 	return result.TestContext, result.Config
 }
 
-// testJava runs tests using the javaFixtureFactory
+// testJava runs tests using the prepareForJavaTest
 //
-// Do not add any new usages of this, instead use the javaFixtureFactory directly as it makes it
+// Do not add any new usages of this, instead use the prepareForJavaTest directly as it makes it
 // much easier to customize the test behavior.
 //
 // If it is necessary to customize the behavior of an existing test that uses this then please first
-// convert the test to using javaFixtureFactory first and then in a following change add the
+// convert the test to using prepareForJavaTest first and then in a following change add the
 // appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
 // that it did not change the test behavior unexpectedly.
 //
 // deprecated
 func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	result := javaFixtureFactory.RunTestWithBp(t, bp)
+	result := prepareForJavaTest.RunTestWithBp(t, bp)
 	return result.TestContext, result.Config
 }
 
-// testJavaWithConfig runs tests using the javaFixtureFactory
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
-	t.Helper()
-	result := javaFixtureFactory.RunTestWithConfig(t, config)
-	return result.TestContext, result.Config
-}
-
-func moduleToPath(name string) string {
-	switch {
-	case name == `""`:
-		return name
-	case strings.HasSuffix(name, ".jar"):
-		return name
-	default:
-		return filepath.Join(buildDir, ".intermediates", name, "android_common", "turbine-combined", name+".jar")
-	}
-}
-
 // defaultModuleToPath constructs a path to the turbine generate jar for a default test module that
 // is defined in PrepareForIntegrationTestWithJava
 func defaultModuleToPath(name string) string {
@@ -249,7 +129,7 @@
 	case strings.HasSuffix(name, ".jar"):
 		return name
 	default:
-		return filepath.Join(buildDir, ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
+		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
 	}
 }
 
@@ -361,7 +241,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac").RelativeToTop()
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
@@ -369,8 +249,8 @@
 	}
 
 	baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
-	barTurbine := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
-	bazTurbine := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
+	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	bazTurbine := filepath.Join("out", "soong", ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
 
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barTurbine)
 
@@ -512,13 +392,19 @@
 			}
 		`
 
-		config := testConfig(nil, bp, nil)
-		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+		errorHandler := android.FixtureExpectsNoErrors
 		if enforce {
-			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
-		} else {
-			testJavaWithConfig(t, config)
+			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
 		}
+
+		android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+			}),
+		).
+			ExtendWithErrorHandler(errorHandler).
+			RunTestWithBp(t, bp)
 	}
 }
 
@@ -596,13 +482,16 @@
 			srcs: ["b.java"],
 		}
 	`
-	config := testConfig(nil, bp, nil)
-	config.TestProductVariables.MinimizeJavaDebugInfo = proptools.BoolPtr(true)
 
-	ctx, _ := testJavaWithConfig(t, config)
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.MinimizeJavaDebugInfo = proptools.BoolPtr(true)
+		}),
+	).RunTestWithBp(t, bp)
 
 	// first, check that the -g flag is added to target modules
-	targetLibrary := ctx.ModuleForTests("target_library", "android_common")
+	targetLibrary := result.ModuleForTests("target_library", "android_common")
 	targetJavaFlags := targetLibrary.Module().VariablesForTests()["javacFlags"]
 	if !strings.Contains(targetJavaFlags, "-g:source,lines") {
 		t.Errorf("target library javac flags %v should contain "+
@@ -611,7 +500,7 @@
 
 	// check that -g is not overridden for host modules
 	buildOS := android.BuildOs.String()
-	hostBinary := ctx.ModuleForTests("host_binary", buildOS+"_common")
+	hostBinary := result.ModuleForTests("host_binary", buildOS+"_common")
 	hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"]
 	if strings.Contains(hostJavaFlags, "-g:source,lines") {
 		t.Errorf("java_binary_host javac flags %v should not have "+
@@ -699,11 +588,9 @@
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
 	}
 
-	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().String()
-	expectedDexJar := buildDir + "/.intermediates/baz/android_common/dex/baz.jar"
-	if bazDexJar != expectedDexJar {
-		t.Errorf("baz dex jar build path expected %q, got %q", expectedDexJar, bazDexJar)
-	}
+	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath()
+	expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
+	android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
 
 	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
 }
@@ -744,7 +631,7 @@
 }
 
 func TestJavaSdkLibraryImport(t *testing.T) {
-	result := javaFixtureFactory.RunTestWithBp(t, `
+	result := prepareForJavaTest.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -798,7 +685,8 @@
 }
 
 func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 	).RunTestWithBp(t, `
@@ -840,7 +728,8 @@
 }
 
 func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 	).RunTestWithBp(t, `
@@ -947,7 +836,7 @@
 			if expectedErrorPattern != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
 			}
-			javaFixtureFactory.ExtendWithErrorHandler(errorHandler).RunTest(t, createPreparer(info))
+			prepareForJavaTest.ExtendWithErrorHandler(errorHandler).RunTest(t, createPreparer(info))
 		})
 	}
 
@@ -1078,14 +967,14 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac").RelativeToTop()
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	barTurbine := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
@@ -1331,7 +1220,7 @@
 }
 
 func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
-	config := testConfig(
+	config := TestConfig(t.TempDir(),
 		nil,
 		`
 		java_library {
@@ -1425,8 +1314,8 @@
 }
 
 func TestTurbine(t *testing.T) {
-	result := javaFixtureFactory.
-		Extend(FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
 		RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -1449,19 +1338,19 @@
 		}
 		`)
 
-	fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine")
-	barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine")
-	barJavac := result.ModuleForTests("bar", "android_common").Rule("javac")
-	barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine")
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
+	fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine").RelativeToTop()
+	barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine").RelativeToTop()
+	barJavac := result.ModuleForTests("bar", "android_common").Rule("javac").RelativeToTop()
+	barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine").RelativeToTop()
+	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac").RelativeToTop()
 
-	android.AssertArrayString(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs.Strings())
+	android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
 
-	fooHeaderJar := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
-	barTurbineJar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine", "bar.jar")
+	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+	barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["classpath"], fooHeaderJar)
 	android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
-	android.AssertArrayString(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs.Strings())
+	android.AssertPathsRelativeToTopEquals(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs)
 	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar")
 }
 
@@ -1474,9 +1363,9 @@
 		}
 		`)
 
-	barHeaderJar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
 	for i := 0; i < 3; i++ {
-		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
+		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i)).RelativeToTop()
 		if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
 			t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar)
 		}
@@ -1541,12 +1430,12 @@
 
 	barStubsOutput := barStubsOutputs[0]
 	barDoc := ctx.ModuleForTests("bar-doc", "android_common")
-	javaDoc := barDoc.Rule("javadoc")
+	javaDoc := barDoc.Rule("javadoc").RelativeToTop()
 	if g, w := javaDoc.Implicits.Strings(), barStubsOutput.String(); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
 	}
 
-	expected := "-sourcepath " + buildDir + "/.intermediates/bar-doc/android_common/srcjars "
+	expected := "-sourcepath out/soong/.intermediates/bar-doc/android_common/srcjars "
 	if !strings.Contains(javaDoc.RuleParams.Command, expected) {
 		t.Errorf("bar-doc command does not contain flag %q, but should\n%q", expected, javaDoc.RuleParams.Command)
 	}
@@ -1804,7 +1693,7 @@
 }
 
 func TestJavaLibrary(t *testing.T) {
-	config := testConfig(nil, "", map[string][]byte{
+	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
 						name: "core",
@@ -1816,14 +1705,12 @@
 					name: "core-jar",
 					srcs: [":core{.jar}"],
 				}
-`),
+		`),
 	})
-	ctx := testContext(config)
-	run(t, ctx, config)
 }
 
 func TestJavaImport(t *testing.T) {
-	config := testConfig(nil, "", map[string][]byte{
+	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_import {
 						name: "core",
@@ -1834,14 +1721,13 @@
 					name: "core-jar",
 					srcs: [":core{.jar}"],
 				}
-`),
+		`),
 	})
-	ctx := testContext(config)
-	run(t, ctx, config)
 }
 
 func TestJavaSdkLibrary(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithPrebuiltApis(map[string][]string{
 			"28": {"foo"},
@@ -1966,7 +1852,8 @@
 }
 
 func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 	).RunTestWithBp(t, `
@@ -2002,7 +1889,8 @@
 }
 
 func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2030,7 +1918,8 @@
 }
 
 func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
-	javaFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2051,11 +1940,11 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
-	javaFixtureFactory.
-		Extend(
-			PrepareForTestWithJavaSdkLibraryFiles,
-			FixtureWithLastReleaseApis("foo"),
-		).
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
@@ -2075,7 +1964,8 @@
 }
 
 func TestJavaSdkLibrary_Deps(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 	).RunTestWithBp(t, `
@@ -2100,7 +1990,7 @@
 }
 
 func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
-	javaFixtureFactory.RunTestWithBp(t, `
+	prepareForJavaTest.RunTestWithBp(t, `
 		java_sdk_library_import {
 			name: "foo",
 			public: {
@@ -2133,7 +2023,7 @@
 		`
 
 	t.Run("stubs.source", func(t *testing.T) {
-		javaFixtureFactory.
+		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
 			RunTestWithBp(t, bp+`
 				java_library {
@@ -2148,7 +2038,7 @@
 	})
 
 	t.Run("api.txt", func(t *testing.T) {
-		javaFixtureFactory.
+		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
 			RunTestWithBp(t, bp+`
 				java_library {
@@ -2162,7 +2052,7 @@
 	})
 
 	t.Run("removed-api.txt", func(t *testing.T) {
-		javaFixtureFactory.
+		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
 			RunTestWithBp(t, bp+`
 				java_library {
@@ -2177,7 +2067,7 @@
 }
 
 func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
-	javaFixtureFactory.
+	prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
 		RunTestWithBp(t, `
 			java_sdk_library {
@@ -2197,7 +2087,8 @@
 }
 
 func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
-	javaFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2214,7 +2105,8 @@
 }
 
 func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
-	javaFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2233,7 +2125,8 @@
 }
 
 func TestJavaSdkLibrary_SystemServer(t *testing.T) {
-	javaFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2252,7 +2145,7 @@
 }
 
 func TestJavaSdkLibrary_MissingScope(t *testing.T) {
-	javaFixtureFactory.
+	prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
 		RunTestWithBp(t, `
 			java_sdk_library {
@@ -2273,7 +2166,8 @@
 }
 
 func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
-	javaFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2296,7 +2190,8 @@
 }
 
 func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
-	result := javaFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -2388,7 +2283,7 @@
 
 // TODO(jungjw): Consider making this more robust by ignoring path order.
 func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
-	variables := ctx.ModuleForTests(moduleName, "android_common").Module().VariablesForTests()
+	variables := ctx.ModuleForTests(moduleName, "android_common").VariablesForTestsRelativeToTop()
 	flags := strings.Split(variables["javacFlags"], " ")
 	got := ""
 	for _, flag := range flags {
@@ -2398,7 +2293,7 @@
 			break
 		}
 	}
-	if expected != got {
+	if expected != android.StringPathRelativeToTop(ctx.Config().BuildDir(), got) {
 		t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got)
 	}
 }
@@ -2468,10 +2363,10 @@
 		ctx, _ := testJava(t, bp)
 
 		checkPatchModuleFlag(t, ctx, "foo", "")
-		expected := "java.base=.:" + buildDir
+		expected := "java.base=.:out/soong"
 		checkPatchModuleFlag(t, ctx, "bar", expected)
 		expected = "java.base=" + strings.Join([]string{
-			".", buildDir, "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":")
+			".", "out/soong", "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":")
 		checkPatchModuleFlag(t, ctx, "baz", expected)
 	})
 }
@@ -2590,11 +2485,9 @@
 
 	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
-	expected := []string{buildDir + "/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
 	actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
-	}
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
 }
 
 func TestDefaultInstallable(t *testing.T) {
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 77ef294..1c146a1 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -176,19 +176,22 @@
 		env := map[string]string{
 			"RUN_ERROR_PRONE": "true",
 		}
-		config := testConfig(env, bp, nil)
-		ctx, _ := testJavaWithConfig(t, config)
+
+		result := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureMergeEnv(env),
+		).RunTestWithBp(t, bp)
 
 		buildOS := android.BuildOs.String()
 
-		kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+		kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
 		//kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
-		javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
-		errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+		javac := result.ModuleForTests("foo", "android_common").Description("javac")
+		errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
 
-		bar := ctx.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
-		baz := ctx.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
-		myCheck := ctx.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
+		bar := result.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
+		baz := result.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
+		myCheck := result.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
 
 		// Test that the errorprone plugins are not passed to kapt
 		expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 3c43a8e..03e82c2 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -15,12 +15,23 @@
 package java
 
 import (
+	"path/filepath"
+
 	"android/soong/android"
+	"github.com/google/blueprint"
+
 	"fmt"
 )
 
 func init() {
 	registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext)
+
+	android.RegisterSdkMemberType(&compatConfigMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "compat_configs",
+			SupportsSdk:  true,
+		},
+	})
 }
 
 func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
@@ -42,6 +53,7 @@
 
 type platformCompatConfig struct {
 	android.ModuleBase
+	android.SdkBase
 
 	properties     platformCompatConfigProperties
 	installDirPath android.InstallPath
@@ -113,10 +125,54 @@
 func PlatformCompatConfigFactory() android.Module {
 	module := &platformCompatConfig{}
 	module.AddProperties(&module.properties)
+	android.InitSdkAwareModule(module)
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
 }
 
+type compatConfigMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*platformCompatConfig)
+	return ok
+}
+
+func (b *compatConfigMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_platform_compat_config")
+}
+
+func (b *compatConfigMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &compatConfigSdkMemberProperties{}
+}
+
+type compatConfigSdkMemberProperties struct {
+	android.SdkMemberPropertiesBase
+
+	Metadata android.Path
+}
+
+func (b *compatConfigSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+	module := variant.(*platformCompatConfig)
+	b.Metadata = module.metadataFile
+}
+
+func (b *compatConfigSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+	builder := ctx.SnapshotBuilder()
+	if b.Metadata != nil {
+		snapshotRelativePath := filepath.Join("compat_configs", ctx.Name(), b.Metadata.Base())
+		builder.CopyToSnapshot(b.Metadata, snapshotRelativePath)
+		propertySet.AddProperty("metadata", snapshotRelativePath)
+	}
+}
+
+var _ android.SdkMemberType = (*compatConfigMemberType)(nil)
+
 // A prebuilt version of the platform compat config module.
 type prebuiltCompatConfigModule struct {
 	android.ModuleBase
diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go
index 0c5d001..80d991c 100644
--- a/java/platform_compat_config_test.go
+++ b/java/platform_compat_config_test.go
@@ -21,7 +21,7 @@
 )
 
 func TestPlatformCompatConfig(t *testing.T) {
-	result := emptyFixtureFactory.RunTest(t,
+	result := android.GroupFixturePreparers(
 		PrepareForTestWithPlatformCompatConfig,
 		android.FixtureWithRootAndroidBp(`
 			platform_compat_config {
@@ -34,20 +34,11 @@
 				name: "myconfig3",
 			}
 		`),
-	)
+	).RunTest(t)
 
-	checkMergedCompatConfigInputs(t, result, "myconfig",
+	CheckMergedCompatConfigInputs(t, result, "myconfig",
 		"out/soong/.intermediates/myconfig1/myconfig1_meta.xml",
 		"out/soong/.intermediates/myconfig2/myconfig2_meta.xml",
 		"out/soong/.intermediates/myconfig3/myconfig3_meta.xml",
 	)
 }
-
-// Check that the merged file create by platform_compat_config_singleton has the correct inputs.
-func checkMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
-	sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
-	allOutputs := sourceGlobalCompatConfig.AllOutputs()
-	android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
-	output := sourceGlobalCompatConfig.Output(allOutputs[0])
-	android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
-}
diff --git a/java/plugin.go b/java/plugin.go
index 947c286..297ac2c 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -17,7 +17,11 @@
 import "android/soong/android"
 
 func init() {
-	android.RegisterModuleType("java_plugin", PluginFactory)
+	registerJavaPluginBuildComponents(android.InitRegistrationContext)
+}
+
+func registerJavaPluginBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_plugin", PluginFactory)
 }
 
 // A java_plugin module describes a host java library that will be used by javac as an annotation processor.
diff --git a/java/rro_test.go b/java/rro_test.go
index 061d9d3..0a10d93 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -24,7 +24,7 @@
 )
 
 func TestRuntimeResourceOverlay(t *testing.T) {
-	fs := map[string][]byte{
+	fs := android.MockFS{
 		"baz/res/res/values/strings.xml": nil,
 		"bar/res/res/values/strings.xml": nil,
 	}
@@ -57,15 +57,18 @@
 			sdk_version: "current",
 			resource_dirs: ["baz/res"],
 		}
-		`
-	config := testAppConfig(nil, bp, fs)
-	ctx := testContext(config)
-	run(t, ctx, config)
+	`
 
-	m := ctx.ModuleForTests("foo", "android_common")
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithOverlayBuildComponents,
+		fs.AddToFixture(),
+	).RunTestWithBp(t, bp)
+
+	m := result.ModuleForTests("foo", "android_common")
 
 	// Check AAPT2 link flags.
-	aapt2Flags := m.Output("package-res.apk").Args["flags"]
+	aapt2Flags := m.Output("package-res.apk").RelativeToTop().Args["flags"]
 	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
 	absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
 	if len(absentFlags) > 0 {
@@ -73,14 +76,14 @@
 	}
 
 	// Check overlay.list output for static_libs dependency.
-	overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
-	staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
+	overlayList := android.PathsRelativeToTop(m.Output("aapt2/overlay.list").Inputs)
+	staticLibPackage := "out/soong/.intermediates/bar/android_common/package-res.apk"
 	if !inList(staticLibPackage, overlayList) {
 		t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
 	}
 
 	// Check AAPT2 link flags for resource_libs dependency.
-	resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
+	resourceLibFlag := "-I " + "out/soong/.intermediates/baz/android_common/package-res.apk"
 	if !strings.Contains(aapt2Flags, resourceLibFlag) {
 		t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
 	}
@@ -97,7 +100,7 @@
 	if expected != signingFlag {
 		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
 	}
-	androidMkEntries := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0]
+	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
 	path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
 	expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
 	if !reflect.DeepEqual(path, expectedPath) {
@@ -106,19 +109,15 @@
 
 	// Check device location.
 	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay")}
-	if !reflect.DeepEqual(path, expectedPath) {
-		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
-	}
+	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
 
 	// A themed module has a different device location
-	m = ctx.ModuleForTests("foo_themed", "android_common")
-	androidMkEntries = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0]
+	m = result.ModuleForTests("foo_themed", "android_common")
+	androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
 	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/faza")}
-	if !reflect.DeepEqual(path, expectedPath) {
-		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
-	}
+	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
 
 	overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
 	expectedOverrides := []string{"foo"}
@@ -128,7 +127,7 @@
 }
 
 func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
-	ctx, _ := testJava(t, `
+	ctx, config := testJava(t, `
 		java_defaults {
 			name: "rro_defaults",
 			theme: "default_theme",
@@ -161,10 +160,8 @@
 
 	// Check device location.
 	path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath := []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/default_theme")}
-	if !reflect.DeepEqual(path, expectedPath) {
-		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
-	}
+	expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
 
 	//
 	// RRO module without defaults
@@ -180,10 +177,8 @@
 
 	// Check device location.
 	path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/system/overlay")}
-	if !reflect.DeepEqual(path, expectedPath) {
-		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
-	}
+	expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")}
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
 }
 
 func TestOverrideRuntimeResourceOverlay(t *testing.T) {
@@ -214,7 +209,7 @@
 	}{
 		{
 			variantName:       "android_common",
-			apkPath:           "/target/product/test_device/product/overlay/foo_overlay.apk",
+			apkPath:           "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
 			overrides:         nil,
 			targetVariant:     "android_common",
 			packageFlag:       "",
@@ -222,7 +217,7 @@
 		},
 		{
 			variantName:       "android_common_bar_overlay",
-			apkPath:           "/target/product/test_device/product/overlay/bar_overlay.apk",
+			apkPath:           "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
 			overrides:         []string{"foo_overlay"},
 			targetVariant:     "android_common_bar",
 			packageFlag:       "com.android.bar.overlay",
@@ -233,18 +228,7 @@
 		variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
 
 		// Check the final apk name
-		outputs := variant.AllOutputs()
-		expectedApkPath := buildDir + expected.apkPath
-		found := false
-		for _, o := range outputs {
-			if o == expectedApkPath {
-				found = true
-				break
-			}
-		}
-		if !found {
-			t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
-		}
+		variant.Output(expected.apkPath)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*RuntimeResourceOverlay)
@@ -298,7 +282,7 @@
 		"product/vendor/blah/overlay",
 	}
 
-	fs := map[string][]byte{
+	fs := android.MockFS{
 		"lib2/res/values/strings.xml":                             nil,
 		"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
 	}
@@ -334,19 +318,22 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			config := testAppConfig(nil, bp, fs)
-			config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
-			if testCase.enforceRROTargets != nil {
-				config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
-			}
-
-			ctx := testContext(config)
-			run(t, ctx, config)
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithOverlayBuildComponents,
+				fs.AddToFixture(),
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.ProductResourceOverlays = productResourceOverlays
+					if testCase.enforceRROTargets != nil {
+						variables.EnforceRROTargets = testCase.enforceRROTargets
+					}
+				}),
+			).RunTestWithBp(t, bp)
 
 			modules := []string{"foo", "bar"}
 			for _, moduleName := range modules {
-				module := ctx.ModuleForTests(moduleName, "android_common")
-				mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
+				module := result.ModuleForTests(moduleName, "android_common")
+				mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
 				actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
 				if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
 					t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 37875a4..028c4fe 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"path/filepath"
-	"reflect"
 	"strings"
 	"testing"
 
@@ -98,7 +97,7 @@
 			bootclasspath:  []string{"android_stubs_current", "core-lambda-stubs"},
 			system:         "core-current-stubs-system-modules",
 			java9classpath: []string{"android_stubs_current"},
-			aidl:           "-p" + buildDir + "/framework.aidl",
+			aidl:           "-pout/soong/framework.aidl",
 		},
 		{
 
@@ -107,7 +106,7 @@
 			bootclasspath:  []string{"android_system_stubs_current", "core-lambda-stubs"},
 			system:         "core-current-stubs-system-modules",
 			java9classpath: []string{"android_system_stubs_current"},
-			aidl:           "-p" + buildDir + "/framework.aidl",
+			aidl:           "-pout/soong/framework.aidl",
 		},
 		{
 
@@ -135,7 +134,7 @@
 			bootclasspath:  []string{"android_test_stubs_current", "core-lambda-stubs"},
 			system:         "core-current-stubs-system-modules",
 			java9classpath: []string{"android_test_stubs_current"},
-			aidl:           "-p" + buildDir + "/framework.aidl",
+			aidl:           "-pout/soong/framework.aidl",
 		},
 		{
 
@@ -222,7 +221,7 @@
 			bootclasspath:  []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
 			system:         "core-current-stubs-system-modules",
 			java9classpath: []string{"android_module_lib_stubs_current"},
-			aidl:           "-p" + buildDir + "/framework_non_updatable.aidl",
+			aidl:           "-pout/soong/framework_non_updatable.aidl",
 		},
 		{
 			name:           "system_server_current",
@@ -230,7 +229,7 @@
 			bootclasspath:  []string{"android_system_server_stubs_current", "core-lambda-stubs"},
 			system:         "core-current-stubs-system-modules",
 			java9classpath: []string{"android_system_server_stubs_current"},
-			aidl:           "-p" + buildDir + "/framework.aidl",
+			aidl:           "-pout/soong/framework.aidl",
 		},
 	}
 
@@ -303,23 +302,23 @@
 				} else {
 					dir = defaultJavaDir
 				}
-				system = "--system=" + filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system")
+				system = "--system=" + filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system")
 				// The module-relative parts of these paths are hardcoded in system_modules.go:
 				systemDeps = []string{
-					filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
-					filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
-					filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "release"),
+					filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
+					filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
+					filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "release"),
 				}
 			}
 
 			checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
 				foo := result.ModuleForTests("foo", variant)
-				javac := foo.Rule("javac")
+				javac := foo.Rule("javac").RelativeToTop()
 				var deps []string
 
-				aidl := foo.MaybeRule("aidl")
+				aidl := foo.MaybeRule("aidl").RelativeToTop()
 				if aidl.Rule != nil {
-					deps = append(deps, aidl.Output.String())
+					deps = append(deps, android.PathRelativeToTop(aidl.Output))
 				}
 
 				got := javac.Args["bootClasspath"]
@@ -347,12 +346,11 @@
 					t.Errorf("classpath expected %q != got %q", expected, got)
 				}
 
-				if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
-					t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
-				}
+				android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits)
 			}
 
-			fixtureFactory := javaFixtureFactory.Extend(
+			fixtureFactory := android.GroupFixturePreparers(
+				prepareForJavaTest,
 				FixtureWithPrebuiltApis(map[string][]string{
 					"29":      {},
 					"30":      {},
@@ -378,7 +376,7 @@
 				checkClasspath(t, result, true /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
+					aidl := result.ModuleForTests("foo", variant).Rule("aidl").RelativeToTop()
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -391,7 +389,7 @@
 				checkClasspath(t, result, false /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
+					aidl := result.ModuleForTests("foo", variant).Rule("aidl").RelativeToTop()
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 3d9f398..7d8935a 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -50,7 +50,7 @@
 `)
 
 func TestJavaSystemModules(t *testing.T) {
-	result := javaFixtureFactory.RunTest(t, addSourceSystemModules)
+	result := prepareForJavaTest.RunTest(t, addSourceSystemModules)
 
 	// check the existence of the source module
 	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
@@ -77,7 +77,7 @@
 `)
 
 func TestJavaSystemModulesImport(t *testing.T) {
-	result := javaFixtureFactory.RunTest(t, addPrebuiltSystemModules)
+	result := prepareForJavaTest.RunTest(t, addPrebuiltSystemModules)
 
 	// check the existence of the renamed prebuilt module
 	prebuiltSystemModules := result.ModuleForTests("system-modules", "android_common")
@@ -89,7 +89,7 @@
 }
 
 func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
-	result := javaFixtureFactory.RunTest(t,
+	result := prepareForJavaTest.RunTest(t,
 		addSourceSystemModules,
 		addPrebuiltSystemModules,
 	)
diff --git a/java/testing.go b/java/testing.go
index 4b8b849..82a2103 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -44,6 +44,7 @@
 	android.PrepareForTestWithAndroidBuildComponents,
 	// Make java build components available to the test.
 	android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+	android.FixtureRegisterWithContext(registerJavaPluginBuildComponents),
 )
 
 // Test fixture preparer that will define default java modules, e.g. standard prebuilt modules.
@@ -54,6 +55,8 @@
 	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", GatherRequiredDepsForTest()),
 )
 
+var PrepareForTestWithOverlayBuildComponents = android.FixtureRegisterWithContext(registerOverlayBuildComponents)
+
 // Prepare a fixture to use all java module types, mutators and singletons fully.
 //
 // This should only be used by tests that want to run with as much of the build enabled as possible.
@@ -319,3 +322,12 @@
 		t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual)
 	}
 }
+
+// Check that the merged file create by platform_compat_config_singleton has the correct inputs.
+func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
+	sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
+	allOutputs := sourceGlobalCompatConfig.AllOutputs()
+	android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
+	output := sourceGlobalCompatConfig.Output(allOutputs[0])
+	android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
+}
diff --git a/python/python_test.go b/python/python_test.go
index 6263c8a..f57f504 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -335,13 +335,12 @@
 		}
 
 		t.Run(d.desc, func(t *testing.T) {
-			result := emptyFixtureFactory.
-				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
-				RunTest(t,
-					android.PrepareForTestWithDefaults,
-					PrepareForTestWithPythonBuildComponents,
-					d.mockFiles.AddToFixture(),
-				)
+			result := android.GroupFixturePreparers(
+				android.PrepareForTestWithDefaults,
+				PrepareForTestWithPythonBuildComponents,
+				d.mockFiles.AddToFixture(),
+			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+				RunTest(t)
 
 			if len(result.Errs) > 0 {
 				return
@@ -376,8 +375,6 @@
 	android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
 }
 
-var emptyFixtureFactory = android.NewFixtureFactory(nil)
-
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index e24f666..e90564f 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -66,7 +66,7 @@
 	for _, tc := range clippyLintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(buildDir, nil, bp, fs)
+			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 			ctx := CreateTestContext(config)
 			ctx.Register()
 			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 2b40727..3ed086f 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -153,7 +153,7 @@
 	for _, tc := range lintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(buildDir, nil, bp, fs)
+			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 			ctx := CreateTestContext(config)
 			ctx.Register()
 			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 289bcb8..8f64f56 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -27,15 +27,14 @@
 // 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 {
-	tctx := newTestRustCtx(t, bp)
-	tctx.env = map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
-	tctx.generateConfig()
-	tctx.parse(t)
+	result := prepareForRustTest.
+		Extend(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
 	// it exists.
-	content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
+	content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
 	if err != nil {
 		t.Errorf("rust-project.json has not been generated")
 	}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 1ac66f3..f0f5ec0 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -101,7 +101,7 @@
 	}
 
 	// Check that we're including the exported directory from libprotobuf-cpp-full
-	if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
+	if w := "-I" + rustDefaultsDir + "libprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
 		t.Errorf("expected %q in %q", w, cmd)
 	}
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index a0ed534..bed28ec 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -15,7 +15,6 @@
 package rust
 
 import (
-	"io/ioutil"
 	"os"
 	"runtime"
 	"strings"
@@ -24,72 +23,94 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/cc"
+	"android/soong/genrule"
 )
 
-var (
-	buildDir string
-)
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_rust_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
-}
-
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
+	os.Exit(m.Run())
+}
 
-		return m.Run()
-	}
+var prepareForRustTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithArchMutator,
+	android.PrepareForTestWithDefaults,
+	android.PrepareForTestWithPrebuilts,
 
-	os.Exit(run())
+	genrule.PrepareForTestWithGenRuleBuildComponents,
+
+	PrepareForIntegrationTestWithRust,
+)
+
+var rustMockedFiles = android.MockFS{
+	"foo.rs":          nil,
+	"foo.c":           nil,
+	"src/bar.rs":      nil,
+	"src/any.h":       nil,
+	"proto.proto":     nil,
+	"proto/buf.proto": nil,
+	"buf.proto":       nil,
+	"foo.proto":       nil,
+	"liby.so":         nil,
+	"libz.so":         nil,
+	"data.txt":        nil,
 }
 
 // testRust returns a TestContext in which a basic environment has been setup.
-// This environment contains a few mocked files. See testRustCtx.useMockedFs
-// for the list of these files.
+// This environment contains a few mocked files. See rustMockedFiles for the list of these files.
 func testRust(t *testing.T, bp string) *android.TestContext {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	return tctx.parse(t)
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+	).
+		RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
 func testRustVndk(t *testing.T, bp string) *android.TestContext {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	tctx.setVndk(t)
-	return tctx.parse(t)
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.DeviceVndkVersion = StringPtr("current")
+				variables.ProductVndkVersion = StringPtr("current")
+				variables.Platform_vndk_version = StringPtr("VER")
+			},
+		),
+	).RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
 // testRustCov returns a TestContext in which a basic environment has been
 // setup. This environment explicitly enables coverage.
 func testRustCov(t *testing.T, bp string) *android.TestContext {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	tctx.enableCoverage(t)
-	return tctx.parse(t)
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ClangCoverage = proptools.BoolPtr(true)
+				variables.Native_coverage = proptools.BoolPtr(true)
+				variables.NativeCoveragePaths = []string{"*"}
+			},
+		),
+	).RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
 // testRustError ensures that at least one error was raised and its value
 // matches the pattern provided. The error can be either in the parsing of the
 // Blueprint or when generating the build actions.
 func testRustError(t *testing.T, pattern string, bp string) {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	tctx.parseError(t, pattern)
+	skipTestIfOsNotSupported(t)
+	android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+		RunTestWithBp(t, bp)
 }
 
 // testRustCtx is used to build a particular test environment. Unless your
@@ -102,99 +123,11 @@
 	config *android.Config
 }
 
-// newTestRustCtx returns a new testRustCtx for the Blueprint definition argument.
-func newTestRustCtx(t *testing.T, bp string) *testRustCtx {
+func skipTestIfOsNotSupported(t *testing.T) {
 	// TODO (b/140435149)
 	if runtime.GOOS != "linux" {
 		t.Skip("Rust Soong tests can only be run on Linux hosts currently")
 	}
-	return &testRustCtx{bp: bp}
-}
-
-// useMockedFs setup a default mocked filesystem for the test environment.
-func (tctx *testRustCtx) useMockedFs() {
-	tctx.fs = map[string][]byte{
-		"foo.rs":          nil,
-		"foo.c":           nil,
-		"src/bar.rs":      nil,
-		"src/any.h":       nil,
-		"proto.proto":     nil,
-		"proto/buf.proto": nil,
-		"buf.proto":       nil,
-		"foo.proto":       nil,
-		"liby.so":         nil,
-		"libz.so":         nil,
-		"data.txt":        nil,
-	}
-}
-
-// generateConfig creates the android.Config based on the bp, fs and env
-// attributes of the testRustCtx.
-func (tctx *testRustCtx) generateConfig() {
-	tctx.bp = tctx.bp + GatherRequiredDepsForTest()
-	tctx.bp = tctx.bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-	cc.GatherRequiredFilesForTest(tctx.fs)
-	config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
-	tctx.config = &config
-}
-
-// enableCoverage configures the test to enable coverage.
-func (tctx *testRustCtx) enableCoverage(t *testing.T) {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	tctx.config.TestProductVariables.ClangCoverage = proptools.BoolPtr(true)
-	tctx.config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
-	tctx.config.TestProductVariables.NativeCoveragePaths = []string{"*"}
-}
-
-func (tctx *testRustCtx) setVndk(t *testing.T) {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	tctx.config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	tctx.config.TestProductVariables.ProductVndkVersion = StringPtr("current")
-	tctx.config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-}
-
-// parse validates the configuration and parses the Blueprint file. It returns
-// a TestContext which can be used to retrieve the generated modules via
-// ModuleForTests.
-func (tctx testRustCtx) parse(t *testing.T) *android.TestContext {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	ctx := CreateTestContext(*tctx.config)
-	ctx.Register()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(*tctx.config)
-	android.FailIfErrored(t, errs)
-	return ctx
-}
-
-// parseError parses the Blueprint file and ensure that at least one error
-// matching the provided pattern is observed.
-func (tctx testRustCtx) parseError(t *testing.T, pattern string) {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	ctx := CreateTestContext(*tctx.config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	_, errs = ctx.PrepareBuildActions(*tctx.config)
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
 }
 
 // Test that we can extract the link path from a lib path.
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 5b7a6da..43e6cbf 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -87,7 +87,7 @@
         comm -13 "${outfile}.dynsyms" "${outfile}.funcsyms" > "${outfile}.keep_symbols"
         echo >> "${outfile}.keep_symbols" # Ensure that the keep_symbols file is not empty.
         "${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo"
-        "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
+        "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --remove-section .rustc --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
         "${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
         "${XZ}" --block-size=64k --threads=0 "${outfile}.mini_debuginfo"
 
diff --git a/scripts/update-apex-allowed-deps.sh b/scripts/update-apex-allowed-deps.sh
deleted file mode 100755
index 872d746..0000000
--- a/scripts/update-apex-allowed-deps.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash -e
-#
-# The script to run locally to re-generate global allowed list of dependencies
-# for updatable modules.
-
-if [ ! -e "build/envsetup.sh" ]; then
-  echo "ERROR: $0 must be run from the top of the tree"
-  exit 1
-fi
-
-source build/envsetup.sh > /dev/null || exit 1
-
-readonly OUT_DIR=$(get_build_var OUT_DIR)
-
-readonly ALLOWED_DEPS_FILE="build/soong/apex/allowed_deps.txt"
-readonly NEW_ALLOWED_DEPS_FILE="${OUT_DIR}/soong/apex/depsinfo/new-allowed-deps.txt"
-
-# If the script is run after droidcore failure, ${NEW_ALLOWED_DEPS_FILE}
-# should already be built. If running the script manually, make sure it exists.
-m "${NEW_ALLOWED_DEPS_FILE}" -j
-
-cat > "${ALLOWED_DEPS_FILE}" << EndOfFileComment
-# A list of allowed dependencies for all updatable modules.
-#
-# The list tracks all direct and transitive dependencies that end up within any
-# of the updatable binaries; specifically excluding external dependencies
-# required to compile those binaries. This prevents potential regressions in
-# case a new dependency is not aware of the different functional and
-# non-functional requirements being part of an updatable module, for example
-# setting correct min_sdk_version.
-#
-# To update the list, run:
-# repo-root$ build/soong/scripts/update-apex-allowed-deps.sh
-#
-# See go/apex-allowed-deps-error for more details.
-# TODO(b/157465465): introduce automated quality signals and remove this list.
-EndOfFileComment
-
-cat "${NEW_ALLOWED_DEPS_FILE}" >> "${ALLOWED_DEPS_FILE}"
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 6e49c6d..7b034e6 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -23,6 +23,7 @@
         "boot_image_sdk_test.go",
         "bp_test.go",
         "cc_sdk_test.go",
+        "compat_config_sdk_test.go",
         "exports_test.go",
         "java_sdk_test.go",
         "sdk_test.go",
diff --git a/sdk/boot_image_sdk_test.go b/sdk/boot_image_sdk_test.go
index 9805a6a..5a03e34 100644
--- a/sdk/boot_image_sdk_test.go
+++ b/sdk/boot_image_sdk_test.go
@@ -14,20 +14,27 @@
 
 package sdk
 
-import "testing"
+import (
+	"testing"
+
+	"android/soong/android"
+)
 
 func TestSnapshotWithBootImage(t *testing.T) {
-	result := testSdkWithJava(t, `
-		sdk {
-			name: "mysdk",
-			boot_images: ["mybootimage"],
-		}
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				boot_images: ["mybootimage"],
+			}
 
-		boot_image {
-			name: "mybootimage",
-			image_name: "art",
-		}
-	`)
+			boot_image {
+				name: "mybootimage",
+				image_name: "art",
+			}
+		`),
+	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
@@ -60,3 +67,39 @@
 `),
 		checkAllCopyRules(""))
 }
+
+// Test that boot_image works with sdk.
+func TestBasicSdkWithBootImage(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForSdkTestWithApex,
+		prepareForSdkTestWithJava,
+		android.FixtureWithRootAndroidBp(`
+		sdk {
+			name: "mysdk",
+			boot_images: ["mybootimage"],
+		}
+
+		boot_image {
+			name: "mybootimage",
+			image_name: "art",
+			apex_available: ["myapex"],
+		}
+
+		sdk_snapshot {
+			name: "mysdk@1",
+			boot_images: ["mybootimage_mysdk_1"],
+		}
+
+		prebuilt_boot_image {
+			name: "mybootimage_mysdk_1",
+			sdk_member_name: "mybootimage",
+			prefer: false,
+			visibility: ["//visibility:public"],
+			apex_available: [
+				"myapex",
+			],
+			image_name: "art",
+		}
+	`),
+	).RunTest(t)
+}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index a2539c9..9626a04 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -808,7 +808,8 @@
 }
 
 func TestSnapshotWithSingleHostOsType(t *testing.T) {
-	result := sdkFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForSdkTest,
 		ccTestFs.AddToFixture(),
 		cc.PrepareForTestOnLinuxBionic,
 		android.FixtureModifyConfig(func(config android.Config) {
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
new file mode 100644
index 0000000..dffc02a
--- /dev/null
+++ b/sdk/compat_config_sdk_test.go
@@ -0,0 +1,70 @@
+// 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 sdk
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func TestSnapshotWithCompatConfig(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		java.PrepareForTestWithPlatformCompatConfig,
+	).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			compat_configs: ["myconfig"],
+		}
+
+		platform_compat_config {
+			name: "myconfig",
+		}
+	`)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_platform_compat_config {
+    name: "mysdk_myconfig@current",
+    sdk_member_name: "myconfig",
+    visibility: ["//visibility:public"],
+    metadata: "compat_configs/myconfig/myconfig_meta.xml",
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    compat_configs: ["mysdk_myconfig@current"],
+}
+`),
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_platform_compat_config {
+    name: "myconfig",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    metadata: "compat_configs/myconfig/myconfig_meta.xml",
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myconfig/android_common/myconfig_meta.xml -> compat_configs/myconfig/myconfig_meta.xml
+`),
+	)
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 6ca8512..e561529 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -445,20 +445,26 @@
 	}
 }
 
+// An interface that encapsulates all the functionality needed to manage the sdk dependencies.
+//
+// It is a mixture of apex and sdk module functionality.
+type sdkAndApexModule interface {
+	android.Module
+	android.DepIsInSameApex
+	android.RequiredSdks
+}
+
 // Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
 // descendants
 func sdkDepsMutator(mctx android.TopDownMutatorContext) {
-	if parent, ok := mctx.Module().(interface {
-		android.DepIsInSameApex
-		android.RequiredSdks
-	}); ok {
+	if parent, ok := mctx.Module().(sdkAndApexModule); ok {
 		// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
 		// by reading its own properties like `uses_sdks`.
 		requiredSdks := parent.RequiredSdks()
 		if len(requiredSdks) > 0 {
 			mctx.VisitDirectDeps(func(m android.Module) {
 				// Only propagate required sdks from the apex onto its contents.
-				if dep, ok := m.(android.SdkAware); ok && parent.DepIsInSameApex(mctx, dep) {
+				if dep, ok := m.(android.SdkAware); ok && android.IsDepInSameApex(mctx, parent, dep) {
 					dep.BuildWithSdks(requiredSdks)
 				}
 			})
@@ -497,10 +503,7 @@
 
 // Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
 func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
-	if m, ok := mctx.Module().(interface {
-		android.DepIsInSameApex
-		android.RequiredSdks
-	}); ok {
+	if m, ok := mctx.Module().(sdkAndApexModule); ok {
 		requiredSdks := m.RequiredSdks()
 		if len(requiredSdks) == 0 {
 			return
@@ -519,9 +522,18 @@
 				return
 			}
 
-			// If the dep is outside of the APEX, but is not in any of the
-			// required SDKs, we know that the dep is a violation.
+			// If the dep is outside of the APEX, but is not in any of the required SDKs, we know that the
+			// dep is a violation.
 			if sa, ok := dep.(android.SdkAware); ok {
+				// It is not an error if a dependency that is excluded from the apex due to the tag is not
+				// in one of the required SDKs. That is because all of the existing tags that implement it
+				// do not depend on modules which can or should belong to an sdk_snapshot.
+				if _, ok := tag.(android.ExcludeFromApexContentsTag); ok {
+					// The tag defines a dependency that never requires the child module to be part of the
+					// same apex.
+					return
+				}
+
 				if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
 					mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
 						sa.Name(), sa.ContainingSdk(), requiredSdks)
diff --git a/sdk/testing.go b/sdk/testing.go
index 6df402c..d21f425 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -27,14 +27,9 @@
 	"android/soong/java"
 )
 
-var sdkFixtureFactory = android.NewFixtureFactory(
-	nil,
+// Prepare for running an sdk test with an apex.
+var prepareForSdkTestWithApex = android.GroupFixturePreparers(
 	apex.PrepareForTestWithApexBuildComponents,
-	cc.PrepareForTestWithCcDefaultModules,
-	genrule.PrepareForTestWithGenRuleBuildComponents,
-	java.PrepareForTestWithJavaBuildComponents,
-	PrepareForTestWithSdkBuildComponents,
-
 	android.FixtureAddTextFile("sdk/tests/Android.bp", `
 		apex_key {
 			name: "myapex.key",
@@ -59,6 +54,24 @@
 		"myapex.x509.pem":                              nil,
 		"myapex.pk8":                                   nil,
 	}),
+)
+
+// Legacy preparer used for running tests within the sdk package.
+//
+// This includes everything that was needed to run any test in the sdk package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testSdkError() and testSdkWithFs() methods should avoid using this and
+// instead should use the various preparers directly using android.GroupFixturePreparers(...) to
+// group them when necessary.
+//
+// deprecated
+var prepareForSdkTest = android.GroupFixturePreparers(
+	cc.PrepareForTestWithCcDefaultModules,
+	genrule.PrepareForTestWithGenRuleBuildComponents,
+	java.PrepareForTestWithJavaBuildComponents,
+	PrepareForTestWithSdkBuildComponents,
+
+	prepareForSdkTestWithApex,
 
 	cc.PrepareForTestOnWindows,
 	android.FixtureModifyConfig(func(config android.Config) {
@@ -77,12 +90,12 @@
 
 func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult {
 	t.Helper()
-	return sdkFixtureFactory.RunTest(t, fs.AddToFixture(), android.FixtureWithRootAndroidBp(bp))
+	return prepareForSdkTest.RunTest(t, fs.AddToFixture(), android.FixtureWithRootAndroidBp(bp))
 }
 
 func testSdkError(t *testing.T, pattern, bp string) {
 	t.Helper()
-	sdkFixtureFactory.
+	prepareForSdkTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithBp(t, bp)
 }
@@ -201,13 +214,33 @@
 
 	// Populate a mock filesystem with the files that would have been copied by
 	// the rules.
-	fs := make(map[string][]byte)
+	fs := android.MockFS{}
+	snapshotSubDir := "snapshot"
 	for _, dest := range snapshotBuildInfo.snapshotContents {
-		fs[dest] = nil
+		fs[filepath.Join(snapshotSubDir, dest)] = nil
 	}
+	fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
 
-	// Process the generated bp file to make sure it is valid.
-	testSdkWithFs(t, snapshotBuildInfo.androidBpContents, fs)
+	preparer := result.Preparer()
+
+	// Process the generated bp file to make sure it is valid. Use the same preparer as was used to
+	// produce this result.
+	t.Run("snapshot without source", func(t *testing.T) {
+		android.GroupFixturePreparers(
+			preparer,
+			// TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
+			//  files the snapshot needs are actually copied into the snapshot.
+
+			// Add the files (including bp) created for this snapshot to the test fixture.
+			fs.AddToFixture(),
+
+			// Remove the source Android.bp file to make sure it works without.
+			// TODO(b/183184375): Add a test with the source.
+			android.FixtureModifyMockFS(func(fs android.MockFS) {
+				delete(fs, "Android.bp")
+			}),
+		).RunTest(t)
+	})
 }
 
 type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 5887b56..9e7e594 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -13,8 +13,7 @@
 	os.Exit(m.Run())
 }
 
-var shFixtureFactory = android.NewFixtureFactory(
-	nil,
+var prepareForShTest = android.GroupFixturePreparers(
 	cc.PrepareForTestWithCcBuildComponents,
 	PrepareForTestWithShBuildComponents,
 	android.FixtureMergeMockFs(android.MockFS{
@@ -24,19 +23,19 @@
 	}),
 )
 
-// testShBinary runs tests using the shFixtureFactory
+// testShBinary runs tests using the prepareForShTest
 //
-// Do not add any new usages of this, instead use the shFixtureFactory directly as it makes it much
+// Do not add any new usages of this, instead use the prepareForShTest directly as it makes it much
 // easier to customize the test behavior.
 //
 // If it is necessary to customize the behavior of an existing test that uses this then please first
-// convert the test to using shFixtureFactory first and then in a following change add the
+// convert the test to using prepareForShTest first and then in a following change add the
 // appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
 // that it did not change the test behavior unexpectedly.
 //
 // deprecated
 func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
-	result := shFixtureFactory.RunTestWithBp(t, bp)
+	result := prepareForShTest.RunTestWithBp(t, bp)
 
 	return result.TestContext, result.Config
 }
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index cb1e362..e9d9051 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -30,8 +30,6 @@
 	os.Exit(m.Run())
 }
 
-var emptyFixtureFactory = android.NewFixtureFactory(nil)
-
 func test(t *testing.T, bp string) *android.TestResult {
 	t.Helper()
 
@@ -124,7 +122,7 @@
 		"com/android2/OdmProperties.sysprop":         nil,
 	}
 
-	result := emptyFixtureFactory.RunTest(t,
+	result := android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
 		java.PrepareForTestWithJavaDefaultModules,
 		PrepareForTestWithSyspropBuildComponents,
@@ -135,7 +133,7 @@
 		}),
 		mockFS.AddToFixture(),
 		android.FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 
 	return result
 }
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 1fabd92..45ccd04 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -112,9 +112,15 @@
 
 func stopRBE(ctx Context, config Config) {
 	cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
-	if output, err := cmd.CombinedOutput(); err != nil {
+	output, err := cmd.CombinedOutput()
+	if err != nil {
 		ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
 	}
+
+	if len(output) > 0 {
+		fmt.Fprintln(ctx.Writer, "")
+		fmt.Fprintln(ctx.Writer, fmt.Sprintf("%s", output))
+	}
 }
 
 // DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
diff --git a/xml/xml_test.go b/xml/xml_test.go
index 83ae51c..a59a293 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -26,8 +26,6 @@
 	os.Exit(m.Run())
 }
 
-var emptyFixtureFactory = android.NewFixtureFactory(nil)
-
 func testXml(t *testing.T, bp string) *android.TestResult {
 	fs := android.MockFS{
 		"foo.xml": nil,
@@ -37,13 +35,13 @@
 		"baz.xml": nil,
 	}
 
-	return emptyFixtureFactory.RunTest(t,
+	return android.GroupFixturePreparers(
 		android.PrepareForTestWithArchMutator,
 		etc.PrepareForTestWithPrebuiltEtc,
 		PreparerForTestWithXmlBuildComponents,
 		fs.AddToFixture(),
 		android.FixtureWithRootAndroidBp(bp),
-	)
+	).RunTest(t)
 }
 
 // Minimal test