Merge "Reorganize soong_build/main.go ."
diff --git a/android/apex.go b/android/apex.go
index a5ff442..257bdad 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -453,6 +453,23 @@
 	}
 }
 
+// AvailableToSameApexes returns true if the two modules are apex_available to
+// exactly the same set of APEXes (and platform), i.e. if their apex_available
+// properties have the same elements.
+func AvailableToSameApexes(mod1, mod2 ApexModule) bool {
+	mod1ApexAvail := SortedUniqueStrings(mod1.apexModuleBase().ApexProperties.Apex_available)
+	mod2ApexAvail := SortedUniqueStrings(mod2.apexModuleBase().ApexProperties.Apex_available)
+	if len(mod1ApexAvail) != len(mod2ApexAvail) {
+		return false
+	}
+	for i, v := range mod1ApexAvail {
+		if v != mod2ApexAvail[i] {
+			return false
+		}
+	}
+	return true
+}
+
 type byApexName []ApexInfo
 
 func (a byApexName) Len() int           { return len(a) }
diff --git a/android/bazel.go b/android/bazel.go
index 5bb3879..51ff3cb 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -129,49 +129,61 @@
 	}
 
 	// Per-module denylist to always opt modules out.
-	bp2buildModuleDoNotConvert = map[string]bool{
-		"libBionicBenchmarksUtils":      true,
-		"libbionic_spawn_benchmark":     true,
-		"libc_jemalloc_wrapper":         true,
-		"libc_bootstrap":                true,
-		"libc_init_static":              true,
-		"libc_init_dynamic":             true,
-		"libc_tzcode":                   true,
-		"libc_freebsd":                  true,
-		"libc_freebsd_large_stack":      true,
-		"libc_netbsd":                   true,
-		"libc_openbsd_ndk":              true,
-		"libc_openbsd_large_stack":      true,
-		"libc_openbsd":                  true,
-		"libc_gdtoa":                    true,
-		"libc_fortify":                  true,
-		"libc_bionic":                   true,
-		"libc_bionic_ndk":               true,
-		"libc_bionic_systrace":          true,
-		"libc_pthread":                  true,
-		"libc_syscalls":                 true,
-		"libc_aeabi":                    true,
-		"libc_ndk":                      true,
-		"libc_nopthread":                true,
-		"libc_common":                   true,
-		"libc_static_dispatch":          true,
-		"libc_dynamic_dispatch":         true,
-		"libc_common_static":            true,
-		"libc_common_shared":            true,
-		"libc_unwind_static":            true,
-		"libc_nomalloc":                 true,
-		"libasync_safe":                 true,
-		"libc_malloc_debug_backtrace":   true,
-		"libsystemproperties":           true,
-		"libdl_static":                  true,
-		"liblinker_main":                true,
-		"liblinker_malloc":              true,
-		"liblinker_debuggerd_stub":      true,
-		"libbionic_tests_headers_posix": true,
-		"libc_dns":                      true,
+	bp2buildModuleDoNotConvertList = []string{
+		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static
+		"libbionic_spawn_benchmark",     // ruperts@, cc_library_static, depends on //system/libbase
+		"libc_jemalloc_wrapper",         // ruperts@, cc_library_static, depends on //external/jemalloc_new
+		"libc_bootstrap",                // ruperts@, cc_library_static
+		"libc_init_static",              // ruperts@, cc_library_static
+		"libc_init_dynamic",             // ruperts@, cc_library_static
+		"libc_tzcode",                   // ruperts@, cc_library_static
+		"libc_freebsd",                  // ruperts@, cc_library_static
+		"libc_freebsd_large_stack",      // ruperts@, cc_library_static
+		"libc_netbsd",                   // ruperts@, cc_library_static
+		"libc_openbsd_ndk",              // ruperts@, cc_library_static
+		"libc_openbsd_large_stack",      // ruperts@, cc_library_static
+		"libc_openbsd",                  // ruperts@, cc_library_static
+		"libc_gdtoa",                    // ruperts@, cc_library_static
+		"libc_fortify",                  // ruperts@, cc_library_static
+		"libc_bionic",                   // ruperts@, cc_library_static
+		"libc_bionic_ndk",               // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
+		"libc_bionic_systrace",          // ruperts@, cc_library_static
+		"libc_pthread",                  // ruperts@, cc_library_static
+		"libc_syscalls",                 // ruperts@, cc_library_static
+		"libc_aeabi",                    // ruperts@, cc_library_static
+		"libc_ndk",                      // ruperts@, cc_library_static, depends on //bionic/libm:libm
+		"libc_nopthread",                // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
+		"libc_common",                   // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
+		"libc_static_dispatch",          // ruperts@, cc_library_static
+		"libc_dynamic_dispatch",         // ruperts@, cc_library_static
+		"libc_common_static",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+		"libc_common_shared",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+		"libc_unwind_static",            // ruperts@, cc_library_static
+		"libc_nomalloc",                 // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+		"libasync_safe",                 // ruperts@, cc_library_static
+		"libc_malloc_debug_backtrace",   // ruperts@, cc_library_static, depends on //system/libbase
+		"libsystemproperties",           // ruperts@, cc_library_static, depends on //system/core/property_service/libpropertyinfoparser
+		"libdl_static",                  // ruperts@, cc_library_static
+		"liblinker_main",                // ruperts@, cc_library_static, depends on //system/libbase
+		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
+		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
+		"libbionic_tests_headers_posix", // ruperts@, cc_library_static
+		"libc_dns",                      // ruperts@, cc_library_static
+		"generated_android_ids",         // cparsons@, genrule
+		"note_memtag_heap_async",        // cparsons@, cc_library_static
+		"note_memtag_heap_sync",         // cparsons@, cc_library_static
 	}
+
+	// Used for quicker lookups
+	bp2buildModuleDoNotConvert = map[string]bool{}
 )
 
+func init() {
+	for _, moduleName := range bp2buildModuleDoNotConvertList {
+		bp2buildModuleDoNotConvert[moduleName] = true
+	}
+}
+
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
 func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
 	if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 0595d68..abc793f 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,6 +27,7 @@
 	"sync"
 
 	"android/soong/bazel/cquery"
+
 	"github.com/google/blueprint/bootstrap"
 
 	"android/soong/bazel"
@@ -37,7 +38,6 @@
 
 const (
 	getAllFiles CqueryRequestType = iota
-	getCcObjectFiles
 	getAllFilesAndCcObjectFiles
 )
 
@@ -56,10 +56,6 @@
 	// Returns result files built by building the given bazel target label.
 	GetOutputFiles(label string, archType ArchType) ([]string, bool)
 
-	// Returns object files produced by compiling the given cc-related target.
-	// Retrieves these files from Bazel's CcInfo provider.
-	GetCcObjectFiles(label string, archType ArchType) ([]string, bool)
-
 	// TODO(cparsons): Other cquery-related methods should be added here.
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
 	GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
@@ -116,11 +112,6 @@
 	return result, ok
 }
 
-func (m MockBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := m.AllFiles[label]
-	return result, ok
-}
-
 func (m MockBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
 	result, ok := m.AllFiles[label]
 	return result, result, ok
@@ -154,16 +145,6 @@
 	return ret, ok
 }
 
-func (bazelCtx *bazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
-	rawString, ok := bazelCtx.cquery(label, cquery.GetCcObjectFiles, archType)
-	var returnResult []string
-	if ok {
-		bazelOutput := strings.TrimSpace(rawString)
-		returnResult = cquery.GetCcObjectFiles.ParseResult(bazelOutput).([]string)
-	}
-	return returnResult, ok
-}
-
 func (bazelCtx *bazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
 	var outputFiles []string
 	var ccObjects []string
@@ -183,10 +164,6 @@
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
-	panic("unimplemented")
-}
-
 func (n noopBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
 	panic("unimplemented")
 }
@@ -332,8 +309,13 @@
     name = "sourceroot",
     path = "%s",
 )
+
+local_repository(
+    name = "rules_cc",
+    path = "%s/build/bazel/rules_cc",
+)
 `
-	return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+	return []byte(fmt.Sprintf(formatString, context.workspaceDir, context.workspaceDir))
 }
 
 func (context *bazelContext) mainBzlFileContents() []byte {
@@ -344,73 +326,39 @@
 # This file is generated by soong_build. Do not edit.
 #####################################################
 
-def _x86_64_transition_impl(settings, attr):
+def _config_node_transition_impl(settings, attr):
     return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86_64",
+        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_%s" % attr.arch,
     }
 
-def _x86_transition_impl(settings, attr):
-    return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86",
-    }
-
-def _arm64_transition_impl(settings, attr):
-    return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm64",
-    }
-
-def _arm_transition_impl(settings, attr):
-    return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm",
-    }
-
-x86_64_transition = transition(
-    implementation = _x86_64_transition_impl,
+_config_node_transition = transition(
+    implementation = _config_node_transition_impl,
     inputs = [],
     outputs = [
         "//command_line_option:platforms",
     ],
 )
 
-x86_transition = transition(
-    implementation = _x86_transition_impl,
-    inputs = [],
-    outputs = [
-        "//command_line_option:platforms",
-    ],
+def _passthrough_rule_impl(ctx):
+    return [DefaultInfo(files = depset(ctx.files.deps))]
+
+config_node = rule(
+    implementation = _passthrough_rule_impl,
+    attrs = {
+        "arch" : attr.string(mandatory = True),
+        "deps" : attr.label_list(cfg = _config_node_transition),
+        "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+    },
 )
 
-arm64_transition = transition(
-    implementation = _arm64_transition_impl,
-    inputs = [],
-    outputs = [
-        "//command_line_option:platforms",
-    ],
-)
-
-arm_transition = transition(
-    implementation = _arm_transition_impl,
-    inputs = [],
-    outputs = [
-        "//command_line_option:platforms",
-    ],
-)
-
-def _mixed_build_root_impl(ctx):
-    all_files = ctx.files.deps_x86_64 + ctx.files.deps_x86 + ctx.files.deps_arm64 + ctx.files.deps_arm
-    return [DefaultInfo(files = depset(all_files))]
 
 # Rule representing the root of the build, to depend on all Bazel targets that
 # are required for the build. Building this target will build the entire Bazel
 # build tree.
 mixed_build_root = rule(
-    implementation = _mixed_build_root_impl,
+    implementation = _passthrough_rule_impl,
     attrs = {
-        "deps_x86_64" : attr.label_list(cfg = x86_64_transition),
-        "deps_x86" : attr.label_list(cfg = x86_transition),
-        "deps_arm64" : attr.label_list(cfg = arm64_transition),
-        "deps_arm" : attr.label_list(cfg = arm_transition),
-        "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+        "deps" : attr.label_list(),
     },
 )
 
@@ -446,44 +394,42 @@
 	// architecture mapping.
 	formatString := `
 # This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root", "phony_root")
+load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
+
+%s
 
 mixed_build_root(name = "buildroot",
-    deps_x86_64 = [%s],
-    deps_x86 = [%s],
-    deps_arm64 = [%s],
-    deps_arm = [%s],
+    deps = [%s],
 )
 
 phony_root(name = "phonyroot",
     deps = [":buildroot"],
 )
 `
-	var deps_x86_64 []string = nil
-	var deps_x86 []string = nil
-	var deps_arm64 []string = nil
-	var deps_arm []string = nil
+	configNodeFormatString := `
+config_node(name = "%s",
+    arch = "%s",
+    deps = [%s],
+)
+`
+
+	configNodesSection := ""
+
+	labelsByArch := map[string][]string{}
 	for val, _ := range context.requests {
 		labelString := fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label))
-		switch getArchString(val) {
-		case "x86_64":
-			deps_x86_64 = append(deps_x86_64, labelString)
-		case "x86":
-			deps_x86 = append(deps_x86, labelString)
-		case "arm64":
-			deps_arm64 = append(deps_arm64, labelString)
-		case "arm":
-			deps_arm = append(deps_arm, labelString)
-		default:
-			panic(fmt.Sprintf("unhandled architecture %s for %v", getArchString(val), val))
-		}
+		archString := getArchString(val)
+		labelsByArch[archString] = append(labelsByArch[archString], labelString)
 	}
 
-	return []byte(fmt.Sprintf(formatString,
-		strings.Join(deps_x86_64, ",\n            "),
-		strings.Join(deps_x86, ",\n            "),
-		strings.Join(deps_arm64, ",\n            "),
-		strings.Join(deps_arm, ",\n            ")))
+	configNodeLabels := []string{}
+	for archString, labels := range labelsByArch {
+		configNodeLabels = append(configNodeLabels, fmt.Sprintf("\":%s\"", archString))
+		labelsString := strings.Join(labels, ",\n            ")
+		configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+	}
+
+	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(configNodeLabels, ",\n            ")))
 }
 
 func indent(original string) string {
diff --git a/android/filegroup.go b/android/filegroup.go
index abbb4d4..2f13ab8 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -30,7 +30,7 @@
 
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
-	Srcs bazel.LabelList
+	Srcs bazel.LabelListAttribute
 }
 
 type bazelFilegroup struct {
@@ -57,8 +57,10 @@
 		return
 	}
 
+	srcs := bazel.MakeLabelListAttribute(
+		BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
 	attrs := &bazelFilegroupAttributes{
-		Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs),
+		Srcs: srcs,
 	}
 
 	props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
diff --git a/android/fixture.go b/android/fixture.go
index 8d62958..5fc668a 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -26,16 +26,9 @@
 // Fixture
 // =======
 // These determine the environment within which a test can be run. Fixtures are mutable and are
-// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
-// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
-// instances to it to modify the environment.
-//
-// FixtureFactory (deprecated)
-// ===========================
-// These are responsible for creating fixtures. Factories are immutable and are intended to be
-// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
-// preparers that prepare a fixture for running a test. Factories can also be used to create other
-// factories by extending them with additional fixture preparers.
+// created and mutated by FixturePreparer instances. They are created by first creating a base
+// Fixture (which is essentially empty) and then applying FixturePreparer instances to it to modify
+// the environment.
 //
 // FixturePreparer
 // ===============
@@ -169,77 +162,6 @@
 //    PrepareForApex,
 // )
 //
-// // FixtureFactory instances have been deprecated, this remains for informational purposes to
-// // help explain some of the existing code but will be removed along with FixtureFactory.
-//
-// var javaFixtureFactory = android.NewFixtureFactory(
-//    PrepareForIntegrationTestWithJava,
-//    FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-//      ctx.RegisterModuleType("test_module", testModule)
-//    }),
-//    javaMockFS.AddToFixture(),
-//    ...
-// }
-//
-// func TestJavaStuff(t *testing.T) {
-//   result := javaFixtureFactory.RunTest(t,
-//       android.FixtureWithRootAndroidBp(`java_library {....}`),
-//       android.MockFS{...}.AddToFixture(),
-//   )
-//   ... test result ...
-// }
-//
-// package cc
-// var PrepareForTestWithCC = GroupFixturePreparers(
-//    android.PrepareForArchMutator,
-//    android.prepareForPrebuilts,
-//    FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
-//    ...
-// )
-//
-// package apex
-//
-// var PrepareForApex = GroupFixturePreparers(
-//    ...
-// )
-//
-// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
-// android.PrepareForArchMutator) will be automatically deduped.
-//
-// var apexFixtureFactory = android.NewFixtureFactory(
-//    PrepareForJava,
-//    PrepareForCC,
-//    PrepareForApex,
-// )
-
-// Factory for Fixture objects.
-//
-// This is configured with a set of FixturePreparer objects that are used to
-// initialize each Fixture instance this creates.
-//
-// deprecated: Use FixturePreparer instead.
-type FixtureFactory interface {
-	FixturePreparer
-}
-
-// Create a new FixtureFactory that will apply the supplied preparers.
-//
-// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
-// the package level setUp method. It has to be a pointer to the variable as the variable will not
-// have been initialized at the time the factory is created. If it is nil then a test specific
-// temporary directory will be created instead.
-//
-// deprecated: The functionality provided by FixtureFactory will be merged into FixturePreparer
-func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
-	f := &fixtureFactory{
-		buildDirSupplier: buildDirSupplier,
-		compositeFixturePreparer: compositeFixturePreparer{
-			preparers: dedupAndFlattenPreparers(nil, preparers),
-		},
-	}
-	f.initBaseFixturePreparer(f)
-	return f
-}
 
 // A set of mock files to add to the mock file system.
 type MockFS map[string][]byte
@@ -445,17 +367,8 @@
 	// Return the flattened and deduped list of simpleFixturePreparer pointers.
 	list() []*simpleFixturePreparer
 
-	// Creates a copy of this instance and adds some additional preparers.
-	//
-	// Before the preparers are used they are combined with the preparers provided when the factory
-	// was created, any groups of preparers are flattened, and the list is deduped so that each
-	// preparer is only used once. See the file documentation in android/fixture.go for more details.
-	//
-	// deprecated: Use GroupFixturePreparers() instead.
-	Extend(preparers ...FixturePreparer) FixturePreparer
-
 	// Create a Fixture.
-	Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
+	Fixture(t *testing.T) Fixture
 
 	// ExtendWithErrorHandler creates a new FixturePreparer that will use the supplied error handler
 	// to check the errors (may be 0) reported by the test.
@@ -466,12 +379,13 @@
 
 	// 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
+	// Shorthand for Fixture(t).RunTest()
+	RunTest(t *testing.T) *TestResult
 
 	// Run the test with the supplied Android.bp file.
 	//
-	// Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
+	// preparer.RunTestWithBp(t, bp) is shorthand for
+	// android.GroupFixturePreparers(preparer, android.FixtureWithRootAndroidBp(bp)).RunTest(t)
 	RunTestWithBp(t *testing.T, bp string) *TestResult
 
 	// RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
@@ -705,13 +619,11 @@
 	NinjaDeps []string
 }
 
-func createFixture(t *testing.T, buildDir string, base []*simpleFixturePreparer, extra []FixturePreparer) Fixture {
-	all := dedupAndFlattenPreparers(base, extra)
-
+func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture {
 	config := TestConfig(buildDir, nil, "", nil)
 	ctx := NewTestContext(config)
 	fixture := &fixture{
-		preparers: all,
+		preparers: preparers,
 		t:         t,
 		config:    config,
 		ctx:       ctx,
@@ -720,7 +632,7 @@
 		errorHandler: FixtureExpectsNoErrors,
 	}
 
-	for _, preparer := range all {
+	for _, preparer := range preparers {
 		preparer.function(fixture)
 	}
 
@@ -735,30 +647,25 @@
 	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) Fixture(t *testing.T) Fixture {
+	return createFixture(t, t.TempDir(), b.self.list())
 }
 
 func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer {
-	return b.self.Extend(newSimpleFixturePreparer(func(fixture *fixture) {
+	return GroupFixturePreparers(b.self, newSimpleFixturePreparer(func(fixture *fixture) {
 		fixture.errorHandler = errorHandler
 	}))
 }
 
-func (b *baseFixturePreparer) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
+func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
 	t.Helper()
-	fixture := b.self.Fixture(t, preparers...)
+	fixture := b.self.Fixture(t)
 	return fixture.RunTest()
 }
 
 func (b *baseFixturePreparer) RunTestWithBp(t *testing.T, bp string) *TestResult {
 	t.Helper()
-	return b.RunTest(t, FixtureWithRootAndroidBp(bp))
+	return GroupFixturePreparers(b.self, FixtureWithRootAndroidBp(bp)).RunTest(t)
 }
 
 func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *TestResult {
@@ -783,46 +690,6 @@
 	return fixture.RunTest()
 }
 
-var _ FixtureFactory = (*fixtureFactory)(nil)
-
-type fixtureFactory struct {
-	compositeFixturePreparer
-
-	buildDirSupplier *string
-}
-
-// Override to preserve the buildDirSupplier.
-func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixturePreparer {
-	// If there is no buildDirSupplier then just use the default implementation.
-	if f.buildDirSupplier == nil {
-		return f.baseFixturePreparer.Extend(preparers...)
-	}
-
-	all := dedupAndFlattenPreparers(f.preparers, preparers)
-
-	// Create a new factory which uses the same buildDirSupplier as the previous one.
-	extendedFactory := &fixtureFactory{
-		buildDirSupplier: f.buildDirSupplier,
-		compositeFixturePreparer: compositeFixturePreparer{
-			preparers: all,
-		},
-	}
-	extendedFactory.initBaseFixturePreparer(extendedFactory)
-	return extendedFactory
-}
-
-func (f *fixtureFactory) Fixture(t *testing.T, 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 preparers used to create this fixture.
 	preparers []*simpleFixturePreparer
@@ -937,10 +804,10 @@
 // that produced this result.
 //
 // e.g. assuming that this result was created by running:
-//     factory.Extend(preparer1, preparer2).RunTest(t, preparer3, preparer4)
+//     GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t)
 //
 // Then this method will be equivalent to running:
-//     GroupFixturePreparers(preparer1, preparer2, preparer3, preparer4)
+//     GroupFixturePreparers(preparer1, preparer2, preparer3)
 //
 // This is intended for use by tests whose output is Android.bp files to verify that those files
 // are valid, e.g. tests of the snapshots produced by the sdk module type.
diff --git a/android/fixture_test.go b/android/fixture_test.go
index 681a034..5b810e0 100644
--- a/android/fixture_test.go
+++ b/android/fixture_test.go
@@ -41,45 +41,42 @@
 
 	group := GroupFixturePreparers(preparer1, preparer2, preparer1, preparer1Then2)
 
-	extension := group.Extend(preparer4, preparer2)
+	extension := GroupFixturePreparers(group, preparer4, preparer2)
 
-	extension.Fixture(t, preparer1, preparer2, preparer2Then1, preparer3)
+	GroupFixturePreparers(extension, preparer1, preparer2, preparer2Then1, preparer3).Fixture(t)
 
 	AssertDeepEquals(t, "preparers called in wrong order",
 		[]string{"preparer1", "preparer2", "preparer4", "preparer3"}, list)
 }
 
 func TestFixtureValidateMockFS(t *testing.T) {
-	buildDir := "<unused>"
-	factory := NewFixtureFactory(&buildDir)
-
 	t.Run("absolute path", func(t *testing.T) {
 		AssertPanicMessageContains(t, "source path validation failed", "Path is outside directory: /abs/path/Android.bp", func() {
-			factory.Fixture(t, FixtureAddFile("/abs/path/Android.bp", nil))
+			FixtureAddFile("/abs/path/Android.bp", nil).Fixture(t)
 		})
 	})
 	t.Run("not canonical", func(t *testing.T) {
 		AssertPanicMessageContains(t, "source path validation failed", `path "path/with/../in/it/Android.bp" is not a canonical path, use "path/in/it/Android.bp" instead`, func() {
-			factory.Fixture(t, FixtureAddFile("path/with/../in/it/Android.bp", nil))
+			FixtureAddFile("path/with/../in/it/Android.bp", nil).Fixture(t)
 		})
 	})
 	t.Run("FixtureAddFile", func(t *testing.T) {
 		AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
-			factory.Fixture(t, FixtureAddFile("out/Android.bp", nil))
+			FixtureAddFile("out/Android.bp", nil).Fixture(t)
 		})
 	})
 	t.Run("FixtureMergeMockFs", func(t *testing.T) {
 		AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
-			factory.Fixture(t, FixtureMergeMockFs(MockFS{
+			FixtureMergeMockFs(MockFS{
 				"out/Android.bp": nil,
-			}))
+			}).Fixture(t)
 		})
 	})
 	t.Run("FixtureModifyMockFS", func(t *testing.T) {
 		AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
-			factory.Fixture(t, FixtureModifyMockFS(func(fs MockFS) {
+			FixtureModifyMockFS(func(fs MockFS) {
 				fs["out/Android.bp"] = nil
-			}))
+			}).Fixture(t)
 		})
 	})
 }
diff --git a/android/mutator.go b/android/mutator.go
index 9e99bee..e25e2e8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -202,7 +202,7 @@
 	RegisterPrebuiltsPostDepsMutators,
 	RegisterVisibilityRuleEnforcer,
 	RegisterLicensesDependencyChecker,
-	RegisterNeverallowMutator,
+	registerNeverallowMutator,
 	RegisterOverridePostDepsMutators,
 }
 
@@ -539,7 +539,7 @@
 		Name: &name,
 	}
 
-	b := t.CreateModule(factory, &nameProp, attrs).(BazelTargetModule)
+	b := t.createModuleWithoutInheritance(factory, &nameProp, attrs).(BazelTargetModule)
 	b.SetBazelTargetModuleProperties(bazelProps)
 	return b
 }
@@ -608,6 +608,11 @@
 	return module
 }
 
+func (t *topDownMutatorContext) createModuleWithoutInheritance(factory ModuleFactory, props ...interface{}) Module {
+	module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), props...).(Module)
+	return module
+}
+
 func (b *bottomUpMutatorContext) MutatorName() string {
 	return b.bp.MutatorName()
 }
diff --git a/android/neverallow.go b/android/neverallow.go
index 7455e6a..a385bbc 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -42,7 +42,7 @@
 //     counts as a match
 // - it has none of the "Without" properties matched (same rules as above)
 
-func RegisterNeverallowMutator(ctx RegisterMutatorsContext) {
+func registerNeverallowMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
 }
 
@@ -661,6 +661,22 @@
 // Overrides the default neverallow rules for the supplied config.
 //
 // For testing only.
-func SetTestNeverallowRules(config Config, testRules []Rule) {
+func setTestNeverallowRules(config Config, testRules []Rule) {
 	config.Once(neverallowRulesKey, func() interface{} { return testRules })
 }
+
+// Prepares for a test by setting neverallow rules and enabling the mutator.
+//
+// If the supplied rules are nil then the default rules are used.
+func PrepareForTestWithNeverallowRules(testRules []Rule) FixturePreparer {
+	return GroupFixturePreparers(
+		FixtureModifyConfig(func(config Config) {
+			if testRules != nil {
+				setTestNeverallowRules(config, testRules)
+			}
+		}),
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.PostDepsMutators(registerNeverallowMutator)
+		}),
+	)
+}
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index de0197a..268346a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -292,7 +292,6 @@
 		ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
-		ctx.PostDepsMutators(RegisterNeverallowMutator)
 	}),
 )
 
@@ -301,12 +300,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			GroupFixturePreparers(
 				prepareForNeverAllowTest,
-				FixtureModifyConfig(func(config Config) {
-					// If the test has its own rules then use them instead of the default ones.
-					if test.rules != nil {
-						SetTestNeverallowRules(config, test.rules)
-					}
-				}),
+				PrepareForTestWithNeverallowRules(test.rules),
 				test.fs.AddToFixture(),
 			).
 				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
diff --git a/android/paths.go b/android/paths.go
index 1278961..ba1ab11 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -286,6 +286,16 @@
 	return p.path
 }
 
+// RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the
+// result of calling Path.RelativeToTop on it.
+func (p OptionalPath) RelativeToTop() OptionalPath {
+	if !p.valid {
+		return p
+	}
+	p.path = p.path.RelativeToTop()
+	return p
+}
+
 // String returns the string version of the Path, or "" if it isn't valid.
 func (p OptionalPath) String() string {
 	if p.valid {
@@ -477,6 +487,9 @@
 // already be resolved by either deps mutator or path deps mutator.
 func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
 	m, _ := ctx.GetDirectDep(dep)
+	if m == nil {
+		panic(fmt.Errorf("cannot get direct dep %s of %s", dep, ctx.Module().Name()))
+	}
 	otherLabel := bazelModuleLabel(ctx, m, tag)
 	label := bazelModuleLabel(ctx, ctx.Module(), "")
 	if samePackage(label, otherLabel) {
@@ -1642,16 +1655,10 @@
 
 // Will panic if called from outside a test environment.
 func ensureTestOnly() {
-	// Normal soong test environment
-	if InList("-test.short", os.Args) {
+	if PrefixInList(os.Args, "-test.") {
 		return
 	}
-	// IntelliJ test environment
-	if InList("-test.v", os.Args) {
-		return
-	}
-
-	panic(fmt.Errorf("Not in test\n%s", strings.Join(os.Args, "\n")))
+	panic(fmt.Errorf("Not in test. Command line:\n  %s", strings.Join(os.Args, "\n  ")))
 }
 
 func (p InstallPath) RelativeToTop() Path {
diff --git a/android/queryview.go b/android/queryview.go
index 12d14df..224652e 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -68,7 +68,7 @@
 			Command: fmt.Sprintf(
 				`rm -rf "${outDir}/"* && `+
 					`mkdir -p "${outDir}" && `+
-					`echo WORKSPACE: cat "%s" > "${outDir}/.queryview-depfile.d" && `+
+					`echo WORKSPACE: $$(cat "%s") > "${outDir}/.queryview-depfile.d" && `+
 					`BUILDER="%s" && `+
 					`echo BUILDER=$$BUILDER && `+
 					`cd "$$(dirname "$$BUILDER")" && `+
diff --git a/android/register.go b/android/register.go
index 35469d4..4c8088d 100644
--- a/android/register.go
+++ b/android/register.go
@@ -192,6 +192,15 @@
 		t.register(ctx)
 	}
 
+	if ctx.config.BazelContext.BazelEnabled() {
+		// Hydrate the configuration of bp2build-enabled module types. This is
+		// required as a signal to identify which modules should be deferred to
+		// Bazel in mixed builds, if it is enabled.
+		for t, _ := range bp2buildMutators {
+			ctx.config.bp2buildModuleTypeConfig[t] = true
+		}
+	}
+
 	mutators := collateGloballyRegisteredMutators()
 	mutators.registerAll(ctx)
 
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 9c5ca41..d2a7d8d 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -645,7 +645,7 @@
 		rspFile := "out/soong/.intermediates/foo/rsp"
 		rspFile2 := "out/soong/.intermediates/foo/rsp2"
 		module := result.ModuleForTests("foo", "")
-		check(t, module.Rule("rule").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+		check(t, module.Rule("rule"), module.Output(rspFile2),
 			"cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
 			outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
 	})
@@ -662,7 +662,7 @@
 		cmd := `rm -rf ` + outDir + `/gen && ` +
 			sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
 		module := result.ModuleForTests("foo_sbox", "")
-		check(t, module.Output("gen/foo_sbox").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+		check(t, module.Output("gen/foo_sbox"), module.Output(rspFile2),
 			cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
 	})
 	t.Run("sbox_inputs", func(t *testing.T) {
@@ -679,7 +679,7 @@
 			sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
 
 		module := result.ModuleForTests("foo_sbox_inputs", "")
-		check(t, module.Output("gen/foo_sbox_inputs").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+		check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
 			cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
 	})
 	t.Run("singleton", func(t *testing.T) {
@@ -687,7 +687,7 @@
 		rspFile := filepath.Join("out/soong/singleton/rsp")
 		rspFile2 := filepath.Join("out/soong/singleton/rsp2")
 		singleton := result.SingletonForTests("rule_builder_test")
-		check(t, singleton.Rule("rule").RelativeToTop(), singleton.Output(rspFile2).RelativeToTop(),
+		check(t, singleton.Rule("rule"), singleton.Output(rspFile2),
 			"cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
 			outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
 	})
diff --git a/android/testing.go b/android/testing.go
index 27573d5..ce27fca 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -551,6 +551,8 @@
 //   * CommandOrderOnly
 //
 // See PathRelativeToTop for more details.
+//
+// deprecated: this is no longer needed as TestingBuildParams are created in this form.
 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.
@@ -558,7 +560,7 @@
 		return p
 	}
 	if p.config.config == nil {
-		panic("cannot call RelativeToTop() on a TestingBuildParams previously returned by RelativeToTop()")
+		return p
 	}
 	// Take a copy of the build params and replace any args that contains test specific temporary
 	// paths with paths relative to the top.
@@ -670,7 +672,7 @@
 		config:      b.config,
 		BuildParams: bparams,
 		RuleParams:  b.provider.RuleParamsForTests()[bparams.Rule],
-	}
+	}.RelativeToTop()
 }
 
 func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
@@ -816,6 +818,22 @@
 	return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
 }
 
+// OutputFiles calls OutputFileProducer.OutputFiles on the encapsulated module, exits the test
+// immediately if there is an error and otherwise returns the result of calling Paths.RelativeToTop
+// on the returned Paths.
+func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths {
+	producer, ok := m.module.(OutputFileProducer)
+	if !ok {
+		t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name())
+	}
+	paths, err := producer.OutputFiles(tag)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return paths.RelativeToTop()
+}
+
 // 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 {
@@ -1040,3 +1058,20 @@
 	}
 	return result
 }
+
+// StringRelativeToTop 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 StringRelativeToTop(config Config, command string) string {
+	return normalizeStringRelativeToTop(config, command)
+}
+
+// StringsRelativeToTop will return a new slice such that each item in the new slice is the result
+// of calling StringRelativeToTop on the corresponding item in the input slice.
+func StringsRelativeToTop(config Config, command []string) []string {
+	return normalizeStringArrayRelativeToTop(config, command)
+}
diff --git a/android/util.go b/android/util.go
index 506f8f7..a0394f6 100644
--- a/android/util.go
+++ b/android/util.go
@@ -193,6 +193,17 @@
 	return
 }
 
+// FilterListPred returns the elements of the given list for which the predicate
+// returns true. Order is kept.
+func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
+	for _, l := range list {
+		if pred(l) {
+			filtered = append(filtered, l)
+		}
+	}
+	return
+}
+
 // RemoveListFromList removes the strings belonging to the filter list from the
 // given list and returns the result
 func RemoveListFromList(list []string, filter_out []string) (result []string) {
diff --git a/android/util_test.go b/android/util_test.go
index fa26c77..09bec01 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"reflect"
 	"strconv"
+	"strings"
 	"testing"
 )
 
@@ -299,6 +300,14 @@
 	}
 }
 
+func TestFilterListPred(t *testing.T) {
+	pred := func(s string) bool { return strings.HasPrefix(s, "a/") }
+	AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "b/a", "a/b"}, pred), []string{"a/c", "a/b"})
+	AssertArrayString(t, "filter", FilterListPred([]string{"b/c", "a/a", "b/b"}, pred), []string{"a/a"})
+	AssertArrayString(t, "filter", FilterListPred([]string{"c/c", "b/a", "c/b"}, pred), []string{})
+	AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "a/a", "a/b"}, pred), []string{"a/c", "a/a", "a/b"})
+}
+
 func TestRemoveListFromList(t *testing.T) {
 	input := []string{"a", "b", "c", "d", "a", "c", "d"}
 	filter := []string{"a", "c"}
diff --git a/android/variable.go b/android/variable.go
index 2ab51c7..dff48c2 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -448,6 +448,63 @@
 	}
 }
 
+// ProductConfigContext requires the access to the Module to get product config properties.
+type ProductConfigContext interface {
+	Module() Module
+}
+
+// ProductConfigProperty contains the information for a single property (may be a struct) paired
+// with the appropriate ProductConfigVariable.
+type ProductConfigProperty struct {
+	ProductConfigVariable string
+	Property              interface{}
+}
+
+// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
+// all it all product variable-specific versions of a property are easily accessed together
+type ProductConfigProperties map[string][]ProductConfigProperty
+
+// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
+// have been set for the module in the given context.
+func ProductVariableProperties(ctx ProductConfigContext) ProductConfigProperties {
+	module := ctx.Module()
+	moduleBase := module.base()
+
+	productConfigProperties := ProductConfigProperties{}
+
+	if moduleBase.variableProperties == nil {
+		return productConfigProperties
+	}
+
+	variableValues := reflect.ValueOf(moduleBase.variableProperties).Elem().FieldByName("Product_variables")
+	for i := 0; i < variableValues.NumField(); i++ {
+		variableValue := variableValues.Field(i)
+		// Check if any properties were set for the module
+		if variableValue.IsZero() {
+			continue
+		}
+		// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
+		productVariableName := variableValues.Type().Field(i).Name
+		for j := 0; j < variableValue.NumField(); j++ {
+			property := variableValue.Field(j)
+			// If the property wasn't set, no need to pass it along
+			if property.IsZero() {
+				continue
+			}
+
+			// e.g. Asflags, Cflags, Enabled, etc.
+			propertyName := variableValue.Type().Field(j).Name
+			productConfigProperties[propertyName] = append(productConfigProperties[propertyName],
+				ProductConfigProperty{
+					ProductConfigVariable: productVariableName,
+					Property:              property.Interface(),
+				})
+		}
+	}
+
+	return productConfigProperties
+}
+
 func VariableMutator(mctx BottomUpMutatorContext) {
 	var module Module
 	var ok bool
diff --git a/apex/apex.go b/apex/apex.go
index 80d9615..9d06e1c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1702,6 +1702,9 @@
 							filesInfo = append(filesInfo, af)
 						}
 					}
+
+					// Track transitive dependencies.
+					return true
 				}
 			case javaLibTag:
 				switch child.(type) {
@@ -1910,6 +1913,21 @@
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
+				} else if java.IsbootImageContentDepTag(depTag) {
+					// Add the contents of the boot image to the apex.
+					switch child.(type) {
+					case *java.Library, *java.SdkLibrary:
+						af := apexFileForJavaModule(ctx, child.(javaModule))
+						if !af.ok() {
+							ctx.PropertyErrorf("boot_images", "boot image content %q is not configured to be compiled into dex", depName)
+							return false
+						}
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
+					default:
+						ctx.PropertyErrorf("boot_images", "boot image content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+					}
+
 				} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
 					// nothing
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index bcbbb28..0caad13 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -16,13 +16,13 @@
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
 	"reflect"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -38,8 +38,6 @@
 	"android/soong/sh"
 )
 
-var buildDir string
-
 // names returns name list from white space separated string
 func names(s string) (ns []string) {
 	for _, n := range strings.Split(s, " ") {
@@ -52,18 +50,28 @@
 
 func testApexError(t *testing.T, pattern, bp string, preparers ...android.FixturePreparer) {
 	t.Helper()
-	apexFixtureFactory.Extend(preparers...).
+	android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.GroupFixturePreparers(preparers...),
+	).
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithBp(t, bp)
 }
 
 func testApex(t *testing.T, bp string, preparers ...android.FixturePreparer) *android.TestContext {
 	t.Helper()
-	factory := apexFixtureFactory.Extend(preparers...)
+
+	optionalBpPreparer := android.NullFixturePreparer
 	if bp != "" {
-		factory = factory.Extend(android.FixtureWithRootAndroidBp(bp))
+		optionalBpPreparer = android.FixtureWithRootAndroidBp(bp)
 	}
-	result := factory.RunTest(t)
+
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.GroupFixturePreparers(preparers...),
+		optionalBpPreparer,
+	).RunTest(t)
+
 	return result.TestContext
 }
 
@@ -117,8 +125,16 @@
 	},
 )
 
-var apexFixtureFactory = android.NewFixtureFactory(
-	&buildDir,
+// Legacy preparer used for running tests within the apex package.
+//
+// This includes everything that was needed to run any test in the apex package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testApex...() methods should avoid using this and instead use the
+// various preparers directly, using android.GroupFixturePreparers(...) to group them when
+// necessary.
+//
+// deprecated
+var prepareForApexTest = android.GroupFixturePreparers(
 	// General preparers in alphabetical order as test infrastructure will enforce correct
 	// registration order.
 	android.PrepareForTestWithAndroidBuildComponents,
@@ -208,18 +224,6 @@
 	"system/sepolicy/apex/myapex-file_contexts": nil,
 })
 
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_apex_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	_ = os.RemoveAll(buildDir)
-}
-
 // ensure that 'result' equals 'expected'
 func ensureEquals(t *testing.T, result string, expected string) {
 	t.Helper()
@@ -515,7 +519,7 @@
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
 	// Ensure that the NOTICE output is being packaged as an asset.
-	ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex_image/NOTICE")
+	ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex_image/NOTICE")
 
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -816,7 +820,7 @@
 	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stubs for mylib2
-	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3/mylib2.so")
+	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
 	// ... and not linking to the non-stub (impl) variant of mylib2
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
@@ -1294,15 +1298,15 @@
 			name:          "unspecified version links to the latest",
 			minSdkVersion: "",
 			apexVariant:   "apex10000",
-			shouldLink:    "30",
-			shouldNotLink: []string{"29"},
+			shouldLink:    "current",
+			shouldNotLink: []string{"29", "30"},
 		},
 		{
 			name:          "always use the latest",
 			minSdkVersion: "min_sdk_version: \"29\",",
 			apexVariant:   "apex29",
-			shouldLink:    "30",
-			shouldNotLink: []string{"29"},
+			shouldLink:    "current",
+			shouldNotLink: []string{"29", "30"},
 		},
 	}
 	for _, tc := range testcases {
@@ -1369,7 +1373,11 @@
 			}
 
 			mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
-			ensureContains(t, mylibCFlags, "__LIBBAR_API__="+tc.shouldLink)
+			ver := tc.shouldLink
+			if tc.shouldLink == "current" {
+				ver = strconv.Itoa(android.FutureApiLevelInt)
+			}
+			ensureContains(t, mylibCFlags, "__LIBBAR_API__="+ver)
 		})
 	}
 }
@@ -1431,12 +1439,12 @@
 
 	// For dependency to libc
 	// Ensure that mylib is linking with the latest version of stubs
-	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29/libc.so")
+	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_current/libc.so")
 	// ... and not linking to the non-stub (impl) variant
 	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared/libc.so")
 	// ... Cflags from stub is correctly exported to mylib
-	ensureContains(t, mylibCFlags, "__LIBC_API__=29")
-	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")
+	ensureContains(t, mylibCFlags, "__LIBC_API__=10000")
+	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=10000")
 
 	// For dependency to libm
 	// Ensure that mylib is linking with the non-stub (impl) variant
@@ -1542,12 +1550,14 @@
 	}
 	// platform liba is linked to non-stub version
 	expectLink("liba", "shared", "libz", "shared")
-	// liba in myapex is linked to #30
-	expectLink("liba", "shared_apex29", "libz", "shared_30")
+	// liba in myapex is linked to current
+	expectLink("liba", "shared_apex29", "libz", "shared_current")
+	expectNoLink("liba", "shared_apex29", "libz", "shared_30")
 	expectNoLink("liba", "shared_apex29", "libz", "shared_28")
 	expectNoLink("liba", "shared_apex29", "libz", "shared")
-	// liba in otherapex is linked to #30
-	expectLink("liba", "shared_apex30", "libz", "shared_30")
+	// liba in otherapex is linked to current
+	expectLink("liba", "shared_apex30", "libz", "shared_current")
+	expectNoLink("liba", "shared_apex30", "libz", "shared_30")
 	expectNoLink("liba", "shared_apex30", "libz", "shared_28")
 	expectNoLink("liba", "shared_apex30", "libz", "shared")
 }
@@ -1598,7 +1608,8 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_apex10000", "libz", "shared_R")
+	expectLink("libx", "shared_apex10000", "libz", "shared_current")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared_R")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared_29")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
@@ -1644,8 +1655,9 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_apex10000", "libz", "shared_2")
+	expectLink("libx", "shared_apex10000", "libz", "shared_current")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared_1")
+	expectNoLink("libx", "shared_apex10000", "libz", "shared_2")
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
@@ -1692,7 +1704,8 @@
 		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libz", "shared", "libx", "shared_2")
+	expectLink("libz", "shared", "libx", "shared_current")
+	expectNoLink("libz", "shared", "libx", "shared_2")
 	expectNoLink("libz", "shared", "libz", "shared_1")
 	expectNoLink("libz", "shared", "libz", "shared")
 }
@@ -1739,7 +1752,7 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_30")
+	expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_current")
 }
 
 func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
@@ -2087,7 +2100,7 @@
 			private_key: "testkey.pem",
 		}
 
-		// mylib in myapex will link to mylib2#30
+		// mylib in myapex will link to mylib2#current
 		// mylib in otherapex will link to mylib2(non-stub) in otherapex as well
 		cc_library {
 			name: "mylib",
@@ -2121,7 +2134,7 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("mylib", "shared_apex29", "mylib2", "shared_30")
+	expectLink("mylib", "shared_apex29", "mylib2", "shared_current")
 	expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30")
 }
 
@@ -2189,10 +2202,10 @@
 		}
 	`, withSAsActiveCodeNames)
 
-	// ensure libfoo is linked with "S" version of libbar stub
+	// ensure libfoo is linked with current version of libbar stub
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex10000")
 	libFlags := libfoo.Rule("ld").Args["libFlags"]
-	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_T/libbar.so")
+	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_current/libbar.so")
 }
 
 func TestFilesInSubDir(t *testing.T) {
@@ -2468,8 +2481,8 @@
 	prefix := "TARGET_"
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
-	androidMk := builder.String()
-	installPath := path.Join(buildDir, "../target/product/test_device/vendor/apex")
+	androidMk := android.StringRelativeToTop(ctx.Config(), builder.String())
+	installPath := "out/target/product/test_device/vendor/apex"
 	ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
 
 	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
@@ -2516,10 +2529,10 @@
 	ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
 	libs := names(ldRule.Args["libFlags"])
 	// VNDK libs(libvndk/libc++) as they are
-	ensureListContains(t, libs, buildDir+"/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
-	ensureListContains(t, libs, buildDir+"/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
+	ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
+	ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
 	// non-stable Vendor libs as APEX variants
-	ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+	ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
 
 	// VNDK libs are not included when use_vndk_as_stable: true
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -4092,15 +4105,15 @@
 			`)
 
 			apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-			expected := buildDir + "/target/product/test_device/" + tc.parition + "/apex"
-			actual := apex.installDir.String()
+			expected := "out/soong/target/product/test_device/" + tc.parition + "/apex"
+			actual := apex.installDir.RelativeToTop().String()
 			if actual != expected {
 				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
 			}
 
 			flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-			expected = buildDir + "/target/product/test_device/" + tc.flattenedPartition + "/apex"
-			actual = flattened.installDir.String()
+			expected = "out/soong/target/product/test_device/" + tc.flattenedPartition + "/apex"
+			actual = flattened.installDir.RelativeToTop().String()
 			if actual != expected {
 				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
 			}
@@ -4494,14 +4507,11 @@
 			if filepath.Base(output) == base {
 				foundLibfooJar = true
 				buildRule := s.Output(output)
-				actual := android.NormalizePathForTesting(buildRule.Input)
-				if actual != bootDexJarPath {
-					t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath)
-				}
+				android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
 			}
 		}
 		if !foundLibfooJar {
-			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs")
+			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().BuildDir(), s.AllOutputs()))
 		}
 	}
 
@@ -4543,8 +4553,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -4650,8 +4660,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -4717,8 +4727,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -4786,8 +4796,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
@@ -5081,7 +5091,7 @@
 	}
 	// JNI libraries including transitive deps are
 	for _, jni := range []string{"libjni", "libfoo"} {
-		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile()
+		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToTop()
 		// ... embedded inside APK (jnilibs.zip)
 		ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
 		// ... and not directly inside the APEX
@@ -5719,7 +5729,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -5770,7 +5780,7 @@
 
 	// The bar library should depend on the stubs jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -5860,7 +5870,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -5893,9 +5903,10 @@
 }
 
 func TestCompatConfig(t *testing.T) {
-	result := apexFixtureFactory.
-		Extend(java.PrepareForTestWithPlatformCompatConfig).
-		RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithPlatformCompatConfig,
+	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -6331,7 +6342,7 @@
 	)
 
 	m := ctx.ModuleForTests("myapex", "android_common")
-	extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+	extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
 
 	actual := extractedApex.Inputs
 	if len(actual) != 1 {
@@ -6438,68 +6449,46 @@
 func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
 	t.Helper()
 
-	bp += cc.GatherRequiredDepsForTest(android.Android)
-	bp += java.GatherRequiredDepsForTest()
-
-	fs := map[string][]byte{
-		"a.java":                             nil,
-		"a.jar":                              nil,
-		"build/make/target/product/security": nil,
-		"apex_manifest.json":                 nil,
-		"AndroidManifest.xml":                nil,
+	fs := android.MockFS{
+		"a.java":              nil,
+		"a.jar":               nil,
+		"apex_manifest.json":  nil,
+		"AndroidManifest.xml": nil,
 		"system/sepolicy/apex/myapex-file_contexts":                  nil,
 		"system/sepolicy/apex/some-updatable-apex-file_contexts":     nil,
 		"system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
 		"system/sepolicy/apex/com.android.art.debug-file_contexts":   nil,
 		"framework/aidl/a.aidl":                                      nil,
 	}
-	cc.GatherRequiredFilesForTest(fs)
 
-	for k, v := range filesForSdkLibrary {
-		fs[k] = v
-	}
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
-
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("apex", BundleFactory)
-	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterComponentsMutator)
-	android.RegisterPrebuiltMutators(ctx)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterHiddenApiSingletonComponents(ctx)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-
-	ctx.Register()
-
-	pathCtx := android.PathContextForTesting(config)
-	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	transformDexpreoptConfig(dexpreoptConfig)
-	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
-	// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
-	// product variables.
-	config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
-	config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
-
-	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	if errmsg == "" {
-		android.FailIfErrored(t, errs)
-	} else if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, errmsg, errs)
-	} else {
-		t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
+	errorHandler := android.FixtureExpectsNoErrors
+	if errmsg != "" {
+		errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
 	}
 
-	return ctx
+	result := android.GroupFixturePreparers(
+		cc.PrepareForTestWithCcDefaultModules,
+		java.PrepareForTestWithHiddenApiBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureModifyConfig(func(config android.Config) {
+			pathCtx := android.PathContextForTesting(config)
+			dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
+			transformDexpreoptConfig(dexpreoptConfig)
+			dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
+
+			// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
+			// product variables.
+			config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
+			config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
+		}),
+		fs.AddToFixture(),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
+
+	return result.TestContext
 }
 
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
@@ -6673,45 +6662,33 @@
 		public_key: "testkey.avbpubkey",
 		private_key: "testkey.pem",
 	}`
-	fs := map[string][]byte{
+	fs := android.MockFS{
 		"lib1/src/A.java": nil,
 		"lib2/src/B.java": nil,
 		"system/sepolicy/apex/myapex-file_contexts": nil,
 	}
 
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
-	android.SetTestNeverallowRules(config, rules)
-	updatableBootJars := make([]string, 0, len(apexBootJars))
-	for _, apexBootJar := range apexBootJars {
-		updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+	errorHandler := android.FixtureExpectsNoErrors
+	if errmsg != "" {
+		errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
 	}
-	config.TestProductVariables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
 
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("apex", BundleFactory)
-	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	java.RegisterRequiredBuildComponentsForTest(ctx)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-	ctx.PostDepsMutators(android.RegisterNeverallowMutator)
-
-	ctx.Register()
-
-	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	if errmsg == "" {
-		android.FailIfErrored(t, errs)
-	} else if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, errmsg, errs)
-		return
-	} else {
-		t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
-	}
+	android.GroupFixturePreparers(
+		android.PrepareForTestWithAndroidBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		PrepareForTestWithApexBuildComponents,
+		android.PrepareForTestWithNeverallowRules(rules),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			updatableBootJars := make([]string, 0, len(apexBootJars))
+			for _, apexBootJar := range apexBootJars {
+				updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+			}
+			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
+		}),
+		fs.AddToFixture(),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
 }
 
 func TestApexPermittedPackagesRules(t *testing.T) {
@@ -6863,21 +6840,77 @@
 		}
 	`)
 
-	// the test 'mytest' is a test for the apex, therefore is linked to the
+	ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+		ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+		mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+		android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+	}
+
+	// These modules are tests for the apex, therefore are linked to the
 	// actual implementation of mylib instead of its stub.
-	ldFlags := ctx.ModuleForTests("mytest", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
-	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+	ensureLinkedLibIs("mytest", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+	ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+	ensureLinkedLibIs("mybench", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+}
 
-	// The same should be true for cc_library
-	ldFlags = ctx.ModuleForTests("mytestlib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
-	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+func TestIndirectTestFor(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib", "myprivlib"],
+			updatable: false,
+		}
 
-	// ... and for cc_benchmark
-	ldFlags = ctx.ModuleForTests("mybench", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
-	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1"],
+			},
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "myprivlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			shared_libs: ["mylib"],
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "mytestlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			shared_libs: ["myprivlib"],
+			stl: "none",
+			test_for: ["myapex"],
+		}
+	`)
+
+	ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+		ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+		mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+		android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+	}
+
+	// The platform variant of mytestlib links to the platform variant of the
+	// internal myprivlib.
+	ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/myprivlib/", "android_arm64_armv8-a_shared/myprivlib.so")
+
+	// The platform variant of myprivlib links to the platform variant of mylib
+	// and bypasses its stubs.
+	ensureLinkedLibIs("myprivlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
 }
 
 // TODO(jungjw): Move this to proptools
@@ -6908,7 +6941,7 @@
 	m := ctx.ModuleForTests("myapex", "android_common")
 
 	// Check extract_apks tool parameters.
-	extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+	extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
 	actual := extractedApex.Args["abis"]
 	expected := "ARMEABI_V7A,ARM64_V8A"
 	if actual != expected {
@@ -7387,7 +7420,7 @@
 							t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
 						}
 						cflags := entry.mkEntries.EntryMap["LOCAL_EXPORT_CFLAGS"]
-						expected := "-D__STUBLIB_API__=1"
+						expected := "-D__STUBLIB_API__=10000"
 						if !android.InList(expected, cflags) {
 							t.Errorf("LOCAL_EXPORT_CFLAGS expected to have %q, but got %q", expected, cflags)
 						}
@@ -7399,12 +7432,5 @@
 }
 
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
+	os.Exit(m.Run())
 }
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 678a4cd..574166a 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -177,8 +177,10 @@
 			boot_images: [
 				"mybootimage",
 			],
+			// bar (like foo) should be transitively included in this apex because it is part of the
+			// mybootimage boot_image. However, it is kept here to ensure that the apex dedups the files
+			// correctly.
 			java_libs: [
-				"foo",
 				"bar",
 			],
 			updatable: false,
@@ -247,7 +249,6 @@
 	java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
 		`bar`,
 		`com.android.art.key`,
-		`foo`,
 		`mybootimage`,
 	})
 }
@@ -315,4 +316,67 @@
 	})
 }
 
+func TestBootImageContentsNoName(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		prepareForTestWithMyapex,
+	).RunTestWithBp(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			boot_images: [
+				"mybootimage",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		boot_image {
+			name: "mybootimage",
+			contents: [
+				"foo",
+				"bar",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`)
+
+	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+		// This does not include art, oat or vdex files as they are only included for the art boot
+		// image.
+		"javalib/bar.jar",
+		"javalib/foo.jar",
+	})
+
+	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+		`myapex.key`,
+		`mybootimage`,
+	})
+}
+
 // TODO(b/177892522) - add test for host apex.
diff --git a/bazel/properties.go b/bazel/properties.go
index abdc107..1763f2d 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"regexp"
 	"sort"
 )
 
@@ -31,6 +32,8 @@
 
 const BazelTargetModuleNamePrefix = "__bp2build__"
 
+var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
+
 // Label is used to represent a Bazel compatible Label. Also stores the original bp text to support
 // string replacement.
 type Label struct {
@@ -76,6 +79,92 @@
 	return uniqueLabelList
 }
 
+const (
+	ARCH_X86    = "x86"
+	ARCH_X86_64 = "x86_64"
+	ARCH_ARM    = "arm"
+	ARCH_ARM64  = "arm64"
+)
+
+var (
+	// This is the list of architectures with a Bazel config_setting and
+	// constraint value equivalent. is actually android.ArchTypeList, but the
+	// android package depends on the bazel package, so a cyclic dependency
+	// prevents using that here.
+	selectableArchs = []string{ARCH_X86, ARCH_X86_64, ARCH_ARM, ARCH_ARM64}
+)
+
+// Arch-specific label_list typed Bazel attribute values. This should correspond
+// to the types of architectures supported for compilation in arch.go.
+type labelListArchValues struct {
+	X86    LabelList
+	X86_64 LabelList
+	Arm    LabelList
+	Arm64  LabelList
+	// TODO(b/181299724): this is currently missing the "common" arch, which
+	// doesn't have an equivalent platform() definition yet.
+}
+
+// LabelListAttribute is used to represent a list of Bazel labels as an
+// attribute.
+type LabelListAttribute struct {
+	// The non-arch specific attribute label list Value. Required.
+	Value LabelList
+
+	// The arch-specific attribute label list values. Optional. If used, these
+	// are generated in a select statement and appended to the non-arch specific
+	// label list Value.
+	ArchValues labelListArchValues
+}
+
+// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
+func MakeLabelListAttribute(value LabelList) LabelListAttribute {
+	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
+}
+
+// HasArchSpecificValues returns true if the attribute contains
+// architecture-specific label_list values.
+func (attrs *LabelListAttribute) HasArchSpecificValues() bool {
+	for _, arch := range selectableArchs {
+		if len(attrs.GetValueForArch(arch).Includes) > 0 || len(attrs.GetValueForArch(arch).Excludes) > 0 {
+			return true
+		}
+	}
+	return false
+}
+
+// GetValueForArch returns the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
+	switch arch {
+	case ARCH_X86:
+		return attrs.ArchValues.X86
+	case ARCH_X86_64:
+		return attrs.ArchValues.X86_64
+	case ARCH_ARM:
+		return attrs.ArchValues.Arm
+	case ARCH_ARM64:
+		return attrs.ArchValues.Arm64
+	default:
+		panic(fmt.Errorf("Unknown arch: %s", arch))
+	}
+}
+
+// SetValueForArch sets the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
+	switch arch {
+	case "x86":
+		attrs.ArchValues.X86 = value
+	case "x86_64":
+		attrs.ArchValues.X86_64 = value
+	case "arm":
+		attrs.ArchValues.Arm = value
+	case "arm64":
+		attrs.ArchValues.Arm64 = value
+	default:
+		panic(fmt.Errorf("Unknown arch: %s", arch))
+	}
+}
+
 // StringListAttribute corresponds to the string_list Bazel attribute type with
 // support for additional metadata, like configurations.
 type StringListAttribute struct {
@@ -89,11 +178,10 @@
 // Arch-specific string_list typed Bazel attribute values. This should correspond
 // to the types of architectures supported for compilation in arch.go.
 type stringListArchValues struct {
-	X86     []string
-	X86_64  []string
-	Arm     []string
-	Arm64   []string
-	Default []string
+	X86    []string
+	X86_64 []string
+	Arm    []string
+	Arm64  []string
 	// TODO(b/181299724): this is currently missing the "common" arch, which
 	// doesn't have an equivalent platform() definition yet.
 }
@@ -101,7 +189,7 @@
 // HasArchSpecificValues returns true if the attribute contains
 // architecture-specific string_list values.
 func (attrs *StringListAttribute) HasArchSpecificValues() bool {
-	for _, arch := range []string{"x86", "x86_64", "arm", "arm64", "default"} {
+	for _, arch := range selectableArchs {
 		if len(attrs.GetValueForArch(arch)) > 0 {
 			return true
 		}
@@ -112,16 +200,14 @@
 // GetValueForArch returns the string_list attribute value for an architecture.
 func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
 	switch arch {
-	case "x86":
+	case ARCH_X86:
 		return attrs.ArchValues.X86
-	case "x86_64":
+	case ARCH_X86_64:
 		return attrs.ArchValues.X86_64
-	case "arm":
+	case ARCH_ARM:
 		return attrs.ArchValues.Arm
-	case "arm64":
+	case ARCH_ARM64:
 		return attrs.ArchValues.Arm64
-	case "default":
-		return attrs.ArchValues.Default
 	default:
 		panic(fmt.Errorf("Unknown arch: %s", arch))
 	}
@@ -130,17 +216,35 @@
 // SetValueForArch sets the string_list attribute value for an architecture.
 func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
 	switch arch {
-	case "x86":
+	case ARCH_X86:
 		attrs.ArchValues.X86 = value
-	case "x86_64":
+	case ARCH_X86_64:
 		attrs.ArchValues.X86_64 = value
-	case "arm":
+	case ARCH_ARM:
 		attrs.ArchValues.Arm = value
-	case "arm64":
+	case ARCH_ARM64:
 		attrs.ArchValues.Arm64 = value
-	case "default":
-		attrs.ArchValues.Default = value
 	default:
 		panic(fmt.Errorf("Unknown arch: %s", arch))
 	}
 }
+
+// TryVariableSubstitution, replace string substitution formatting within each string in slice with
+// Starlark string.format compatible tag for productVariable.
+func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
+	ret := make([]string, 0, len(slice))
+	changesMade := false
+	for _, s := range slice {
+		newS, changed := TryVariableSubstitution(s, productVariable)
+		ret = append(ret, newS)
+		changesMade = changesMade || changed
+	}
+	return ret, changesMade
+}
+
+// TryVariableSubstitution, replace string substitution formatting within s with Starlark
+// string.format compatible tag for productVariable.
+func TryVariableSubstitution(s string, productVariable string) (string, bool) {
+	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "{"+productVariable+"}")
+	return sub, s != sub
+}
diff --git a/bloaty/Android.bp b/bloaty/Android.bp
index b1f1e39..96cc1a5 100644
--- a/bloaty/Android.bp
+++ b/bloaty/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 bootstrap_go_package {
     name: "soong-bloaty",
     pkgPath: "android/soong/bloaty",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 9c98c76..e93b3dc 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -370,9 +370,20 @@
 		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
 		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
 		//
-		// In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
-		// value of unset attributes.
-		return "", nil
+		// In Bazel-parlance, we would use "attr.<type>(default = <default
+		// value>)" to set the default value of unset attributes. In the cases
+		// where the bp2build converter didn't set the default value within the
+		// mutator when creating the BazelTargetModule, this would be a zero
+		// value. For those cases, we return a non-surprising default value so
+		// generated BUILD files are syntactically correct.
+		switch propertyValue.Kind() {
+		case reflect.Slice:
+			return "[]", nil
+		case reflect.Map:
+			return "{}", nil
+		default:
+			return "", nil
+		}
 	}
 
 	var ret string
@@ -404,9 +415,34 @@
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
 		// by wrapping the attributes in a custom struct type.
-		if labels, ok := propertyValue.Interface().(bazel.LabelList); ok {
+		if labels, ok := propertyValue.Interface().(bazel.LabelListAttribute); ok {
 			// TODO(b/165114590): convert glob syntax
-			return prettyPrint(reflect.ValueOf(labels.Includes), indent)
+			ret, err := prettyPrint(reflect.ValueOf(labels.Value.Includes), indent)
+			if err != nil {
+				return ret, err
+			}
+
+			if !labels.HasArchSpecificValues() {
+				// Select statement not needed.
+				return ret, nil
+			}
+
+			ret += " + " + "select({\n"
+			for _, arch := range android.ArchTypeList() {
+				value := labels.GetValueForArch(arch.Name)
+				if len(value.Includes) > 0 {
+					ret += makeIndent(indent + 1)
+					list, _ := prettyPrint(reflect.ValueOf(value.Includes), indent+1)
+					ret += fmt.Sprintf("\"%s\": %s,\n", platformArchMap[arch], list)
+				}
+			}
+
+			ret += makeIndent(indent + 1)
+			ret += fmt.Sprintf("\"%s\": [],\n", "//conditions:default")
+
+			ret += makeIndent(indent)
+			ret += "})"
+			return ret, err
 		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
 			return fmt.Sprintf("%q", label.Label), nil
 		} else if stringList, ok := propertyValue.Interface().(bazel.StringListAttribute); ok {
@@ -432,8 +468,7 @@
 			}
 
 			ret += makeIndent(indent + 1)
-			list, _ := prettyPrint(reflect.ValueOf(stringList.GetValueForArch("default")), indent+1)
-			ret += fmt.Sprintf("\"%s\": %s,\n", "//conditions:default", list)
+			ret += fmt.Sprintf("\"%s\": [],\n", "//conditions:default")
 
 			ret += makeIndent(indent)
 			ret += "})"
@@ -533,6 +568,13 @@
 
 func escapeString(s string) string {
 	s = strings.ReplaceAll(s, "\\", "\\\\")
+
+	// b/184026959: Reverse the application of some common control sequences.
+	// These must be generated literally in the BUILD file.
+	s = strings.ReplaceAll(s, "\t", "\\t")
+	s = strings.ReplaceAll(s, "\n", "\\n")
+	s = strings.ReplaceAll(s, "\r", "\\r")
+
 	return strings.ReplaceAll(s, "\"", "\\\"")
 }
 
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index b9b250a..49897b3 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -241,6 +241,22 @@
     string_prop = "a",
 )`,
 		},
+		{
+			bp: `custom {
+	name: "control_characters",
+    string_list_prop: ["\t", "\n"],
+    string_prop: "a\t\n\r",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTarget: `custom(
+    name = "control_characters",
+    string_list_prop = [
+        "\t",
+        "\n",
+    ],
+    string_prop = "a\t\n\r",
+)`,
+		},
 	}
 
 	dir := "."
@@ -502,8 +518,6 @@
 			expectedBazelTargets: []string{
 				`filegroup(
     name = "fg_foo",
-    srcs = [
-    ],
 )`,
 			},
 		},
@@ -1101,8 +1115,8 @@
         "out",
     ],
     srcs = [
-        "srcs-from-3",
         "in1",
+        "srcs-from-3",
     ],
 )`,
 			description: "genrule applies properties from genrule_defaults transitively",
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1c058ba..9461739 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -74,8 +74,8 @@
     ],
     srcs = [
         "a/b/bar.h",
-        "a/b/foo.h",
         "a/b/c.c",
+        "a/b/foo.h",
     ],
 )`,
 			},
@@ -209,6 +209,34 @@
 )`,
 			},
 		},
+		{
+			description:                        "cc_object with product variable",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			blueprint: `cc_object {
+    name: "foo",
+    include_build_directory: false,
+    product_variables: {
+        platform_sdk_version: {
+            asflags: ["-DPLATFORM_SDK_VERSION=%d"],
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "foo",
+    asflags = [
+        "-DPLATFORM_SDK_VERSION={Platform_sdk_version}",
+    ],
+    copts = [
+        "-fno-addrsig",
+    ],
+)`,
+			},
+		},
 	}
 
 	dir := "."
@@ -278,9 +306,13 @@
 			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 			blueprint: `cc_object {
     name: "foo",
+    srcs: ["a.cpp"],
     arch: {
         x86: {
-            cflags: ["-fPIC"],
+            cflags: ["-fPIC"], // string list
+        },
+        arm: {
+            srcs: ["arch/arm/file.S"], // label list
         },
     },
     bazel_module: { bp2build_available: true },
@@ -295,12 +327,19 @@
         "@bazel_tools//platforms:x86_32": [
             "-fPIC",
         ],
-        "//conditions:default": [
-        ],
+        "//conditions:default": [],
     }),
     local_include_dirs = [
         ".",
     ],
+    srcs = [
+        "a.cpp",
+    ] + select({
+        "@bazel_tools//platforms:arm": [
+            "arch/arm/file.S",
+        ],
+        "//conditions:default": [],
+    }),
 )`,
 			},
 		},
@@ -311,17 +350,22 @@
 			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 			blueprint: `cc_object {
     name: "foo",
+    srcs: ["base.cpp"],
     arch: {
         x86: {
+            srcs: ["x86.cpp"],
             cflags: ["-fPIC"],
         },
         x86_64: {
+            srcs: ["x86_64.cpp"],
             cflags: ["-fPIC"],
         },
         arm: {
+            srcs: ["arm.cpp"],
             cflags: ["-Wall"],
         },
         arm64: {
+            srcs: ["arm64.cpp"],
             cflags: ["-Wall"],
         },
     },
@@ -346,12 +390,28 @@
         "@bazel_tools//platforms:x86_64": [
             "-fPIC",
         ],
-        "//conditions:default": [
-        ],
+        "//conditions:default": [],
     }),
     local_include_dirs = [
         ".",
     ],
+    srcs = [
+        "base.cpp",
+    ] + select({
+        "@bazel_tools//platforms:arm": [
+            "arm.cpp",
+        ],
+        "@bazel_tools//platforms:aarch64": [
+            "arm64.cpp",
+        ],
+        "@bazel_tools//platforms:x86_32": [
+            "x86.cpp",
+        ],
+        "@bazel_tools//platforms:x86_64": [
+            "x86_64.cpp",
+        ],
+        "//conditions:default": [],
+    }),
 )`,
 			},
 		},
diff --git a/cc/cc.go b/cc/cc.go
index f843b41..f074597 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -363,7 +363,7 @@
 	// List of APEXes that this module has private access to for testing purpose. The module
 	// can depend on libraries that are not exported by the APEXes and use private symbols
 	// from the exported libraries.
-	Test_for []string
+	Test_for []string `android:"arch_variant"`
 }
 
 type VendorProperties struct {
@@ -2631,14 +2631,31 @@
 						// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
 						// always link to non-stub variant
 						useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
-						// Another exception: if this module is bundled with an APEX, then
-						// it is linked with the non-stub variant of a module in the APEX
-						// as if this is part of the APEX.
-						testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
-						for _, apexContents := range testFor.ApexContents {
-							if apexContents.DirectlyInApex(depName) {
+						if useStubs {
+							// Another exception: if this module is a test for an APEX, then
+							// it is linked with the non-stub variant of a module in the APEX
+							// as if this is part of the APEX.
+							testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+							for _, apexContents := range testFor.ApexContents {
+								if apexContents.DirectlyInApex(depName) {
+									useStubs = false
+									break
+								}
+							}
+						}
+						if useStubs {
+							// Yet another exception: If this module and the dependency are
+							// available to the same APEXes then skip stubs between their
+							// platform variants. This complements the test_for case above,
+							// which avoids the stubs on a direct APEX library dependency, by
+							// avoiding stubs for indirect test dependencies as well.
+							//
+							// TODO(b/183882457): This doesn't work if the two libraries have
+							// only partially overlapping apex_available. For that test_for
+							// modules would need to be split into APEX variants and resolved
+							// separately for each APEX they have access to.
+							if android.AvailableToSameApexes(c, dep.(android.ApexModule)) {
 								useStubs = false
-								break
 							}
 						}
 					} else {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index f1efbff..76e75da 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -27,33 +26,11 @@
 	"android/soong/android"
 )
 
-var buildDir string
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_cc_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
-}
-
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
+	os.Exit(m.Run())
 }
 
-var ccFixtureFactory = android.NewFixtureFactory(
-	&buildDir,
+var prepareForCcTest = android.GroupFixturePreparers(
 	PrepareForTestWithCcIncludeVndk,
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.DeviceVndkVersion = StringPtr("current")
@@ -62,62 +39,62 @@
 	}),
 )
 
-// testCcWithConfig runs tests using the ccFixtureFactory
+// testCcWithConfig runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testCcWithConfig(t *testing.T, config android.Config) *android.TestContext {
 	t.Helper()
-	result := ccFixtureFactory.RunTestWithConfig(t, config)
+	result := prepareForCcTest.RunTestWithConfig(t, config)
 	return result.TestContext
 }
 
-// testCc runs tests using the ccFixtureFactory
+// testCc runs tests using the prepareForCcTest
 //
-// Do not add any new usages of this, instead use the ccFixtureFactory directly as it makes it much
+// Do not add any new usages of this, instead use the prepareForCcTest 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 ccFixtureFactory first and then in a following change add the
+// convert the test to using prepareForCcTest 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 testCc(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	result := ccFixtureFactory.RunTestWithBp(t, bp)
+	result := prepareForCcTest.RunTestWithBp(t, bp)
 	return result.TestContext
 }
 
-// testCcNoVndk runs tests using the ccFixtureFactory
+// testCcNoVndk runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testCcNoVndk(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 
 	return testCcWithConfig(t, config)
 }
 
-// testCcNoProductVndk runs tests using the ccFixtureFactory
+// testCcNoProductVndk runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testCcNoProductVndk(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 
 	return testCcWithConfig(t, config)
 }
 
-// testCcErrorWithConfig runs tests using the ccFixtureFactory
+// testCcErrorWithConfig runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
 //
@@ -125,33 +102,33 @@
 func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
 	t.Helper()
 
-	ccFixtureFactory.Extend().
+	prepareForCcTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithConfig(t, config)
 }
 
-// testCcError runs tests using the ccFixtureFactory
+// testCcError runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testCcError(t *testing.T, pattern string, bp string) {
 	t.Helper()
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	testCcErrorWithConfig(t, pattern, config)
 	return
 }
 
-// testCcErrorProductVndk runs tests using the ccFixtureFactory
+// testCcErrorProductVndk runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
 //
 // deprecated
 func testCcErrorProductVndk(t *testing.T, pattern string, bp string) {
 	t.Helper()
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -189,7 +166,10 @@
 			},
 		}`
 
-	result := ccFixtureFactory.Extend(PrepareForTestOnFuchsia).RunTestWithBp(t, bp)
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		PrepareForTestOnFuchsia,
+	).RunTestWithBp(t, bp)
 
 	rt := false
 	fb := false
@@ -225,7 +205,10 @@
 			},
 		}`
 
-	result := ccFixtureFactory.Extend(PrepareForTestOnFuchsia).RunTestWithBp(t, bp)
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		PrepareForTestOnFuchsia,
+	).RunTestWithBp(t, bp)
 	ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
 	var objs []string
 	for _, o := range ld.Inputs {
@@ -301,13 +284,9 @@
 
 func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
 	t.Helper()
-	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
-	if !ok {
-		t.Errorf("%q must have output\n", moduleName)
-		return
-	}
-	outputFiles, err := mod.OutputFiles("")
-	if err != nil || len(outputFiles) != 1 {
+	mod := ctx.ModuleForTests(moduleName, variant)
+	outputFiles := mod.OutputFiles(t, "")
+	if len(outputFiles) != 1 {
 		t.Errorf("%q must have single output\n", moduleName)
 		return
 	}
@@ -333,14 +312,17 @@
 }
 
 func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
 }
 
 func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
 }
 
 func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
 }
 
@@ -471,7 +453,7 @@
 		}
 	`
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -492,7 +474,7 @@
 
 	// Check VNDK snapshot output.
 	snapshotDir := "vndk-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 
 	vndkLibPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s",
 		"arm64", "armv8-a"))
@@ -593,7 +575,7 @@
 			name: "llndk.libraries.txt",
 			insert_vndk_version: true,
 		}`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := testCcWithConfig(t, config)
@@ -643,7 +625,7 @@
 		}
 	`
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -670,7 +652,7 @@
 		}
  `
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -721,7 +703,7 @@
 		}
  `
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -1346,7 +1328,7 @@
 			nocrt: true,
 		}
 	`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -1791,7 +1773,7 @@
 			nocrt: true,
 		}
 	`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -2118,7 +2100,7 @@
 		}
 	`
 
-	ctx := ccFixtureFactory.RunTestWithBp(t, bp).TestContext
+	ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
 
 	checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
 	checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
@@ -2346,7 +2328,7 @@
 		}
 	`
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	// native:vndk
@@ -2552,7 +2534,7 @@
 func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
 	for _, moduleName := range moduleNames {
 		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
-		output := module.outputFile.Path()
+		output := module.outputFile.Path().RelativeToTop()
 		paths = append(paths, output)
 	}
 	return paths
@@ -2583,7 +2565,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
 	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -2617,7 +2600,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
 	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -2688,9 +2672,11 @@
 	expected := []string{
 		"android_vendor.VER_arm64_armv8-a_shared_1",
 		"android_vendor.VER_arm64_armv8-a_shared_2",
+		"android_vendor.VER_arm64_armv8-a_shared_current",
 		"android_vendor.VER_arm64_armv8-a_shared",
 		"android_vendor.VER_arm_armv7-a-neon_shared_1",
 		"android_vendor.VER_arm_armv7-a-neon_shared_2",
+		"android_vendor.VER_arm_armv7-a-neon_shared_current",
 		"android_vendor.VER_arm_armv7-a-neon_shared",
 	}
 	checkEquals(t, "variants for llndk stubs", expected, actual)
@@ -3136,7 +3122,7 @@
 		}
  `
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -3190,10 +3176,12 @@
 		"android_arm64_armv8-a_shared_1",
 		"android_arm64_armv8-a_shared_2",
 		"android_arm64_armv8-a_shared_3",
+		"android_arm64_armv8-a_shared_current",
 		"android_arm_armv7-a-neon_shared",
 		"android_arm_armv7-a-neon_shared_1",
 		"android_arm_armv7-a-neon_shared_2",
 		"android_arm_armv7-a-neon_shared_3",
+		"android_arm_armv7-a-neon_shared_current",
 	}
 	variantsMismatch := false
 	if len(variants) != len(expectedVariants) {
@@ -3452,7 +3440,8 @@
 		}
 	`
 
-	result := ccFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
 		android.PrepareForTestWithVariables,
 
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -3479,7 +3468,8 @@
 		}
 	`
 
-	result := ccFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
 		android.PrepareForTestWithAllowMissingDependencies,
 	).RunTestWithBp(t, bp)
 
@@ -3531,7 +3521,7 @@
 		}
 	`
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	ctx := testCcWithConfig(t, config)
 
 	hostBin := ctx.ModuleForTests("bin", config.BuildOSTarget.String()).Description("install")
@@ -3822,7 +3812,10 @@
 func TestSanitizeMemtagHeap(t *testing.T) {
 	variant := "android_arm64_armv8-a"
 
-	result := ccFixtureFactory.Extend(prepareForTestWithMemtagHeap).RunTest(t)
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForTestWithMemtagHeap,
+	).RunTest(t)
 	ctx := result.TestContext
 
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
@@ -3877,7 +3870,8 @@
 func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
 	variant := "android_arm64_armv8-a"
 
-	result := ccFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
 		prepareForTestWithMemtagHeap,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.SanitizeDevice = []string{"memtag_heap"}
@@ -3937,7 +3931,8 @@
 func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
 	variant := "android_arm64_armv8-a"
 
-	result := ccFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
 		prepareForTestWithMemtagHeap,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.SanitizeDevice = []string{"memtag_heap"}
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 41ef95c..40a5716 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -36,8 +36,10 @@
 		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
-			t.Errorf("missing aidl includes in global flags")
+		expected := "-I" + filepath.Dir(aidl.Output.String())
+		actual := android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)
+		if !inList(expected, actual) {
+			t.Errorf("missing aidl includes in global flags, expected %q, actual %q", expected, actual)
 		}
 	})
 
@@ -61,7 +63,7 @@
 		aidlManifest := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
+		if !inList("-I"+filepath.Dir(aidl.Output.String()), android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)) {
 			t.Errorf("missing aidl includes in global flags")
 		}
 
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index fa0c6f2..45b343b 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -52,7 +52,7 @@
 					},
 				}
 			`
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
+	config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 
 	ctx := testGenruleContext(config)
 
diff --git a/cc/library.go b/cc/library.go
index 9bec974..091acfe 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -65,7 +65,8 @@
 		// symbols that are exported for stubs variant of this library.
 		Symbol_file *string `android:"path"`
 
-		// List versions to generate stubs libs for.
+		// List versions to generate stubs libs for. The version name "current" is always
+		// implicitly added.
 		Versions []string
 	}
 
@@ -171,6 +172,8 @@
 
 	// This variant is a stubs lib
 	BuildStubs bool `blueprint:"mutated"`
+	// This variant is the latest version
+	IsLatestVersion bool `blueprint:"mutated"`
 	// Version of the stubs lib
 	StubsVersion string `blueprint:"mutated"`
 	// List of all stubs versions associated with an implementation lib
@@ -504,12 +507,11 @@
 			return
 		}
 		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
-		j := 0
-		for i, header := range glob {
+		for _, header := range glob {
 			if isLibcxx {
 				// Glob all files under this special directory, because of C++ headers with no
 				// extension.
-				if !strings.HasSuffix(header, "/") {
+				if strings.HasSuffix(header, "/") {
 					continue
 				}
 			} else {
@@ -525,12 +527,8 @@
 					continue
 				}
 			}
-			if i != j {
-				glob[j] = glob[i]
-			}
-			j++
+			ret = append(ret, android.PathForSource(ctx, header))
 		}
-		glob = glob[:j]
 	}
 
 	// Collect generated headers
@@ -780,7 +778,7 @@
 
 type versionedInterface interface {
 	buildStubs() bool
-	setBuildStubs()
+	setBuildStubs(isLatest bool)
 	hasStubsVariants() bool
 	setStubsVersion(string)
 	stubsVersion() string
@@ -1498,7 +1496,7 @@
 			if ctx.isVndk() && !ctx.IsVndkExt() {
 				return
 			}
-		} else if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.directlyInAnyApex() {
+		} else if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
@@ -1614,11 +1612,29 @@
 }
 
 func (library *libraryDecorator) hasStubsVariants() bool {
-	return len(library.Properties.Stubs.Versions) > 0
+	// Just having stubs.symbol_file is enough to create a stub variant. In that case
+	// the stub for the future API level is created.
+	return library.Properties.Stubs.Symbol_file != nil ||
+		len(library.Properties.Stubs.Versions) > 0
 }
 
 func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	return library.Properties.Stubs.Versions
+	if !library.hasStubsVariants() {
+		return nil
+	}
+
+	// Future API level is implicitly added if there isn't
+	vers := library.Properties.Stubs.Versions
+	if inList(android.FutureApiLevel.String(), vers) {
+		return vers
+	}
+	// In some cases, people use the raw value "10000" in the versions property.
+	// We shouldn't add the future API level in that case, otherwise there will
+	// be two identical versions.
+	if inList(strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt()), vers) {
+		return vers
+	}
+	return append(vers, android.FutureApiLevel.String())
 }
 
 func (library *libraryDecorator) setStubsVersion(version string) {
@@ -1629,8 +1645,9 @@
 	return library.MutatedProperties.StubsVersion
 }
 
-func (library *libraryDecorator) setBuildStubs() {
+func (library *libraryDecorator) setBuildStubs(isLatest bool) {
 	library.MutatedProperties.BuildStubs = true
+	library.MutatedProperties.IsLatestVersion = isLatest
 }
 
 func (library *libraryDecorator) setAllStubsVersions(versions []string) {
@@ -1642,8 +1659,7 @@
 }
 
 func (library *libraryDecorator) isLatestStubVersion() bool {
-	versions := library.Properties.Stubs.Versions
-	return versions[len(versions)-1] == library.stubsVersion()
+	return library.MutatedProperties.IsLatestVersion
 }
 
 func (library *libraryDecorator) availableFor(what string) bool {
@@ -1886,7 +1902,8 @@
 			c.stl = nil
 			c.Properties.PreventInstall = true
 			lib := moduleLibraryInterface(m)
-			lib.setBuildStubs()
+			isLatest := i == (len(versions) - 1)
+			lib.setBuildStubs(isLatest)
 
 			if variants[i] != "" {
 				// A non-LLNDK stubs module is hidden from make and has a dependency from the
@@ -2029,7 +2046,7 @@
 	return outputFile
 }
 
-func Bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelList {
+func Bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelListAttribute {
 	var headerLibs []string
 	for _, linkerProps := range module.linker.linkerProps() {
 		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
@@ -2038,12 +2055,11 @@
 			break
 		}
 	}
-
-	headerLibsLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+	headerLibsLabels := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, headerLibs))
 	return headerLibsLabels
 }
 
-func Bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelList, bazel.LabelList) {
+func Bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelListAttribute, bazel.LabelListAttribute) {
 	libraryDecorator := module.linker.(*libraryDecorator)
 
 	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
@@ -2059,17 +2075,16 @@
 	}
 
 	headersLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
-
-	return includeDirsLabels, headersLabels
+	return bazel.MakeLabelListAttribute(includeDirsLabels), bazel.MakeLabelListAttribute(headersLabels)
 }
 
 type bazelCcLibraryStaticAttributes struct {
 	Copts      []string
-	Srcs       bazel.LabelList
-	Deps       bazel.LabelList
+	Srcs       bazel.LabelListAttribute
+	Deps       bazel.LabelListAttribute
 	Linkstatic bool
-	Includes   bazel.LabelList
-	Hdrs       bazel.LabelList
+	Includes   bazel.LabelListAttribute
+	Hdrs       bazel.LabelListAttribute
 }
 
 type bazelCcLibraryStatic struct {
@@ -2110,7 +2125,7 @@
 			break
 		}
 	}
-	srcsLabels := android.BazelLabelForModuleSrc(ctx, srcs)
+	srcsLabels := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, srcs))
 
 	var staticLibs []string
 	var wholeStaticLibs []string
@@ -2135,18 +2150,18 @@
 	includesLabels := android.BazelLabelForModuleSrc(ctx, allIncludes)
 
 	exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
-	includesLabels.Append(exportedIncludesLabels)
+	includesLabels.Append(exportedIncludesLabels.Value)
 
 	headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
-	depsLabels.Append(headerLibsLabels)
+	depsLabels.Append(headerLibsLabels.Value)
 
 	attrs := &bazelCcLibraryStaticAttributes{
 		Copts:      copts,
-		Srcs:       bazel.UniqueBazelLabelList(srcsLabels),
-		Deps:       bazel.UniqueBazelLabelList(depsLabels),
+		Srcs:       srcsLabels,
+		Deps:       bazel.MakeLabelListAttribute(depsLabels),
 		Linkstatic: true,
-		Includes:   bazel.UniqueBazelLabelList(includesLabels),
-		Hdrs:       bazel.UniqueBazelLabelList(exportedIncludesHeadersLabels),
+		Includes:   bazel.MakeLabelListAttribute(includesLabels),
+		Hdrs:       exportedIncludesHeadersLabels,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 719d538..8286848 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -62,9 +62,9 @@
 }
 
 type bazelCcLibraryHeadersAttributes struct {
-	Hdrs     bazel.LabelList
-	Includes bazel.LabelList
-	Deps     bazel.LabelList
+	Hdrs     bazel.LabelListAttribute
+	Includes bazel.LabelListAttribute
+	Deps     bazel.LabelListAttribute
 }
 
 type bazelCcLibraryHeaders struct {
diff --git a/cc/library_test.go b/cc/library_test.go
index 49838b4..7975275 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -199,7 +199,7 @@
 			},
 		}
 	`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
 	ctx := testCcWithConfig(t, config)
 
@@ -222,7 +222,7 @@
 			},
 		}
 	`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
 	testCcErrorWithConfig(t, `"libfoo" .*: versions: not sorted`, config)
 }
diff --git a/cc/linker.go b/cc/linker.go
index 6d0d416..21281d2 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -599,21 +599,20 @@
 	_                   = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
 	genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
 		blueprint.RuleParams{
-			Command:     "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
-			CommandDeps: []string{"$genSortedBssSymbolsPath", "${crossCompile}nm"},
+			Command:     "CLANG_BIN=${clangBin} $genSortedBssSymbolsPath ${in} ${out}",
+			CommandDeps: []string{"$genSortedBssSymbolsPath", "${clangBin}/llvm-nm"},
 		},
-		"crossCompile")
+		"clangBin")
 )
 
 func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
-	crossCompile := gccCmd(flags.toolchain, "")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        genSortedBssSymbols,
 		Description: "generate bss symbol order " + symbolOrderingFile.Base(),
 		Output:      symbolOrderingFile,
 		Input:       in,
 		Args: map[string]string{
-			"crossCompile": crossCompile,
+			"clangBin": "${config.ClangBin}",
 		},
 	})
 	return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
diff --git a/cc/object.go b/cc/object.go
index 664be8d..ea8d7d3 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -53,8 +53,17 @@
 }
 
 func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
-	// TODO(b/181794963): restore mixed builds once cc_object incompatibility resolved
-	return false
+	bazelCtx := ctx.Config().BazelContext
+	objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+	if ok {
+		if len(objPaths) != 1 {
+			ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
+			return false
+		}
+
+		handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
+	}
+	return ok
 }
 
 type ObjectLinkerProperties struct {
@@ -103,9 +112,10 @@
 
 // For bp2build conversion.
 type bazelObjectAttributes struct {
-	Srcs               bazel.LabelList
-	Deps               bazel.LabelList
+	Srcs               bazel.LabelListAttribute
+	Deps               bazel.LabelListAttribute
 	Copts              bazel.StringListAttribute
+	Asflags            []string
 	Local_include_dirs []string
 }
 
@@ -147,14 +157,17 @@
 
 	// Set arch-specific configurable attributes
 	var copts bazel.StringListAttribute
-	var srcs []string
-	var excludeSrcs []string
+	var srcs bazel.LabelListAttribute
 	var localIncludeDirs []string
+	var asFlags []string
 	for _, props := range m.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			copts.Value = baseCompilerProps.Cflags
-			srcs = baseCompilerProps.Srcs
-			excludeSrcs = baseCompilerProps.Exclude_srcs
+			srcs = bazel.MakeLabelListAttribute(
+				android.BazelLabelForModuleSrcExcludes(
+					ctx,
+					baseCompilerProps.Srcs,
+					baseCompilerProps.Exclude_srcs))
 			localIncludeDirs = baseCompilerProps.Local_include_dirs
 			break
 		}
@@ -164,24 +177,43 @@
 		localIncludeDirs = append(localIncludeDirs, ".")
 	}
 
-	var deps bazel.LabelList
+	var deps bazel.LabelListAttribute
 	for _, props := range m.linker.linkerProps() {
 		if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
-			deps = android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs)
+			deps = bazel.MakeLabelListAttribute(
+				android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
 		}
 	}
 
+	productVariableProps := android.ProductVariableProperties(ctx)
+	if props, exists := productVariableProps["Asflags"]; exists {
+		// TODO(b/183595873): consider deduplicating handling of product variable properties
+		for _, prop := range props {
+			flags, ok := prop.Property.([]string)
+			if !ok {
+				ctx.ModuleErrorf("Could not convert product variable asflag property")
+				return
+			}
+			// TODO(b/183595873) handle other product variable usages -- as selects?
+			if newFlags, subbed := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable); subbed {
+				asFlags = append(asFlags, newFlags...)
+			}
+		}
+	}
+	// TODO(b/183595872) warn/error if we're not handling product variables
+
 	for arch, p := range m.GetArchProperties(&BaseCompilerProperties{}) {
 		if cProps, ok := p.(*BaseCompilerProperties); ok {
+			srcs.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcExcludes(ctx, cProps.Srcs, cProps.Exclude_srcs))
 			copts.SetValueForArch(arch.Name, cProps.Cflags)
 		}
 	}
-	copts.SetValueForArch("default", []string{})
 
 	attrs := &bazelObjectAttributes{
-		Srcs:               android.BazelLabelForModuleSrcExcludes(ctx, srcs, excludeSrcs),
+		Srcs:               srcs,
 		Deps:               deps,
 		Copts:              copts,
+		Asflags:            asFlags,
 		Local_include_dirs: localIncludeDirs,
 	}
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 20274b2..fa6dd87 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,7 +15,6 @@
 package cc
 
 import (
-	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -23,14 +22,17 @@
 	"github.com/google/blueprint"
 )
 
-var prebuiltFixtureFactory = ccFixtureFactory.Extend(
+var prepareForPrebuiltTest = android.GroupFixturePreparers(
+	prepareForCcTest,
 	android.PrepareForTestWithAndroidMk,
 )
 
 func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
-	result := prebuiltFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltTest,
 		fs.AddToFixture(),
-	).Extend(handlers...).RunTestWithBp(t, bp)
+		android.GroupFixturePreparers(handlers...),
+	).RunTestWithBp(t, bp)
 
 	return result.TestContext
 }
@@ -302,8 +304,7 @@
 	})
 
 	fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink")
-	assertString(t, fooRule.Output.String(),
-		filepath.Join(buildDir, ".intermediates/foo/linux_glibc_x86_64/foo"))
+	assertString(t, fooRule.Output.String(), "out/soong/.intermediates/foo/linux_glibc_x86_64/foo")
 	assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo")
 
 	var libfooDep android.Path
@@ -313,8 +314,7 @@
 			break
 		}
 	}
-	assertString(t, libfooDep.String(),
-		filepath.Join(buildDir, ".intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so"))
+	assertString(t, libfooDep.String(), "out/soong/.intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so")
 }
 
 func TestPrebuiltLibrarySanitized(t *testing.T) {
diff --git a/cc/proto_test.go b/cc/proto_test.go
index f8bbd26..b9c89c7 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -61,7 +61,7 @@
 			t.Errorf("expected %q in %q", w, cmd)
 		}
 
-		foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().String()
+		foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().RelativeToTop().String()
 
 		if w := "--plugin=protoc-gen-foobar=" + foobarPath; !strings.Contains(cmd, w) {
 			t.Errorf("expected %q in %q", w, cmd)
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 5a3c181..61925e3 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -66,6 +66,7 @@
 		} else {
 			toFile = m.outputFile.Path()
 		}
+		toFile = toFile.RelativeToTop()
 
 		rule := from.Description("link")
 		for _, dep := range rule.Implicits {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 0833277..8d13ceb 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -86,7 +86,7 @@
 		symbol_file: "",
 	}
 `
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := testCcWithConfig(t, config)
@@ -94,7 +94,7 @@
 	// Check Vendor snapshot output.
 
 	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
 
 	var jsonFiles []string
@@ -212,7 +212,7 @@
 		nocrt: true,
 	}
 `
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	config.TestProductVariables.DirectedVendorSnapshot = true
@@ -224,7 +224,7 @@
 	// Check Vendor snapshot output.
 
 	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
 
 	var includeJsonFiles []string
@@ -516,7 +516,7 @@
 		"vndk/libvndk.so":              nil,
 	}
 
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := CreateTestContext(config)
@@ -628,7 +628,7 @@
 		},
 	}
 `
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := testCcWithConfig(t, config)
@@ -707,7 +707,7 @@
 		"device/vendor.cpp":     nil,
 	}
 
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := CreateTestContext(config)
@@ -730,7 +730,7 @@
 	// Verify the content of the vendor snapshot.
 
 	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
 
 	var includeJsonFiles []string
@@ -799,7 +799,7 @@
 		"device/vendor.cpp": nil,
 	}
 
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := CreateTestContext(config)
@@ -873,7 +873,7 @@
 		recovery_available: true,
 	}
 `
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := testCcWithConfig(t, config)
@@ -881,7 +881,7 @@
 	// Check Recovery snapshot output.
 
 	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
 
 	var jsonFiles []string
@@ -991,7 +991,7 @@
 		"device/recovery.cpp":   nil,
 	}
 
-	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
 	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
 	ctx := CreateTestContext(config)
@@ -1014,7 +1014,7 @@
 	// Verify the content of the recovery snapshot.
 
 	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
 
 	var includeJsonFiles []string
@@ -1091,7 +1091,7 @@
 		nocrt: true,
 	}
 `
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
 	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
 	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -1104,7 +1104,7 @@
 	// Check recovery snapshot output.
 
 	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
 	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
 
 	var includeJsonFiles []string
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index fcc80a9..7bd0868 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -387,6 +387,14 @@
 	}
 	defer in.Close()
 
+	// Remove the target before copying.  In most cases the file won't exist, but if there are
+	// duplicate copy rules for a file and the source file was read-only the second copy could
+	// fail.
+	err = os.Remove(to)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+
 	out, err := os.Create(to)
 	if err != nil {
 		return err
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index 20d9622..dbbc1d8 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -43,6 +43,10 @@
 	// Total size of the logical partition
 	Size *string
 
+	// List of partitions for default group. Default group has no size limit and automatically
+	// minimized when creating an image.
+	Default_group []partitionProperties
+
 	// List of groups. A group defines a fixed sized region. It can host one or more logical
 	// partitions and their total size is limited by the size of the group they are in.
 	Groups []groupProperties
@@ -52,7 +56,7 @@
 }
 
 type groupProperties struct {
-	// Name of the partition group
+	// Name of the partition group. Can't be "default"; use default_group instead.
 	Name *string
 
 	// Size of the partition group
@@ -92,8 +96,9 @@
 	// Sparse the filesystem images and calculate their sizes
 	sparseImages := make(map[string]android.OutputPath)
 	sparseImageSizes := make(map[string]android.OutputPath)
-	for _, group := range l.properties.Groups {
-		for _, part := range group.Partitions {
+
+	sparsePartitions := func(partitions []partitionProperties) {
+		for _, part := range partitions {
 			sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
 			pName := proptools.String(part.Name)
 			sparseImages[pName] = sparseImg
@@ -101,6 +106,12 @@
 		}
 	}
 
+	for _, group := range l.properties.Groups {
+		sparsePartitions(group.Partitions)
+	}
+
+	sparsePartitions(l.properties.Default_group)
+
 	cmd := builder.Command().BuiltTool("lpmake")
 
 	size := proptools.String(l.properties.Size)
@@ -123,10 +134,32 @@
 	groupNames := make(map[string]bool)
 	partitionNames := make(map[string]bool)
 
+	addPartitionsToGroup := func(partitions []partitionProperties, gName string) {
+		for _, part := range partitions {
+			pName := proptools.String(part.Name)
+			if pName == "" {
+				ctx.PropertyErrorf("groups.partitions.name", "must be set")
+			}
+			if _, ok := partitionNames[pName]; ok {
+				ctx.PropertyErrorf("groups.partitions.name", "already exists")
+			} else {
+				partitionNames[pName] = true
+			}
+			// Get size of the partition by reading the -size.txt file
+			pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+			cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
+			cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+		}
+	}
+
+	addPartitionsToGroup(l.properties.Default_group, "default")
+
 	for _, group := range l.properties.Groups {
 		gName := proptools.String(group.Name)
 		if gName == "" {
 			ctx.PropertyErrorf("groups.name", "must be set")
+		} else if gName == "default" {
+			ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`)
 		}
 		if _, ok := groupNames[gName]; ok {
 			ctx.PropertyErrorf("group.name", "already exists")
@@ -142,21 +175,7 @@
 		}
 		cmd.FlagWithArg("--group=", gName+":"+gSize)
 
-		for _, part := range group.Partitions {
-			pName := proptools.String(part.Name)
-			if pName == "" {
-				ctx.PropertyErrorf("groups.partitions.name", "must be set")
-			}
-			if _, ok := partitionNames[pName]; ok {
-				ctx.PropertyErrorf("groups.partitions.name", "already exists")
-			} else {
-				partitionNames[pName] = true
-			}
-			// Get size of the partition by reading the -size.txt file
-			pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
-			cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
-			cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
-		}
+		addPartitionsToGroup(group.Partitions, gName)
 	}
 
 	l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 9019a83..5d438ea 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -798,9 +798,9 @@
 }
 
 type bazelGenruleAttributes struct {
-	Srcs  bazel.LabelList
+	Srcs  bazel.LabelListAttribute
 	Outs  []string
-	Tools bazel.LabelList
+	Tools bazel.LabelListAttribute
 	Cmd   string
 }
 
@@ -828,15 +828,16 @@
 	}
 
 	// Bazel only has the "tools" attribute.
-	tools := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
-	tool_files := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
-	tools.Append(tool_files)
+	tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
+	tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
+	tools_prop.Append(tool_files_prop)
 
-	srcs := android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)
+	tools := bazel.MakeLabelListAttribute(tools_prop)
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs))
 
 	var allReplacements bazel.LabelList
-	allReplacements.Append(tools)
-	allReplacements.Append(srcs)
+	allReplacements.Append(tools.Value)
+	allReplacements.Append(srcs.Value)
 
 	// Replace in and out variables with $< and $@
 	var cmd string
@@ -844,9 +845,9 @@
 		cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
 		cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
 		cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
-		if len(tools.Includes) > 0 {
-			cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Includes[0].Label), -1)
-			cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Includes[0].Label), -1)
+		if len(tools.Value.Includes) > 0 {
+			cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
+			cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
 		}
 		for _, l := range allReplacements.Includes {
 			bpLoc := fmt.Sprintf("$(location %s)", l.Bp_text)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 2ee456d..3f1e9f3 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -670,7 +670,8 @@
 			cmd: "cat $(in) > $(out)",
 		}
        `
-	result := prepareForGenRuleTest.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
 		android.FixtureModifyConfigAndContext(
 			func(config android.Config, ctx *android.TestContext) {
 				config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
diff --git a/java/Android.bp b/java/Android.bp
index b6c14ac..8334b85 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -50,6 +50,7 @@
         "kotlin.go",
         "lint.go",
         "legacy_core_platform_api_usage.go",
+        "platform_bootclasspath.go",
         "platform_compat_config.go",
         "plugin.go",
         "prebuilt_apis.go",
@@ -79,6 +80,7 @@
         "java_test.go",
         "jdeps_test.go",
         "kotlin_test.go",
+        "platform_bootclasspath_test.go",
         "platform_compat_config_test.go",
         "plugin_test.go",
         "rro_test.go",
diff --git a/java/app_test.go b/java/app_test.go
index 825ad20..a99ac62 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -652,7 +652,7 @@
 			} else {
 				aapt2link = m.Output("package-res.apk")
 			}
-			aapt2link = aapt2link.RelativeToTop()
+			aapt2link = aapt2link
 			aapt2Flags := aapt2link.Args["flags"]
 			if test.assetFlag != "" {
 				android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag)
@@ -1993,14 +1993,14 @@
 		`)
 
 	// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
-	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac").RelativeToTop()
+	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
 	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").RelativeToTop()
+	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
 	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)
@@ -2077,7 +2077,7 @@
 		}
 
 		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
-		javac := variant.Rule("javac").RelativeToTop()
+		javac := variant.Rule("javac")
 		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)
@@ -2151,7 +2151,7 @@
 
 	for _, test := range testCases {
 		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
-		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml").RelativeToTop()
+		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
 
 		if len(test.expectedFlags) > 0 {
 			if params.Rule == nil {
@@ -2647,14 +2647,14 @@
 		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").RelativeToTop()
+	res := foo.Output("package-res.apk")
 	aapt2Flags := res.Args["flags"]
 	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 := result.ModuleForTests("bar", "android_common")
-	res = bar.Output("package-res.apk").RelativeToTop()
+	res = bar.Output("package-res.apk")
 	aapt2Flags = res.Args["flags"]
 	e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
 	android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
diff --git a/java/boot_image.go b/java/boot_image.go
index 8594792..0c47976 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -70,16 +70,20 @@
 
 var _ android.ExcludeFromVisibilityEnforcementTag = bootImageContentDepTag
 
+func IsbootImageContentDepTag(tag blueprint.DependencyTag) bool {
+	return tag == bootImageContentDepTag
+}
+
 type bootImageProperties struct {
 	// The name of the image this represents.
 	//
-	// Must be one of "art" or "boot".
+	// If specified then it must be one of "art" or "boot".
 	Image_name *string
 
 	// The contents of this boot image, could be either java_library, java_sdk_library, or boot_image.
 	//
 	// The order of this list matters as it is the order that is used in the bootclasspath.
-	Contents []string `blueprint:"mutated"`
+	Contents []string
 }
 
 type BootImageModule struct {
@@ -104,6 +108,13 @@
 }
 
 func bootImageConsistencyCheck(ctx android.EarlyModuleContext, m *BootImageModule) {
+	contents := m.properties.Contents
+	if m.properties.Image_name == nil && len(contents) == 0 {
+		ctx.ModuleErrorf(`neither of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+	}
+	if m.properties.Image_name != nil && len(contents) != 0 {
+		ctx.ModuleErrorf(`both of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+	}
 	imageName := proptools.String(m.properties.Image_name)
 	if imageName == "art" {
 		// Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
@@ -175,8 +186,8 @@
 func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	tag := ctx.OtherModuleDependencyTag(dep)
 	if tag == bootImageContentDepTag {
-		// Boot image contents are not automatically added to apex, yet.
-		return false
+		// Boot image contents are automatically added to apex.
+		return true
 	}
 	if android.IsMetaDependencyTag(tag) {
 		// Cross-cutting metadata dependencies are metadata.
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
index a289df8..e1866de 100644
--- a/java/boot_image_test.go
+++ b/java/boot_image_test.go
@@ -101,3 +101,27 @@
 			}
 		`)
 }
+
+func TestBootImageWithoutImageNameOrContents(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qneither of the "image_name" and "contents" properties\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+			}
+		`)
+}
+
+func TestBootImageWithImageNameAndContents(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qboth of the "image_name" and "contents" properties\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+				image_name: "boot",
+				contents: ["other"],
+			}
+		`)
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index a8e2b0e..f7595b1 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -226,11 +226,8 @@
 	srcJars     android.Paths
 	srcFiles    android.Paths
 	sourcepaths android.Paths
-	argFiles    android.Paths
 	implicits   android.Paths
 
-	args []string
-
 	docZip      android.WritablePath
 	stubsSrcJar android.WritablePath
 }
@@ -480,15 +477,20 @@
 		j.sourcepaths = android.PathsForModuleSrc(ctx, []string{"."})
 	}
 
-	j.argFiles = android.PathsForModuleSrc(ctx, j.properties.Arg_files)
+	return deps
+}
+
+func (j *Javadoc) expandArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+	var argFiles android.Paths
 	argFilesMap := map[string]string{}
 	argFileLabels := []string{}
 
 	for _, label := range j.properties.Arg_files {
 		var paths = android.PathsForModuleSrc(ctx, []string{label})
 		if _, exists := argFilesMap[label]; !exists {
-			argFilesMap[label] = strings.Join(paths.Strings(), " ")
+			argFilesMap[label] = strings.Join(cmd.PathsForInputs(paths), " ")
 			argFileLabels = append(argFileLabels, label)
+			argFiles = append(argFiles, paths...)
 		} else {
 			ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
 				label, argFilesMap[label], paths)
@@ -508,7 +510,7 @@
 	}
 
 	for _, flag := range flags {
-		args, err := android.Expand(flag, func(name string) (string, error) {
+		expanded, err := android.Expand(flag, func(name string) (string, error) {
 			if strings.HasPrefix(name, "location ") {
 				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
 				if paths, ok := argFilesMap[label]; ok {
@@ -526,10 +528,10 @@
 		if err != nil {
 			ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
 		}
-		j.args = append(j.args, args)
+		cmd.Flag(expanded)
 	}
 
-	return deps
+	cmd.Implicits(argFiles)
 }
 
 func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -563,6 +565,8 @@
 		Flag("-XDignore.symbol.file").
 		Flag("-Xdoclint:none")
 
+	j.expandArgs(ctx, cmd)
+
 	rule.Command().
 		BuiltTool("soong_zip").
 		Flag("-write_if_changed").
@@ -821,7 +825,7 @@
 			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
 	}
 
-	cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
+	d.expandArgs(ctx, cmd)
 
 	if d.properties.Compat_config != nil {
 		compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
index 2b324ae..8d1f591 100644
--- a/java/droiddoc_test.go
+++ b/java/droiddoc_test.go
@@ -80,7 +80,7 @@
 
 	barStubsOutput := barStubsOutputs[0]
 	barDoc := ctx.ModuleForTests("bar-doc", "android_common")
-	javaDoc := barDoc.Rule("javadoc").RelativeToTop()
+	javaDoc := barDoc.Rule("javadoc")
 	if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(barStubsOutput); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
 	}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index e453e62..d7a0668 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -284,7 +284,7 @@
 		cmd.Flag("--include-annotations")
 
 		validatingNullability :=
-			android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
+			strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
 				String(d.properties.Validate_nullability_from_list) != ""
 
 		migratingNullability := String(d.properties.Previous_api) != ""
@@ -360,7 +360,16 @@
 	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
 			for _, dep := range t.deps {
-				if strings.HasSuffix(dep.String(), filename) {
+				if dep.Base() == filename {
+					cmd.Implicit(dep)
+				}
+				if filename != "android.jar" && dep.Base() == "android.jar" {
+					// Metalava implicitly searches these patterns:
+					//  prebuilts/tools/common/api-versions/android-%/android.jar
+					//  prebuilts/sdk/%/public/android.jar
+					// Add android.jar files from the api_levels_annotations_dirs directories to try
+					// to satisfy these patterns.  If Metalava can't find a match for an API level
+					// between 1 and 28 in at least one pattern it will fail.
 					cmd.Implicit(dep)
 				}
 			}
@@ -473,7 +482,7 @@
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 
-	sandbox := proptools.Bool(d.Javadoc.properties.Sandbox)
+	sandbox := proptools.BoolDefault(d.Javadoc.properties.Sandbox, true)
 	if sandbox {
 		rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
 			android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
@@ -509,14 +518,8 @@
 	d.inclusionAnnotationsFlags(ctx, cmd)
 	d.apiLevelsAnnotationsFlags(ctx, cmd)
 
-	if android.InList("--generate-documentation", d.Javadoc.args) {
-		// Currently Metalava have the ability to invoke Javadoc in a separate process.
-		// Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
-		// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
-		d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
-	}
+	d.expandArgs(ctx, cmd)
 
-	cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
 	for _, o := range d.Javadoc.properties.Out {
 		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
 	}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index c6db979..f8125fb 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -34,6 +34,7 @@
 			srcs: ["bar-doc/a.java"],
 			api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
 			api_levels_annotations_enabled: true,
+			sandbox: false,
 		}
 
 		droidstubs {
@@ -43,6 +44,7 @@
 			api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
 			api_levels_annotations_enabled: true,
 			api_levels_jar_filename: "android.other.jar",
+			sandbox: false,
 		}
 		`,
 		map[string][]byte{
@@ -81,10 +83,19 @@
 
 func TestDroidstubsSandbox(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
+		genrule {
+			name: "foo",
+			out: ["foo.txt"],
+			cmd: "touch $(out)",
+		}
+
 		droidstubs {
 			name: "bar-stubs",
 			srcs: ["bar-doc/a.java"],
 			sandbox: true,
+
+			args: "--reference $(location :foo)",
+			arg_files: [":foo"],
 		}
 		`,
 		map[string][]byte{
@@ -96,6 +107,11 @@
 	if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("Expected inputs %q, got %q", w, g)
 	}
+
+	manifest := android.RuleBuilderSboxProtoForTests(t, m.Output("metalava.sbox.textproto"))
+	if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) {
+		t.Errorf("Expected command to contain %q, got %q", w, g)
+	}
 }
 
 func TestDroidstubsWithSystemModules(t *testing.T) {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index d6c6a2d..5c449e5 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -39,7 +39,8 @@
 	prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
 
 func TestHiddenAPISingleton(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
@@ -50,13 +51,14 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
 func TestHiddenAPIIndexSingleton(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("bar"),
 		fixtureSetBootJarsProductVariable("platform:foo", "platform:bar"),
@@ -115,7 +117,8 @@
 			" replaced by the prebuilt module \"prebuilt_foo\" but unfortunately it does not provide a" +
 			" suitable boot dex jar"
 
-	hiddenApiFixtureFactory.Extend(
+	android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
 		RunTestWithBp(t, `
@@ -134,7 +137,8 @@
 }
 
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_import {
@@ -145,13 +149,14 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
@@ -169,7 +174,7 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
 
@@ -178,7 +183,8 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
@@ -196,7 +202,7 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
 
@@ -236,7 +242,8 @@
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			result := hiddenApiFixtureFactory.Extend(
+			result := android.GroupFixturePreparers(
+				hiddenApiFixtureFactory,
 				tc.preparer,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
@@ -244,7 +251,7 @@
 			).RunTest(t)
 
 			hiddenAPI := result.SingletonForTests("hiddenapi")
-			hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
 
@@ -286,7 +293,8 @@
 	// Where to find the prebuilt hiddenapi files:
 	prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
 
-	result := hiddenApiFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
 		fixtureSetBootJarsProductVariable("platform:foo"),
 		fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
 	).RunTestWithBp(t, `
@@ -307,7 +315,7 @@
 	cpRule := hiddenAPI.Rule("Cp")
 	actualCpInput := cpRule.BuildParams.Input
 	actualCpOutput := cpRule.BuildParams.Output
-	encodeDexRule := foo.Rule("hiddenAPIEncodeDex").RelativeToTop()
+	encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
 	actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
 
 	android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
diff --git a/java/java.go b/java/java.go
index 9786947..70ad879 100644
--- a/java/java.go
+++ b/java/java.go
@@ -33,12 +33,12 @@
 )
 
 func init() {
-	RegisterJavaBuildComponents(android.InitRegistrationContext)
+	registerJavaBuildComponents(android.InitRegistrationContext)
 
 	RegisterJavaSdkMemberTypes()
 }
 
-func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
+func registerJavaBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_defaults", DefaultsFactory)
 
 	ctx.RegisterModuleType("java_library", LibraryFactory)
diff --git a/java/java_test.go b/java/java_test.go
index 2ade0fe..fdf7579 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -74,23 +74,6 @@
 	return result.TestContext, result.Config
 }
 
-// testJavaErrorWithConfig is a legacy way of running tests of java modules that expect errors.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
-	t.Helper()
-	// This must be done on the supplied config and not as part of the fixture because any changes to
-	// the fixture's config will be ignored when RunTestWithConfig replaces it.
-	pathCtx := android.PathContextForTesting(config)
-	dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-	result := prepareForJavaTest.
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
-		RunTestWithConfig(t, config)
-	return result.TestContext, result.Config
-}
-
 // testJavaWithFS runs tests using the prepareForJavaTest
 //
 // See testJava for an explanation as to how to stop using this deprecated method.
@@ -250,7 +233,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac").RelativeToTop()
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
@@ -845,7 +828,12 @@
 			if expectedErrorPattern != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
 			}
-			prepareForJavaTest.ExtendWithErrorHandler(errorHandler).RunTest(t, createPreparer(info))
+			android.GroupFixturePreparers(
+				prepareForJavaTest,
+				createPreparer(info),
+			).
+				ExtendWithErrorHandler(errorHandler).
+				RunTest(t)
 		})
 	}
 
@@ -976,7 +964,7 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac").RelativeToTop()
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
@@ -1336,11 +1324,11 @@
 		}
 		`)
 
-	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()
+	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")
 
 	android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
 
@@ -1363,7 +1351,7 @@
 
 	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)).RelativeToTop()
+		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
 		if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
 			t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar)
 		}
@@ -1670,7 +1658,7 @@
 
 	// The bar library should depend on the stubs jar.
 	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -1973,7 +1961,7 @@
 		`)
 	// The baz library should depend on the system stubs jar.
 	bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
new file mode 100644
index 0000000..5507077
--- /dev/null
+++ b/java/platform_bootclasspath.go
@@ -0,0 +1,74 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"android/soong/dexpreopt"
+)
+
+func init() {
+	registerPlatformBootclasspathBuildComponents(android.InitRegistrationContext)
+}
+
+func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
+}
+
+type platformBootclasspathModule struct {
+	android.ModuleBase
+}
+
+func platformBootclasspathFactory() android.Module {
+	m := &platformBootclasspathModule{}
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if SkipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+	dexpreopt.RegisterToolDeps(ctx)
+}
+
+func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Nothing to do if skipping the dexpreopt of boot image jars.
+	if SkipDexpreoptBootJars(ctx) {
+		return
+	}
+
+	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+	// GenerateSingletonBuildActions method as it cannot create it for itself.
+	dexpreopt.GetGlobalSoongConfig(ctx)
+
+	imageConfig := b.getImageConfig(ctx)
+	if imageConfig == nil {
+		return
+	}
+
+	// Construct the boot image info from the config.
+	info := BootImageInfo{imageConfig: imageConfig}
+
+	// Make it available for other modules.
+	ctx.SetProvider(BootImageInfoProvider, info)
+}
+
+func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
+	return defaultBootImageConfig(ctx)
+}
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
new file mode 100644
index 0000000..1c81cfd
--- /dev/null
+++ b/java/platform_bootclasspath_test.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
+)
+
+// Contains some simple tests for platform_bootclasspath.
+
+var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+	dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+func TestPlatformBootclasspath(t *testing.T) {
+	prepareForTestWithPlatformBootclasspath.
+		RunTestWithBp(t, `
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`)
+}
diff --git a/java/rro_test.go b/java/rro_test.go
index 0a10d93..bad60bc 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -68,7 +68,7 @@
 	m := result.ModuleForTests("foo", "android_common")
 
 	// Check AAPT2 link flags.
-	aapt2Flags := m.Output("package-res.apk").RelativeToTop().Args["flags"]
+	aapt2Flags := m.Output("package-res.apk").Args["flags"]
 	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
 	absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
 	if len(absentFlags) > 0 {
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 028c4fe..2b18465 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -313,10 +313,10 @@
 
 			checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
 				foo := result.ModuleForTests("foo", variant)
-				javac := foo.Rule("javac").RelativeToTop()
+				javac := foo.Rule("javac")
 				var deps []string
 
-				aidl := foo.MaybeRule("aidl").RelativeToTop()
+				aidl := foo.MaybeRule("aidl")
 				if aidl.Rule != nil {
 					deps = append(deps, android.PathRelativeToTop(aidl.Output))
 				}
@@ -376,7 +376,7 @@
 				checkClasspath(t, result, true /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl").RelativeToTop()
+					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -389,7 +389,7 @@
 				checkClasspath(t, result, false /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl").RelativeToTop()
+					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -402,14 +402,16 @@
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 8 -target 8
 			t.Run("REL + Java language level 8", func(t *testing.T) {
-				result := fixtureFactory.Extend(prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
+				result := android.GroupFixturePreparers(
+					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
 
 				checkClasspath(t, result, true /* isJava8 */)
 			})
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 9 -target 9
 			t.Run("REL + Java language level 9", func(t *testing.T) {
-				result := fixtureFactory.Extend(prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
+				result := android.GroupFixturePreparers(
+					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
 
 				checkClasspath(t, result, false /* isJava8 */)
 			})
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 7d8935a..7b5a386 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -20,12 +20,12 @@
 	"android/soong/android"
 )
 
-func getModuleHeaderJarsAsNormalizedPaths(result *android.TestResult, moduleNames ...string) []string {
+func getModuleHeaderJarsAsRelativeToTopPaths(result *android.TestResult, moduleNames ...string) []string {
 	paths := []string{}
 	for _, moduleName := range moduleNames {
 		module := result.Module(moduleName, "android_common")
 		info := result.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
-		paths = append(paths, result.NormalizePathsForTesting(info.HeaderJars)...)
+		paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
 	}
 	return paths
 }
@@ -50,15 +50,15 @@
 `)
 
 func TestJavaSystemModules(t *testing.T) {
-	result := prepareForJavaTest.RunTest(t, addSourceSystemModules)
+	result := android.GroupFixturePreparers(prepareForJavaTest, addSourceSystemModules).RunTest(t)
 
 	// check the existence of the source module
 	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
-	expectedSourcePaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
-	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+	expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
 }
 
 var addPrebuiltSystemModules = android.FixtureAddTextFile("prebuilts/Android.bp", `
@@ -77,36 +77,37 @@
 `)
 
 func TestJavaSystemModulesImport(t *testing.T) {
-	result := prepareForJavaTest.RunTest(t, addPrebuiltSystemModules)
+	result := android.GroupFixturePreparers(prepareForJavaTest, addPrebuiltSystemModules).RunTest(t)
 
 	// check the existence of the renamed prebuilt module
 	prebuiltSystemModules := result.ModuleForTests("system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
-	expectedPrebuiltPaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
-	android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+	expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+	android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
 }
 
 func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
-	result := prepareForJavaTest.RunTest(t,
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
 		addSourceSystemModules,
 		addPrebuiltSystemModules,
-	)
+	).RunTest(t)
 
 	// check the existence of the source module
 	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
-	expectedSourcePaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
-	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+	expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
 
 	// check the existence of the renamed prebuilt module
 	prebuiltSystemModules := result.ModuleForTests("prebuilt_system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
-	expectedPrebuiltPaths := getModuleHeaderJarsAsNormalizedPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
-	android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+	expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
+	android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
 }
diff --git a/java/testing.go b/java/testing.go
index 221ceb1..80c107d 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -160,28 +160,6 @@
 	)
 }
 
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
-	bp += GatherRequiredDepsForTest()
-
-	mockFS := android.MockFS{}
-
-	cc.GatherRequiredFilesForTest(mockFS)
-
-	for k, v := range fs {
-		mockFS[k] = v
-	}
-
-	if env == nil {
-		env = make(map[string]string)
-	}
-	if env["ANDROID_JAVA8_HOME"] == "" {
-		env["ANDROID_JAVA8_HOME"] = "jdk8"
-	}
-	config := android.TestArchConfig(buildDir, env, bp, mockFS)
-
-	return config
-}
-
 func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
 	fs := make(map[string][]byte)
 	for _, level := range apiLevels {
@@ -200,19 +178,6 @@
 	return fs
 }
 
-// Register build components provided by this package that are needed by tests.
-//
-// In particular this must register all the components that are used in the `Android.bp` snippet
-// returned by GatherRequiredDepsForTest()
-//
-// deprecated: Use test fixtures instead, e.g. PrepareForTestWithJavaBuildComponents
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
-	registerRequiredBuildComponentsForTest(ctx)
-
-	// Make sure that any tool related module types needed by dexpreopt have been registered.
-	dexpreopt.RegisterToolModulesForTest(ctx)
-}
-
 // registerRequiredBuildComponentsForTest registers the build components used by
 // PrepareForTestWithJavaDefaultModules.
 //
@@ -228,7 +193,8 @@
 	RegisterDexpreoptBootJarsComponents(ctx)
 	RegisterDocsBuildComponents(ctx)
 	RegisterGenRuleBuildComponents(ctx)
-	RegisterJavaBuildComponents(ctx)
+	registerJavaBuildComponents(ctx)
+	registerPlatformBootclasspathBuildComponents(ctx)
 	RegisterPrebuiltApisBuildComponents(ctx)
 	RegisterRuntimeResourceOverlayBuildComponents(ctx)
 	RegisterSdkLibraryBuildComponents(ctx)
@@ -236,23 +202,6 @@
 	RegisterSystemModulesBuildComponents(ctx)
 }
 
-// Gather the module definitions needed by tests that depend upon code from this package.
-//
-// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
-//
-// deprecated: Use test fixtures instead, e.g. PrepareForTestWithJavaDefaultModules
-func GatherRequiredDepsForTest() string {
-	bp := gatherRequiredDepsForTest()
-
-	// For class loader context and <uses-library> tests.
-	bp += dexpreopt.CompatLibDefinitionsForTest()
-
-	// Make sure that any tools needed for dexpreopting are defined.
-	bp += dexpreopt.BpToolModulesForTest()
-
-	return bp
-}
-
 // gatherRequiredDepsForTest gathers the module definitions used by
 // PrepareForTestWithJavaDefaultModules.
 //
diff --git a/python/binary.go b/python/binary.go
index 5b0f080..e955492 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -36,8 +36,8 @@
 
 type bazelPythonBinaryAttributes struct {
 	Main           string
-	Srcs           bazel.LabelList
-	Data           bazel.LabelList
+	Srcs           bazel.LabelListAttribute
+	Data           bazel.LabelListAttribute
 	Python_version string
 }
 
@@ -97,10 +97,13 @@
 		// do nothing, since python_version defaults to PY3.
 	}
 
+	srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+	data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+
 	attrs := &bazelPythonBinaryAttributes{
 		Main:           main,
-		Srcs:           android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs),
-		Data:           android.BazelLabelForModuleSrc(ctx, m.properties.Data),
+		Srcs:           bazel.MakeLabelListAttribute(srcs),
+		Data:           bazel.MakeLabelListAttribute(data),
 		Python_version: python_version,
 	}
 
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index e90564f..bd3bfb1 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -18,7 +18,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/cc"
 )
 
 func TestClippy(t *testing.T) {
@@ -45,15 +44,6 @@
 			clippy_lints: "none",
 		}`
 
-	bp = bp + GatherRequiredDepsForTest()
-	bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-
-	fs := map[string][]byte{
-		// Reuse the same blueprint file for subdirectories.
-		"external/Android.bp": []byte(bp),
-		"hardware/Android.bp": []byte(bp),
-	}
-
 	var clippyLintTests = []struct {
 		modulePath string
 		fooFlags   string
@@ -66,29 +56,22 @@
 	for _, tc := range clippyLintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
-			ctx := CreateTestContext(config)
-			ctx.Register()
-			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
-			android.FailIfErrored(t, errs)
-			_, errs = ctx.PrepareBuildActions(config)
-			android.FailIfErrored(t, errs)
+			result := android.GroupFixturePreparers(
+				prepareForRustTest,
+				// Test with the blueprint file in different directories.
+				android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
+			).RunTest(t)
 
-			r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-			if r.Args["clippyFlags"] != tc.fooFlags {
-				t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["clippyFlags"], tc.fooFlags)
-			}
+			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			android.AssertStringEquals(t, "libfoo flags", tc.fooFlags, r.Args["clippyFlags"])
 
-			r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-			if r.Args["clippyFlags"] != "${config.ClippyDefaultLints}" {
-				t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["clippyFlags"], "${config.ClippyDefaultLints}")
-			}
+			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			android.AssertStringEquals(t, "libbar flags", "${config.ClippyDefaultLints}", r.Args["clippyFlags"])
 
-			r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
 			if r.Rule != nil {
 				t.Errorf("libfoobar is setup to use clippy when explicitly disabled: clippyFlags=%q", r.Args["clippyFlags"])
 			}
-
 		})
 	}
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 3ed086f..c752762 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -19,7 +19,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/cc"
 )
 
 // Test that feature flags are being correctly generated.
@@ -132,15 +131,6 @@
 			lints: "none",
 		}`
 
-	bp = bp + GatherRequiredDepsForTest()
-	bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-
-	fs := map[string][]byte{
-		// Reuse the same blueprint file for subdirectories.
-		"external/Android.bp": []byte(bp),
-		"hardware/Android.bp": []byte(bp),
-	}
-
 	var lintTests = []struct {
 		modulePath string
 		fooFlags   string
@@ -153,29 +143,20 @@
 	for _, tc := range lintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
-			ctx := CreateTestContext(config)
-			ctx.Register()
-			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
-			android.FailIfErrored(t, errs)
-			_, errs = ctx.PrepareBuildActions(config)
-			android.FailIfErrored(t, errs)
+			result := android.GroupFixturePreparers(
+				prepareForRustTest,
+				// Test with the blueprint file in different directories.
+				android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
+			).RunTest(t)
 
-			r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			if !strings.Contains(r.Args["rustcFlags"], tc.fooFlags) {
-				t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["rustcFlags"], tc.fooFlags)
-			}
+			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			android.AssertStringDoesContain(t, "libfoo flags", r.Args["rustcFlags"], tc.fooFlags)
 
-			r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			if !strings.Contains(r.Args["rustcFlags"], "${config.RustDefaultLints}") {
-				t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["rustcFlags"], "${config.RustDefaultLints}")
-			}
+			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			android.AssertStringDoesContain(t, "libbar flags", r.Args["rustcFlags"], "${config.RustDefaultLints}")
 
-			r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			if !strings.Contains(r.Args["rustcFlags"], "${config.RustAllowAllLints}") {
-				t.Errorf("Incorrect flags for libfoobar: %q, want %q", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
-			}
-
+			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			android.AssertStringDoesContain(t, "libfoobar flags", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
 		})
 	}
 }
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 7af4635..09d30db 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -28,9 +28,10 @@
 // testProjectJson run the generation of rust-project.json. It returns the raw
 // content of the generated file.
 func testProjectJson(t *testing.T, bp string) []byte {
-	result := prepareForRustTest.
-		Extend(android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"})).
-		RunTestWithBp(t, bp)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}),
+	).RunTestWithBp(t, bp)
 
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
diff --git a/rust/testing.go b/rust/testing.go
index 5be71c9..75adcfc 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -17,13 +17,12 @@
 import (
 	"android/soong/android"
 	"android/soong/cc"
-	"android/soong/genrule"
 )
 
 // Preparer that will define all cc module types and a limited set of mutators and singletons that
 // make those module types usable.
 var PrepareForTestWithRustBuildComponents = android.GroupFixturePreparers(
-	android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+	android.FixtureRegisterWithContext(registerRequiredBuildComponentsForTest),
 )
 
 // The directory in which rust test default modules will be defined.
@@ -197,7 +196,7 @@
 	return bp
 }
 
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
@@ -231,14 +230,3 @@
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 }
-
-func CreateTestContext(config android.Config) *android.TestContext {
-	ctx := android.NewTestArchContext(config)
-	android.RegisterPrebuiltMutators(ctx)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	genrule.RegisterGenruleBuildComponents(ctx)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	RegisterRequiredBuildComponentsForTest(ctx)
-
-	return ctx
-}
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 8198083..2b9c2de 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -3,3 +3,4 @@
 per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
 per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
 per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
+per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 18174a4..30cb937 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -27,7 +27,6 @@
   platform-mainline-test-exports
   runtime-module-host-exports
   runtime-module-sdk
-  stats-log-api-gen-exports
   statsd-module-sdk
   statsd-module-sdk-for-art
   tzdata-module-test-exports
diff --git a/scripts/gen_ndk_usedby_apex.sh b/scripts/gen_ndk_usedby_apex.sh
index f143161..0d3ed5a 100755
--- a/scripts/gen_ndk_usedby_apex.sh
+++ b/scripts/gen_ndk_usedby_apex.sh
@@ -33,7 +33,7 @@
   do
       if [[ $line = *FUNC*GLOBAL*UND*@* ]] ;
       then
-          echo "$line" | sed -r 's/.*UND (.*)@.*/\1/g' >> "$2"
+          echo "$line" | sed -r 's/.*UND (.*@.*)/\1/g' >> "$2"
       fi
   done < "$1"
   echo "" >> "$2"
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
index 244ed0d..a9b61a1 100755
--- a/scripts/gen_sorted_bss_symbols.sh
+++ b/scripts/gen_sorted_bss_symbols.sh
@@ -18,11 +18,11 @@
 # their sizes.
 # Inputs:
 #  Environment:
-#   CROSS_COMPILE: prefix added to nm tools
+#   CLANG_BIN: path to the clang bin directory
 #  Arguments:
 #   $1: Input ELF file
 #   $2: Output symbol ordering file
 
 set -o pipefail
 
-${CROSS_COMPILE}nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
+${CLANG_BIN}/llvm-nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 1343f35..8168fbf 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -74,7 +74,7 @@
   return parser.parse_args()
 
 
-def enforce_uses_libraries(manifest, required, optional, relax, is_apk = False):
+def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
   """Verify that the <uses-library> tags in the manifest match those provided
   by the build system.
 
@@ -86,26 +86,36 @@
     is_apk:   if the manifest comes from an APK or an XML file
   """
   if is_apk:
-    manifest_required, manifest_optional = extract_uses_libs_apk(manifest)
+    manifest_required, manifest_optional, tags = extract_uses_libs_apk(manifest)
   else:
-    manifest_required, manifest_optional = extract_uses_libs_xml(manifest)
+    manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest)
 
-  err = []
-  if manifest_required != required:
-    err.append('Expected required <uses-library> tags "%s", got "%s"' %
-               (', '.join(required), ', '.join(manifest_required)))
+  if manifest_required == required and manifest_optional == optional:
+    return None
 
-  if manifest_optional != optional:
-    err.append('Expected optional <uses-library> tags "%s", got "%s"' %
-               (', '.join(optional), ', '.join(manifest_optional)))
+  errmsg = ''.join([
+    'mismatch in the <uses-library> tags between the build system and the '
+      'manifest:\n',
+    '\t- required libraries in build system: [%s]\n' % ', '.join(required),
+    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_required),
+    '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
+    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_optional),
+    '\t- tags in the manifest (%s):\n' % path,
+    '\t\t%s\n' % '\t\t'.join(tags),
+      'note: the following options are available:\n',
+    '\t- to temporarily disable the check on command line, rebuild with ',
+      'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
+      'and disable AOT-compilation in dexpreopt)\n',
+    '\t- to temporarily disable the check for the whole product, set ',
+      'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
+    '\t- to fix the check, make build system properties coherent with the '
+      'manifest\n',
+    '\t- see build/make/Changes.md for details\n'])
 
-  if err:
-    errmsg = '\n'.join(err)
-    if not relax:
-      raise ManifestMismatchError(errmsg)
-    return errmsg
+  if not relax:
+    raise ManifestMismatchError(errmsg)
 
-  return None
+  return errmsg
 
 
 def extract_uses_libs_apk(badging):
@@ -115,14 +125,19 @@
 
   required = []
   optional = []
+  lines = []
   for match in re.finditer(pattern, badging):
+    lines.append(match.group(0))
     libname = match.group(2)
     if match.group(1) == None:
       required.append(libname)
     else:
       optional.append(libname)
 
-  return first_unique_elements(required), first_unique_elements(optional)
+  required = first_unique_elements(required)
+  optional = first_unique_elements(optional)
+  tags = first_unique_elements(lines)
+  return required, optional, tags
 
 
 def extract_uses_libs_xml(xml):
@@ -143,7 +158,15 @@
   required = [uses_library_name(x) for x in libs if uses_library_required(x)]
   optional = [uses_library_name(x) for x in libs if not uses_library_required(x)]
 
-  return first_unique_elements(required), first_unique_elements(optional)
+  # render <uses-library> tags as XML for a pretty error message
+  tags = []
+  for lib in libs:
+    tags.append(lib.toprettyxml())
+
+  required = first_unique_elements(required)
+  optional = first_unique_elements(optional)
+  tags = first_unique_elements(tags)
+  return required, optional, tags
 
 
 def first_unique_elements(l):
@@ -278,7 +301,7 @@
       # in the manifest. Raise an exception on mismatch, unless the script was
       # passed a special parameter to suppress exceptions.
       errmsg = enforce_uses_libraries(manifest, required, optional,
-        args.enforce_uses_libraries_relax, is_apk)
+        args.enforce_uses_libraries_relax, is_apk, args.input)
 
       # Create a status file that is empty on success, or contains an error
       # message on failure. When exceptions are suppressed, dexpreopt command
@@ -289,7 +312,12 @@
             f.write("%s\n" % errmsg)
 
     if args.extract_target_sdk_version:
-      print(extract_target_sdk_version(manifest, is_apk))
+      try:
+        print(extract_target_sdk_version(manifest, is_apk))
+      except:
+        # Failed; don't crash, return "any" SDK version. This will result in
+        # dexpreopt not adding any compatibility libraries.
+        print(10000)
 
     if args.output:
       # XML output is supposed to be written only when this script is invoked
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 635ba9d..7159bdd 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -49,9 +49,9 @@
     try:
       relax = False
       manifest_check.enforce_uses_libraries(doc, uses_libraries,
-        optional_uses_libraries, relax, is_apk=False)
+        optional_uses_libraries, relax, False, 'path/to/X/AndroidManifest.xml')
       manifest_check.enforce_uses_libraries(apk, uses_libraries,
-        optional_uses_libraries, relax, is_apk=True)
+        optional_uses_libraries, relax, True, 'path/to/X/X.apk')
       return True
     except manifest_check.ManifestMismatchError:
       return False
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index a886a18..b19fcc5 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -2407,6 +2407,7 @@
             "1",
             "2",
             "3",
+            "current",
         ],
     },
     arch: {
@@ -2461,6 +2462,7 @@
             "1",
             "2",
             "3",
+            "current",
         ],
     },
     target: {
@@ -2500,6 +2502,7 @@
             "1",
             "2",
             "3",
+            "current",
         ],
     },
     target: {
diff --git a/sdk/testing.go b/sdk/testing.go
index 44970f7..9465e13 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -95,7 +95,10 @@
 
 func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult {
 	t.Helper()
-	return prepareForSdkTest.RunTest(t, fs.AddToFixture(), android.FixtureWithRootAndroidBp(bp))
+	return android.GroupFixturePreparers(
+		prepareForSdkTest,
+		fs.AddToFixture(),
+	).RunTestWithBp(t, bp)
 }
 
 func testSdkError(t *testing.T, pattern, bp string) {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 1ae557a..6623381 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -485,7 +485,7 @@
 }
 
 type bazelShBinaryAttributes struct {
-	Srcs bazel.LabelList
+	Srcs bazel.LabelListAttribute
 	// Bazel also supports the attributes below, but (so far) these are not required for Bionic
 	// deps
 	// data
@@ -525,7 +525,8 @@
 		return
 	}
 
-	srcs := android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src})
+	srcs := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
 
 	attrs := &bazelShBinaryAttributes{
 		Srcs: srcs,