Merge "Add libruy_static to allowed_deps"
diff --git a/android/Android.bp b/android/Android.bp
index 8f6c9ad..f17a8a0 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -12,7 +12,6 @@
         "soong",
         "soong-android-soongconfig",
         "soong-bazel",
-        "soong-env",
         "soong-shared",
         "soong-ui-metrics_proto",
     ],
diff --git a/android/androidmk.go b/android/androidmk.go
index 32d7712..9317567 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -44,6 +44,14 @@
 	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
 }
 
+// Enable androidmk support.
+// * Register the singleton
+// * Configure that we are inside make
+var PrepareForTestWithAndroidMk = GroupFixturePreparers(
+	FixtureRegisterWithContext(RegisterAndroidMkBuildComponents),
+	FixtureModifyConfig(SetKatiEnabledForTests),
+)
+
 // Deprecated: Use AndroidMkEntriesProvider instead, especially if you're not going to use the
 // Custom function. It's easier to use and test.
 type AndroidMkDataProvider interface {
diff --git a/android/apex.go b/android/apex.go
index 81f8c86..a014592 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -849,8 +849,12 @@
 		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
 			toName := ctx.OtherModuleName(to)
 			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
-				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s",
-					minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false))
+				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
+					"\n\nDependency path: %s\n\n"+
+					"Consider adding 'min_sdk_version: %q' to %q",
+					minSdkVersion, ctx.ModuleName(), err.Error(),
+					ctx.GetPathString(false),
+					minSdkVersion, ctx.ModuleName())
 				return false
 			}
 		}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index a5c4bed..6675840 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -36,12 +36,15 @@
 
 const (
 	getAllFiles CqueryRequestType = iota
+	getCcObjectFiles
+	getAllFilesAndCcObjectFiles
 )
 
 // Map key to describe bazel cquery requests.
 type cqueryKey struct {
 	label       string
 	requestType CqueryRequestType
+	archType    ArchType
 }
 
 type BazelContext interface {
@@ -50,9 +53,15 @@
 	// has been queued to be run later.
 
 	// Returns result files built by building the given bazel target label.
-	GetAllFiles(label string) ([]string, bool)
+	GetAllFiles(label string, archType ArchType) ([]string, bool)
 
-	// TODO(cparsons): Other cquery-related methods should be added here.
+	// 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)
+
+	// Returns the results of GetAllFiles and GetCcObjectFiles in a single query (in that order).
+	GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
+
 	// ** End cquery methods
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -100,11 +109,21 @@
 	AllFiles map[string][]string
 }
 
-func (m MockBazelContext) GetAllFiles(label string) ([]string, bool) {
+func (m MockBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
 	result, ok := m.AllFiles[label]
 	return result, ok
 }
 
+func (m MockBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
+	result, ok := m.AllFiles[label]
+	return result, ok
+}
+
+func (m MockBazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
+	result, ok := m.AllFiles[label]
+	return result, result, ok
+}
+
 func (m MockBazelContext) InvokeBazel() error {
 	panic("unimplemented")
 }
@@ -123,8 +142,8 @@
 
 var _ BazelContext = MockBazelContext{}
 
-func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) {
-	result, ok := bazelCtx.cquery(label, getAllFiles)
+func (bazelCtx *bazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
+	result, ok := bazelCtx.cquery(label, getAllFiles, archType)
 	if ok {
 		bazelOutput := strings.TrimSpace(result)
 		return strings.Split(bazelOutput, ", "), true
@@ -133,7 +152,41 @@
 	}
 }
 
-func (n noopBazelContext) GetAllFiles(label string) ([]string, bool) {
+func (bazelCtx *bazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
+	result, ok := bazelCtx.cquery(label, getCcObjectFiles, archType)
+	if ok {
+		bazelOutput := strings.TrimSpace(result)
+		return strings.Split(bazelOutput, ", "), true
+	} else {
+		return nil, false
+	}
+}
+
+func (bazelCtx *bazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
+	var allFiles []string
+	var ccObjects []string
+
+	result, ok := bazelCtx.cquery(label, getAllFilesAndCcObjectFiles, archType)
+	if ok {
+		bazelOutput := strings.TrimSpace(result)
+		splitString := strings.Split(bazelOutput, "|")
+		allFilesString := splitString[0]
+		ccObjectsString := splitString[1]
+		allFiles = strings.Split(allFilesString, ", ")
+		ccObjects = strings.Split(ccObjectsString, ", ")
+	}
+	return allFiles, ccObjects, ok
+}
+
+func (n noopBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
+	panic("unimplemented")
+}
+
+func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
+	panic("unimplemented")
+}
+
+func (n noopBazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
 	panic("unimplemented")
 }
 
@@ -207,8 +260,9 @@
 // If the given request was already made (and the results are available), then
 // returns (result, true). If the request is queued but no results are available,
 // then returns ("", false).
-func (context *bazelContext) cquery(label string, requestType CqueryRequestType) (string, bool) {
-	key := cqueryKey{label, requestType}
+func (context *bazelContext) cquery(label string, requestType CqueryRequestType,
+	archType ArchType) (string, bool) {
+	key := cqueryKey{label, requestType, archType}
 	if result, ok := context.results[key]; ok {
 		return result, true
 	} else {
@@ -227,8 +281,12 @@
 	return ""
 }
 
+// Issues the given bazel command with given build label and additional flags.
+// Returns (stdout, stderr, error). The first and second return values are strings
+// containing the stdout and stderr of the run command, and an error is returned if
+// the invocation returned an error code.
 func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
-	extraFlags ...string) (string, error) {
+	extraFlags ...string) (string, string, error) {
 
 	cmdFlags := []string{"--output_base=" + context.outputBase, command}
 	cmdFlags = append(cmdFlags, labels...)
@@ -241,19 +299,24 @@
 		fmt.Sprintf("--platforms=%s", canonicalizeLabel("//build/bazel/platforms:generic_x86_64")))
 	cmdFlags = append(cmdFlags,
 		fmt.Sprintf("--extra_toolchains=%s", canonicalizeLabel("//prebuilts/clang/host/linux-x86:all")))
+	// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
+	cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
 	bazelCmd.Dir = context.workspaceDir
-	bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix())
-
+	bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix(),
+		// Disables local host detection of gcc; toolchain information is defined
+		// explicitly in BUILD files.
+		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
 	stderr := &bytes.Buffer{}
 	bazelCmd.Stderr = stderr
 
 	if output, err := bazelCmd.Output(); err != nil {
-		return "", fmt.Errorf("bazel command failed. command: [%s], error [%s]", bazelCmd, stderr)
+		return "", string(stderr.Bytes()),
+			fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
 	} else {
-		return string(output), nil
+		return string(output), string(stderr.Bytes()), nil
 	}
 }
 
@@ -273,20 +336,81 @@
 }
 
 func (context *bazelContext) mainBzlFileContents() []byte {
+	// TODO(cparsons): Define configuration transitions programmatically based
+	// on available archs.
 	contents := `
 #####################################################
 # This file is generated by soong_build. Do not edit.
 #####################################################
 
+def _x86_64_transition_impl(settings, attr):
+    return {
+        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86_64",
+    }
+
+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,
+    inputs = [],
+    outputs = [
+        "//command_line_option:platforms",
+    ],
+)
+
+x86_transition = transition(
+    implementation = _x86_transition_impl,
+    inputs = [],
+    outputs = [
+        "//command_line_option:platforms",
+    ],
+)
+
+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):
-    return [DefaultInfo(files = depset(ctx.files.deps))]
+    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,
-    attrs = {"deps" : attr.label_list()},
+    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"),
+    },
 )
 
 def _phony_root_impl(ctx):
@@ -317,27 +441,55 @@
 }
 
 func (context *bazelContext) mainBuildFileContents() []byte {
+	// TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
+	// architecture mapping.
 	formatString := `
 # This file is generated by soong_build. Do not edit.
 load(":main.bzl", "mixed_build_root", "phony_root")
 
 mixed_build_root(name = "buildroot",
-    deps = [%s],
+    deps_x86_64 = [%s],
+    deps_x86 = [%s],
+    deps_arm64 = [%s],
+    deps_arm = [%s],
 )
 
 phony_root(name = "phonyroot",
     deps = [":buildroot"],
 )
 `
-	var buildRootDeps []string = nil
+	var deps_x86_64 []string = nil
+	var deps_x86 []string = nil
+	var deps_arm64 []string = nil
+	var deps_arm []string = nil
 	for val, _ := range context.requests {
-		buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)))
+		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))
+		}
 	}
-	buildRootDepsString := strings.Join(buildRootDeps, ",\n            ")
 
-	return []byte(fmt.Sprintf(formatString, buildRootDepsString))
+	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            ")))
 }
 
+// Returns the file contents of the buildroot.cquery file that should be used for the cquery
+// expression in order to obtain information about buildroot and its dependencies.
+// The contents of this file depend on the bazelContext's requests; requests are enumerated
+// and grouped by their request type. The data retrieved for each label depends on its
+// request type.
 func (context *bazelContext) cqueryStarlarkFileContents() []byte {
 	formatString := `
 # This file is generated by soong_build. Do not edit.
@@ -345,23 +497,78 @@
   %s
 }
 
+getCcObjectFilesLabels = {
+  %s
+}
+
+getAllFilesAndCcObjectFilesLabels = {
+  %s
+}
+
+def get_all_files(target):
+  return [f.path for f in target.files.to_list()]
+
+def get_cc_object_files(target):
+  result = []
+  linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
+
+  for linker_input in linker_inputs:
+    for library in linker_input.libraries:
+      for object in library.objects:
+        result += [object.path]
+  return result
+
+def get_arch(target):
+  buildoptions = build_options(target)
+  platforms = build_options(target)["//command_line_option:platforms"]
+  if len(platforms) != 1:
+    # An individual configured target should have only one platform architecture.
+    # Note that it's fine for there to be multiple architectures for the same label,
+    # but each is its own configured target.
+    fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
+  platform_name = build_options(target)["//command_line_option:platforms"][0].name
+  if platform_name == "host":
+    return "HOST"
+  elif not platform_name.startswith("generic_"):
+    fail("expected platform name of the form 'generic_<arch>', but was " + str(platforms))
+    return "UNKNOWN"
+  return platform_name[len("generic_"):]
+
 def format(target):
-  if str(target.label) in getAllFilesLabels:
-    return str(target.label) + ">>" + ', '.join([f.path for f in target.files.to_list()])
+  id_string = str(target.label) + "|" + get_arch(target)
+  if id_string in getAllFilesLabels:
+    return id_string + ">>" + ', '.join(get_all_files(target))
+  elif id_string in getCcObjectFilesLabels:
+    return id_string + ">>" + ', '.join(get_cc_object_files(target))
+  elif id_string in getAllFilesAndCcObjectFilesLabels:
+    return id_string + ">>" + ', '.join(get_all_files(target)) + "|" + ', '.join(get_cc_object_files(target))
   else:
     # This target was not requested via cquery, and thus must be a dependency
     # of a requested target.
-    return ""
+    return id_string + ">>NONE"
 `
-	var buildRootDeps []string = nil
-	// TODO(cparsons): Sort by request type instead of assuming all requests
-	// are of GetAllFiles type.
-	for val, _ := range context.requests {
-		buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label)))
-	}
-	buildRootDepsString := strings.Join(buildRootDeps, ",\n  ")
+	var getAllFilesDeps []string = nil
+	var getCcObjectFilesDeps []string = nil
+	var getAllFilesAndCcObjectFilesDeps []string = nil
 
-	return []byte(fmt.Sprintf(formatString, buildRootDepsString))
+	for val, _ := range context.requests {
+		labelWithArch := getCqueryId(val)
+		mapEntryString := fmt.Sprintf("%q : True", labelWithArch)
+		switch val.requestType {
+		case getAllFiles:
+			getAllFilesDeps = append(getAllFilesDeps, mapEntryString)
+		case getCcObjectFiles:
+			getCcObjectFilesDeps = append(getCcObjectFilesDeps, mapEntryString)
+		case getAllFilesAndCcObjectFiles:
+			getAllFilesAndCcObjectFilesDeps = append(getAllFilesAndCcObjectFilesDeps, mapEntryString)
+		}
+	}
+	getAllFilesDepsString := strings.Join(getAllFilesDeps, ",\n  ")
+	getCcObjectFilesDepsString := strings.Join(getCcObjectFilesDeps, ",\n  ")
+	getAllFilesAndCcObjectFilesDepsString := strings.Join(getAllFilesAndCcObjectFilesDeps, ",\n  ")
+
+	return []byte(fmt.Sprintf(formatString, getAllFilesDepsString, getCcObjectFilesDepsString,
+		getAllFilesAndCcObjectFilesDepsString))
 }
 
 // Returns a workspace-relative path containing build-related metadata required
@@ -376,6 +583,7 @@
 	context.results = make(map[cqueryKey]string)
 
 	var cqueryOutput string
+	var cqueryErr string
 	var err error
 
 	intermediatesDirPath := absolutePath(context.intermediatesDir())
@@ -413,10 +621,16 @@
 		return err
 	}
 	buildrootLabel := "//:buildroot"
-	cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
-		[]string{fmt.Sprintf("deps(%s)", buildrootLabel)},
+	cqueryOutput, cqueryErr, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
+		[]string{fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
 		"--output=starlark",
 		"--starlark:file="+cqueryFileRelpath)
+	err = ioutil.WriteFile(
+		absolutePath(filepath.Join(context.intermediatesDir(), "cquery.out")),
+		[]byte(cqueryOutput), 0666)
+	if err != nil {
+		return err
+	}
 
 	if err != nil {
 		return err
@@ -431,10 +645,11 @@
 	}
 
 	for val, _ := range context.requests {
-		if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok {
+		if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
 			context.results[val] = string(cqueryResult)
 		} else {
-			return fmt.Errorf("missing result for bazel target %s", val.label)
+			return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
+				getCqueryId(val), cqueryOutput, cqueryErr)
 		}
 	}
 
@@ -442,7 +657,7 @@
 	//
 	// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
 	var aqueryOutput string
-	aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
+	aqueryOutput, _, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
 		[]string{fmt.Sprintf("deps(%s)", buildrootLabel),
 			// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
 			// proto sources, which would add a number of unnecessary dependencies.
@@ -460,7 +675,7 @@
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
+	_, _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
 		[]string{"//:phonyroot"})
 
 	if err != nil {
@@ -510,6 +725,9 @@
 
 	// Register bazel-owned build statements (obtained from the aquery invocation).
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
+		if len(buildStatement.Command) < 1 {
+			panic(fmt.Sprintf("unhandled build statement: %s", buildStatement))
+		}
 		rule := NewRuleBuilder(pctx, ctx)
 		cmd := rule.Command()
 		cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
@@ -531,3 +749,16 @@
 		rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
 	}
 }
+
+func getCqueryId(key cqueryKey) string {
+	return canonicalizeLabel(key.label) + "|" + getArchString(key)
+}
+
+func getArchString(key cqueryKey) string {
+	arch := key.archType.Name
+	if len(arch) > 0 {
+		return arch
+	} else {
+		return "x86_64"
+	}
+}
diff --git a/android/config.go b/android/config.go
index ef5eadf..bc1aa3a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -232,7 +232,7 @@
 
 	// Copy the real PATH value to the test environment, it's needed by
 	// NonHermeticHostSystemTool() used in x86_darwin_host.go
-	envCopy["PATH"] = originalEnv["PATH"]
+	envCopy["PATH"] = os.Getenv("PATH")
 
 	config := &config{
 		productVariables: productVariables{
@@ -287,24 +287,21 @@
 	return testConfig
 }
 
-// TestArchConfigFuchsia returns a Config object suitable for using for
-// tests that need to run the arch mutator for the Fuchsia arch.
-func TestArchConfigFuchsia(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
-	testConfig := TestConfig(buildDir, env, bp, fs)
-	config := testConfig.config
-
-	config.Targets = map[OsType][]Target{
-		Fuchsia: []Target{
+func fuchsiaTargets() map[OsType][]Target {
+	return map[OsType][]Target{
+		Fuchsia: {
 			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
 		},
-		BuildOs: []Target{
+		BuildOs: {
 			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
 		},
 	}
-
-	return testConfig
 }
 
+var PrepareForTestSetDeviceToFuchsia = FixtureModifyConfig(func(config Config) {
+	config.Targets = fuchsiaTargets()
+})
+
 func modifyTestConfigToSupportArchMutator(testConfig Config) {
 	config := testConfig.config
 
@@ -527,26 +524,6 @@
 	return PathForOutput(ctx, "host", c.PrebuiltOS(), "framework", path)
 }
 
-// NonHermeticHostSystemTool looks for non-hermetic tools from the system we're
-// running on. These tools are not checked-in to AOSP, and therefore could lead
-// to reproducibility problems. Should not be used for other than finding the
-// XCode SDK (xcrun, sw_vers), etc. See ui/build/paths/config.go for the
-// allowlist of host system tools.
-func (c *config) NonHermeticHostSystemTool(name string) string {
-	for _, dir := range filepath.SplitList(c.Getenv("PATH")) {
-		path := filepath.Join(dir, name)
-		if s, err := os.Stat(path); err != nil {
-			continue
-		} else if m := s.Mode(); !s.IsDir() && m&0111 != 0 {
-			return path
-		}
-	}
-	panic(fmt.Errorf(
-		"Unable to use '%s' as a host system tool for build system "+
-			"hermeticity reasons. See build/soong/ui/build/paths/config.go "+
-			"for the full list of allowed host tools on your system.", name))
-}
-
 // PrebuiltOS returns the name of the host OS used in prebuilts directories.
 func (c *config) PrebuiltOS() string {
 	switch runtime.GOOS {
diff --git a/android/defs.go b/android/defs.go
index 38ecb05..1a76721 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -57,6 +57,15 @@
 		},
 		"cpFlags")
 
+	// A copy rule that only updates the output if it changed.
+	CpIfChanged = pctx.AndroidStaticRule("CpIfChanged",
+		blueprint.RuleParams{
+			Command:     "if ! cmp -s $in $out; then cp $in $out; fi",
+			Description: "cp if changed $out",
+			Restat:      true,
+		},
+		"cpFlags")
+
 	CpExecutable = pctx.AndroidStaticRule("CpExecutable",
 		blueprint.RuleParams{
 			Command:     "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out",
diff --git a/android/env.go b/android/env.go
index c2a09aa..289d803 100644
--- a/android/env.go
+++ b/android/env.go
@@ -15,13 +15,7 @@
 package android
 
 import (
-	"fmt"
-	"os"
-	"os/exec"
-	"strings"
-	"syscall"
-
-	"android/soong/env"
+	"android/soong/shared"
 )
 
 // This file supports dependencies on environment variables.  During build manifest generation,
@@ -32,70 +26,12 @@
 // a manifest regeneration.
 
 var originalEnv map[string]string
-var soongDelveListen string
-var soongDelvePath string
-var soongDelveEnv []string
 
-func init() {
-	// Delve support needs to read this environment variable very early, before NewConfig has created a way to
-	// access originalEnv with dependencies.  Store the value where soong_build can find it, it will manually
-	// ensure the dependencies are created.
-	soongDelveListen = os.Getenv("SOONG_DELVE")
-	soongDelvePath = os.Getenv("SOONG_DELVE_PATH")
-	if soongDelvePath == "" {
-		soongDelvePath, _ = exec.LookPath("dlv")
-	}
-
-	originalEnv = make(map[string]string)
-	soongDelveEnv = []string{}
-	for _, env := range os.Environ() {
-		idx := strings.IndexRune(env, '=')
-		if idx != -1 {
-			originalEnv[env[:idx]] = env[idx+1:]
-			if env[:idx] != "SOONG_DELVE" && env[:idx] != "SOONG_DELVE_PATH" {
-				soongDelveEnv = append(soongDelveEnv, env)
-			}
-		}
-	}
-
-	// Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
-	// variable values.  The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc.
-	os.Clearenv()
-}
-
-func ReexecWithDelveMaybe() {
-	if soongDelveListen == "" {
-		return
-	}
-
-	if soongDelvePath == "" {
-		fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
-		os.Exit(1)
-	}
-	dlvArgv := []string{
-		soongDelvePath,
-		"--listen=:" + soongDelveListen,
-		"--headless=true",
-		"--api-version=2",
-		"exec",
-		os.Args[0],
-		"--",
-	}
-	dlvArgv = append(dlvArgv, os.Args[1:]...)
-	os.Chdir(absSrcDir)
-	syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv)
-	fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
-	os.Exit(1)
-}
-
-// getenv checks either os.Getenv or originalEnv so that it works before or after the init()
-// function above.  It doesn't add any dependencies on the environment variable, so it should
-// only be used for values that won't change.  For values that might change use ctx.Config().Getenv.
-func getenv(key string) string {
-	if originalEnv == nil {
-		return os.Getenv(key)
-	} else {
-		return originalEnv[key]
+func InitEnvironment(envFile string) {
+	var err error
+	originalEnv, err = shared.EnvFromFile(envFile)
+	if err != nil {
+		panic(err)
 	}
 }
 
@@ -108,12 +44,12 @@
 func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) {
 	envDeps := ctx.Config().EnvDeps()
 
-	envFile := PathForOutput(ctx, ".soong.environment")
+	envFile := PathForOutput(ctx, "soong.environment.used")
 	if ctx.Failed() {
 		return
 	}
 
-	data, err := env.EnvFileContents(envDeps)
+	data, err := shared.EnvFileContents(envDeps)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
diff --git a/android/fixture.go b/android/fixture.go
index 0efe329..edbbf08 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"reflect"
 	"strings"
 	"testing"
@@ -61,15 +62,15 @@
 // register module bar twice:
 //   var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
 //   var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
-//   var AllPreparers = FixturePreparers(Preparer1, Preparer2)
+//   var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
 //
 // However, when restructured like this it would work fine:
 //   var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo)
 //   var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar)
 //   var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz)
-//   var Preparer1 = FixturePreparers(RegisterModuleFoo, RegisterModuleBar)
-//   var Preparer2 = FixturePreparers(RegisterModuleBar, RegisterModuleBaz)
-//   var AllPreparers = FixturePreparers(Preparer1, Preparer2)
+//   var Preparer1 = GroupFixturePreparers(RegisterModuleFoo, RegisterModuleBar)
+//   var Preparer2 = GroupFixturePreparers(RegisterModuleBar, RegisterModuleBaz)
+//   var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
 //
 // As after deduping and flattening AllPreparers would result in the following preparers being
 // applied:
@@ -109,7 +110,7 @@
 // An exported preparer for use by other packages that need to use java modules.
 //
 // package java
-// var PrepareForIntegrationTestWithJava = FixturePreparers(
+// var PrepareForIntegrationTestWithJava = GroupFixturePreparers(
 //    android.PrepareForIntegrationTestWithAndroid,
 //    FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
 //    FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
@@ -144,7 +145,7 @@
 // }
 //
 // package cc
-// var PrepareForTestWithCC = FixturePreparers(
+// var PrepareForTestWithCC = GroupFixturePreparers(
 //    android.PrepareForArchMutator,
 //	  android.prepareForPrebuilts,
 //    FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
@@ -153,7 +154,7 @@
 //
 // package apex
 //
-// var PrepareForApex = FixturePreparers(
+// var PrepareForApex = GroupFixturePreparers(
 //    ...
 // )
 //
@@ -182,7 +183,14 @@
 	// Create a Fixture.
 	Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
 
-	// Run the test, expecting no errors, returning a TestResult instance.
+	// ExtendWithErrorHandler creates a new FixtureFactory that will use the supplied error handler
+	// to check the errors (may be 0) reported by the test.
+	//
+	// The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
+	// errors are reported.
+	ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory
+
+	// Run the test, checking any errors reported and returning a TestResult instance.
 	//
 	// Shorthand for Fixture(t, preparers...).RunTest()
 	RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
@@ -191,6 +199,24 @@
 	//
 	// Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
 	RunTestWithBp(t *testing.T, bp string) *TestResult
+
+	// RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
+	// the test fixture.
+	//
+	// In order to allow the Config object to be customized separately to the TestContext a lot of
+	// existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
+	// from the test and then have the TestContext created and configured automatically. e.g.
+	// testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
+	//
+	// This method allows those methods to be migrated to use the test fixture pattern without
+	// requiring that every test that uses those methods be migrated at the same time. That allows
+	// those tests to benefit from correctness in the order of registration quickly.
+	//
+	// This method discards the config (along with its mock file system, product variables,
+	// environment, etc.) that may have been set up by FixturePreparers.
+	//
+	// deprecated
+	RunTestWithConfig(t *testing.T, config Config) *TestResult
 }
 
 // Create a new FixtureFactory that will apply the supplied preparers.
@@ -202,14 +228,23 @@
 	return &fixtureFactory{
 		buildDirSupplier: buildDirSupplier,
 		preparers:        dedupAndFlattenPreparers(nil, preparers),
+
+		// Set the default error handler.
+		errorHandler: FixtureExpectsNoErrors,
 	}
 }
 
 // A set of mock files to add to the mock file system.
 type MockFS map[string][]byte
 
+// Merge adds the extra entries from the supplied map to this one.
+//
+// Fails if the supplied map files with the same paths are present in both of them.
 func (fs MockFS) Merge(extra map[string][]byte) {
 	for p, c := range extra {
+		if _, ok := fs[p]; ok {
+			panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists", p))
+		}
 		fs[p] = c
 	}
 }
@@ -260,25 +295,90 @@
 }
 
 // Add a file to the mock filesystem
+//
+// Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead.
 func FixtureAddFile(path string, contents []byte) FixturePreparer {
 	return FixtureModifyMockFS(func(fs MockFS) {
+		if _, ok := fs[path]; ok {
+			panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path))
+		}
 		fs[path] = contents
 	})
 }
 
 // Add a text file to the mock filesystem
+//
+// Fail if the filesystem already contains a file with that path.
 func FixtureAddTextFile(path string, contents string) FixturePreparer {
 	return FixtureAddFile(path, []byte(contents))
 }
 
+// Override a file in the mock filesystem
+//
+// If the file does not exist this behaves as FixtureAddFile.
+func FixtureOverrideFile(path string, contents []byte) FixturePreparer {
+	return FixtureModifyMockFS(func(fs MockFS) {
+		fs[path] = contents
+	})
+}
+
+// Override a text file in the mock filesystem
+//
+// If the file does not exist this behaves as FixtureAddTextFile.
+func FixtureOverrideTextFile(path string, contents string) FixturePreparer {
+	return FixtureOverrideFile(path, []byte(contents))
+}
+
 // Add the root Android.bp file with the supplied contents.
 func FixtureWithRootAndroidBp(contents string) FixturePreparer {
 	return FixtureAddTextFile("Android.bp", contents)
 }
 
-// Create a composite FixturePreparer that is equivalent to applying each of the supplied
-// FixturePreparer instances in order.
-func FixturePreparers(preparers ...FixturePreparer) FixturePreparer {
+// Merge some environment variables into the fixture.
+func FixtureMergeEnv(env map[string]string) FixturePreparer {
+	return FixtureModifyConfig(func(config Config) {
+		for k, v := range env {
+			if k == "PATH" {
+				panic("Cannot set PATH environment variable")
+			}
+			config.env[k] = v
+		}
+	})
+}
+
+// Modify the env.
+//
+// Will panic if the mutator changes the PATH environment variable.
+func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer {
+	return FixtureModifyConfig(func(config Config) {
+		oldPath := config.env["PATH"]
+		mutator(config.env)
+		newPath := config.env["PATH"]
+		if newPath != oldPath {
+			panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath))
+		}
+	})
+}
+
+// Allow access to the product variables when preparing the fixture.
+type FixtureProductVariables struct {
+	*productVariables
+}
+
+// Modify product variables.
+func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer {
+	return FixtureModifyConfig(func(config Config) {
+		productVariables := FixtureProductVariables{&config.productVariables}
+		mutator(productVariables)
+	})
+}
+
+// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of
+// the supplied FixturePreparer instances in order.
+//
+// Before preparing the fixture the list of preparers is flattened by replacing each
+// instance of GroupFixturePreparers with its contents.
+func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
 	return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
 }
 
@@ -352,9 +452,99 @@
 	return &simpleFixturePreparer{function: preparer}
 }
 
+// FixtureErrorHandler determines how to respond to errors reported by the code under test.
+//
+// Some possible responses:
+// * Fail the test if any errors are reported, see FixtureExpectsNoErrors.
+// * Fail the test if at least one error that matches a pattern is not reported see
+//   FixtureExpectsAtLeastOneErrorMatchingPattern
+// * Fail the test if any unexpected errors are reported.
+//
+// Although at the moment all the error handlers are implemented as simply a wrapper around a
+// function this is defined as an interface to allow future enhancements, e.g. provide different
+// ways other than patterns to match an error and to combine handlers together.
+type FixtureErrorHandler interface {
+	// CheckErrors checks the errors reported.
+	//
+	// The supplied result can be used to access the state of the code under test just as the main
+	// body of the test would but if any errors other than ones expected are reported the state may
+	// be indeterminate.
+	CheckErrors(result *TestResult)
+}
+
+type simpleErrorHandler struct {
+	function func(result *TestResult)
+}
+
+func (h simpleErrorHandler) CheckErrors(result *TestResult) {
+	result.Helper()
+	h.function(result)
+}
+
+// The default fixture error handler.
+//
+// Will fail the test immediately if any errors are reported.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+var FixtureExpectsNoErrors = FixtureCustomErrorHandler(
+	func(result *TestResult) {
+		result.Helper()
+		FailIfErrored(result.T, result.Errs)
+	},
+)
+
+// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail
+// if at least one error that matches the regular expression is not found.
+//
+// The test will be failed if:
+// * No errors are reported.
+// * One or more errors are reported but none match the pattern.
+//
+// The test will not fail if:
+// * Multiple errors are reported that do not match the pattern as long as one does match.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler {
+	return FixtureCustomErrorHandler(func(result *TestResult) {
+		result.Helper()
+		if !FailIfNoMatchingErrors(result.T, pattern, result.Errs) {
+			result.FailNow()
+		}
+	})
+}
+
+// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail
+// if there are any unexpected errors.
+//
+// The test will be failed if:
+// * The number of errors reported does not exactly match the patterns.
+// * One or more of the reported errors do not match a pattern.
+// * No patterns are provided and one or more errors are reported.
+//
+// The test will not fail if:
+// * One or more of the patterns does not match an error.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler {
+	return FixtureCustomErrorHandler(func(result *TestResult) {
+		result.Helper()
+		CheckErrorsAgainstExpectations(result.T, result.Errs, patterns)
+	})
+}
+
+// FixtureCustomErrorHandler creates a custom error handler
+func FixtureCustomErrorHandler(function func(result *TestResult)) FixtureErrorHandler {
+	return simpleErrorHandler{
+		function: function,
+	}
+}
+
 // Fixture defines the test environment.
 type Fixture interface {
-	// Run the test, expecting no errors, returning a TestResult instance.
+	// Run the test, checking any errors reported and returning a TestResult instance.
 	RunTest() *TestResult
 }
 
@@ -381,6 +571,18 @@
 	}
 }
 
+// AssertErrorMessageEquals checks if the error is not nil and has the expected message. If it does
+// not then this reports an error prefixed with the supplied message and including a reason for why
+// it failed.
+func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) {
+	h.Helper()
+	if actual == nil {
+		h.Errorf("Expected error but was nil")
+	} else if actual.Error() != expected {
+		h.Errorf("%s: expected %s, actual %s", message, expected, actual.Error())
+	}
+}
+
 // AssertTrimmedStringEquals checks if the expected and actual values are the same after trimming
 // leading and trailing spaces from them both. If they are not then it reports an error prefixed
 // with the supplied message and including a reason for why it failed.
@@ -408,6 +610,16 @@
 	}
 }
 
+// AssertStringListContains checks if the list of strings contains the expected string. If it does
+// not then it reports an error prefixed with the supplied message and including a reason for why it
+// failed.
+func (h *TestHelper) AssertStringListContains(message string, list []string, expected string) {
+	h.Helper()
+	if !InList(expected, list) {
+		h.Errorf("%s: could not find %q within %q", message, expected, list)
+	}
+}
+
 // AssertArrayString checks if the expected and actual values are equal and if they are not then it
 // reports an error prefixed with the supplied message and including a reason for why it failed.
 func (h *TestHelper) AssertArrayString(message string, expected, actual []string) {
@@ -425,7 +637,7 @@
 	}
 }
 
-// AssertArrayString checks if the expected and actual values are equal using reflect.DeepEqual and
+// AssertDeepEquals checks if the expected and actual values are equal using reflect.DeepEqual and
 // if they are not then it reports an error prefixed with the supplied message and including a
 // reason for why it failed.
 func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
@@ -435,6 +647,23 @@
 	}
 }
 
+// AssertPanic checks that the supplied function panics as expected.
+func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) {
+	h.Helper()
+	panicked := false
+	func() {
+		defer func() {
+			if x := recover(); x != nil {
+				panicked = true
+			}
+		}()
+		funcThatShouldPanic()
+	}()
+	if !panicked {
+		h.Error(message)
+	}
+}
+
 // Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
 type testContext struct {
 	*TestContext
@@ -447,6 +676,9 @@
 
 	fixture *fixture
 	Config  Config
+
+	// The errors that were reported during the test.
+	Errs []error
 }
 
 var _ FixtureFactory = (*fixtureFactory)(nil)
@@ -454,25 +686,33 @@
 type fixtureFactory struct {
 	buildDirSupplier *string
 	preparers        []*simpleFixturePreparer
+	errorHandler     FixtureErrorHandler
 }
 
 func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
-	all := append(f.preparers, dedupAndFlattenPreparers(f.preparers, preparers)...)
-	return &fixtureFactory{
-		buildDirSupplier: f.buildDirSupplier,
-		preparers:        all,
-	}
+	// Create a new slice to avoid accidentally sharing the preparers slice from this factory with
+	// the extending factories.
+	var all []*simpleFixturePreparer
+	all = append(all, f.preparers...)
+	all = append(all, dedupAndFlattenPreparers(f.preparers, preparers)...)
+	// Copy the existing factory.
+	extendedFactory := &fixtureFactory{}
+	*extendedFactory = *f
+	// Use the extended list of preparers.
+	extendedFactory.preparers = all
+	return extendedFactory
 }
 
 func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
 	config := TestConfig(*f.buildDirSupplier, nil, "", nil)
 	ctx := NewTestContext(config)
 	fixture := &fixture{
-		factory: f,
-		t:       t,
-		config:  config,
-		ctx:     ctx,
-		mockFS:  make(MockFS),
+		factory:      f,
+		t:            t,
+		config:       config,
+		ctx:          ctx,
+		mockFS:       make(MockFS),
+		errorHandler: f.errorHandler,
 	}
 
 	for _, preparer := range f.preparers {
@@ -486,6 +726,13 @@
 	return fixture
 }
 
+func (f *fixtureFactory) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory {
+	newFactory := &fixtureFactory{}
+	*newFactory = *f
+	newFactory.errorHandler = errorHandler
+	return newFactory
+}
+
 func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
 	t.Helper()
 	fixture := f.Fixture(t, preparers...)
@@ -497,12 +744,46 @@
 	return f.RunTest(t, FixtureWithRootAndroidBp(bp))
 }
 
+func (f *fixtureFactory) RunTestWithConfig(t *testing.T, config Config) *TestResult {
+	t.Helper()
+	// Create the fixture as normal.
+	fixture := f.Fixture(t).(*fixture)
+
+	// Discard the mock filesystem as otherwise that will override the one in the config.
+	fixture.mockFS = nil
+
+	// Replace the config with the supplied one in the fixture.
+	fixture.config = config
+
+	// Ditto with config derived information in the TestContext.
+	ctx := fixture.ctx
+	ctx.config = config
+	ctx.SetFs(ctx.config.fs)
+	if ctx.config.mockBpList != "" {
+		ctx.SetModuleListFile(ctx.config.mockBpList)
+	}
+
+	return fixture.RunTest()
+}
+
 type fixture struct {
+	// The factory used to create this fixture.
 	factory *fixtureFactory
-	t       *testing.T
-	config  Config
-	ctx     *TestContext
-	mockFS  MockFS
+
+	// The gotest state of the go test within which this was created.
+	t *testing.T
+
+	// The configuration prepared for this fixture.
+	config Config
+
+	// The test context prepared for this fixture.
+	ctx *TestContext
+
+	// The mock filesystem prepared for this fixture.
+	mockFS MockFS
+
+	// The error handler used to check the errors, if any, that are reported.
+	errorHandler FixtureErrorHandler
 }
 
 func (f *fixture) RunTest() *TestResult {
@@ -510,31 +791,40 @@
 
 	ctx := f.ctx
 
-	// The TestConfig() method assumes that the mock filesystem is available when creating so creates
-	// the mock file system immediately. Similarly, the NewTestContext(Config) method assumes that the
-	// supplied Config's FileSystem has been properly initialized before it is called and so it takes
-	// its own reference to the filesystem. However, fixtures create the Config and TestContext early
-	// so they can be modified by preparers at which time the mockFS has not been populated (because
-	// it too is modified by preparers). So, this reinitializes the Config and TestContext's
-	// FileSystem using the now populated mockFS.
-	f.config.mockFileSystem("", f.mockFS)
-	ctx.SetFs(ctx.config.fs)
-	if ctx.config.mockBpList != "" {
-		ctx.SetModuleListFile(ctx.config.mockBpList)
+	// Do not use the fixture's mockFS to initialize the config's mock file system if it has been
+	// cleared by RunTestWithConfig.
+	if f.mockFS != nil {
+		// The TestConfig() method assumes that the mock filesystem is available when creating so
+		// creates the mock file system immediately. Similarly, the NewTestContext(Config) method
+		// assumes that the supplied Config's FileSystem has been properly initialized before it is
+		// called and so it takes its own reference to the filesystem. However, fixtures create the
+		// Config and TestContext early so they can be modified by preparers at which time the mockFS
+		// has not been populated (because it too is modified by preparers). So, this reinitializes the
+		// Config and TestContext's FileSystem using the now populated mockFS.
+		f.config.mockFileSystem("", f.mockFS)
+
+		ctx.SetFs(ctx.config.fs)
+		if ctx.config.mockBpList != "" {
+			ctx.SetModuleListFile(ctx.config.mockBpList)
+		}
 	}
 
 	ctx.Register()
 	_, errs := ctx.ParseBlueprintsFiles("ignored")
-	FailIfErrored(f.t, errs)
-	_, errs = ctx.PrepareBuildActions(f.config)
-	FailIfErrored(f.t, errs)
+	if len(errs) == 0 {
+		_, errs = ctx.PrepareBuildActions(f.config)
+	}
 
 	result := &TestResult{
 		TestHelper:  TestHelper{T: f.t},
 		testContext: testContext{ctx},
 		fixture:     f,
 		Config:      f.config,
+		Errs:        errs,
 	}
+
+	f.errorHandler.CheckErrors(result)
+
 	return result
 }
 
diff --git a/android/fixture_test.go b/android/fixture_test.go
index 7bc033b..5a7bf3b 100644
--- a/android/fixture_test.go
+++ b/android/fixture_test.go
@@ -32,9 +32,9 @@
 	preparer3 := appendToList("preparer3")
 	preparer4 := appendToList("preparer4")
 
-	preparer1Then2 := FixturePreparers(preparer1, preparer2)
+	preparer1Then2 := GroupFixturePreparers(preparer1, preparer2)
 
-	preparer2Then1 := FixturePreparers(preparer2, preparer1)
+	preparer2Then1 := GroupFixturePreparers(preparer2, preparer1)
 
 	buildDir := "build"
 	factory := NewFixtureFactory(&buildDir, preparer1, preparer2, preparer1, preparer1Then2)
diff --git a/android/license.go b/android/license.go
index b140b55..3bc6199 100644
--- a/android/license.go
+++ b/android/license.go
@@ -19,7 +19,7 @@
 )
 
 type licenseKindDependencyTag struct {
-        blueprint.BaseDependencyTag
+	blueprint.BaseDependencyTag
 }
 
 var (
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
index 767b64e..83e83ce 100644
--- a/android/license_kind_test.go
+++ b/android/license_kind_test.go
@@ -8,7 +8,7 @@
 
 var licenseKindTests = []struct {
 	name           string
-	fs             map[string][]byte
+	fs             MockFS
 	expectedErrors []string
 }{
 	{
@@ -97,48 +97,18 @@
 func TestLicenseKind(t *testing.T) {
 	for _, test := range licenseKindTests {
 		t.Run(test.name, func(t *testing.T) {
-			_, errs := testLicenseKind(test.fs)
-
-			expectedErrors := test.expectedErrors
-			if expectedErrors == nil {
-				FailIfErrored(t, errs)
-			} else {
-				for _, expectedError := range expectedErrors {
-					FailIfNoMatchingErrors(t, expectedError, errs)
-				}
-				if len(errs) > len(expectedErrors) {
-					t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
-					for i, expectedError := range expectedErrors {
-						t.Errorf("expectedErrors[%d] = %s", i, expectedError)
-					}
-					for i, err := range errs {
-						t.Errorf("errs[%d] = %s", i, err)
-					}
-				}
-			}
+			licenseTestFixtureFactory.
+				Extend(
+					FixtureRegisterWithContext(func(ctx RegistrationContext) {
+						ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+					}),
+					test.fs.AddToFixture(),
+				).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+				RunTest(t)
 		})
 	}
 }
 
-func testLicenseKind(fs map[string][]byte) (*TestContext, []error) {
-
-	// Create a new config per test as license_kind information is stored in the config.
-	config := TestArchConfig(buildDir, nil, "", fs)
-
-	ctx := NewTestArchContext(config)
-	RegisterLicenseKindBuildComponents(ctx)
-	ctx.RegisterModuleType("mock_license", newMockLicenseModule)
-	ctx.Register()
-
-	_, errs := ctx.ParseBlueprintsFiles(".")
-	if len(errs) > 0 {
-		return ctx, errs
-	}
-
-	_, errs = ctx.PrepareBuildActions(config)
-	return ctx, errs
-}
-
 type mockLicenseProperties struct {
 	License_kinds []string
 }
diff --git a/android/license_test.go b/android/license_test.go
index 552bbae..a564827 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -4,9 +4,24 @@
 	"testing"
 )
 
+// Common test set up for license tests.
+var licenseTestFixtureFactory = emptyTestFixtureFactory.Extend(
+	// General preparers in alphabetical order.
+	PrepareForTestWithDefaults,
+	prepareForTestWithLicenses,
+	PrepareForTestWithOverrides,
+	PrepareForTestWithPackageModule,
+	PrepareForTestWithPrebuilts,
+	PrepareForTestWithVisibility,
+
+	// Additional test specific stuff
+	prepareForTestWithFakePrebuiltModules,
+	FixtureMergeEnv(map[string]string{"ANDROID_REQUIRE_LICENSES": "1"}),
+)
+
 var licenseTests = []struct {
 	name           string
-	fs             map[string][]byte
+	fs             MockFS
 	expectedErrors []string
 }{
 	{
@@ -49,9 +64,9 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice `+
+			`other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice ` +
 				`which is not visible to this module`,
-			`yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice `+
+			`yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice ` +
 				`which is not visible to this module`,
 		},
 	},
@@ -70,7 +85,7 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`top/Blueprints:6:5: module "top_proprietary": license_kinds property `+
+			`top/Blueprints:6:5: module "top_proprietary": license_kinds property ` +
 				`"top_by_exception_only" is not a license_kind module`,
 		},
 	},
@@ -163,58 +178,20 @@
 func TestLicense(t *testing.T) {
 	for _, test := range licenseTests {
 		t.Run(test.name, func(t *testing.T) {
-			_, errs := testLicense(test.fs)
-
-			expectedErrors := test.expectedErrors
-			if expectedErrors == nil {
-				FailIfErrored(t, errs)
-			} else {
-				for _, expectedError := range expectedErrors {
-					FailIfNoMatchingErrors(t, expectedError, errs)
-				}
-				if len(errs) > len(expectedErrors) {
-					t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
-					for i, expectedError := range expectedErrors {
-						t.Errorf("expectedErrors[%d] = %s", i, expectedError)
-					}
-					for i, err := range errs {
-						t.Errorf("errs[%d] = %s", i, err)
-					}
-				}
-			}
+			// Customize the common license text fixture factory.
+			licenseTestFixtureFactory.Extend(
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("rule", newMockRuleModule)
+				}),
+				test.fs.AddToFixture(),
+			).
+				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+				RunTest(t)
 		})
 	}
 }
 
-func testLicense(fs map[string][]byte) (*TestContext, []error) {
-
-	// Create a new config per test as visibility information is stored in the config.
-	env := make(map[string]string)
-	env["ANDROID_REQUIRE_LICENSES"] = "1"
-	config := TestArchConfig(buildDir, env, "", fs)
-
-	ctx := NewTestArchContext(config)
-	RegisterPackageBuildComponents(ctx)
-	registerTestPrebuiltBuildComponents(ctx)
-	RegisterLicenseKindBuildComponents(ctx)
-	RegisterLicenseBuildComponents(ctx)
-	ctx.RegisterModuleType("rule", newMockRuleModule)
-	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
-	ctx.PreArchMutators(RegisterLicensesPackageMapper)
-	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
-	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
-	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
-	ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
-	ctx.Register()
-
-	_, errs := ctx.ParseBlueprintsFiles(".")
-	if len(errs) > 0 {
-		return ctx, errs
-	}
-
-	_, errs = ctx.PrepareBuildActions(config)
-	return ctx, errs
+func testLicense(t *testing.T, fs MockFS, expectedErrors []string) {
 }
 
 type mockRuleModule struct {
diff --git a/android/licenses.go b/android/licenses.go
index 1000429..2838f5d 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -51,7 +51,7 @@
 
 func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty {
 	return applicableLicensesPropertyImpl{
-		name: name,
+		name:             name,
 		licensesProperty: licensesProperty,
 	}
 }
diff --git a/android/licenses_test.go b/android/licenses_test.go
index b94add7..d0e3533 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -6,16 +6,28 @@
 	"github.com/google/blueprint"
 )
 
+var prepareForTestWithLicenses = GroupFixturePreparers(
+	FixtureRegisterWithContext(RegisterLicenseKindBuildComponents),
+	FixtureRegisterWithContext(RegisterLicenseBuildComponents),
+	FixtureRegisterWithContext(registerLicenseMutators),
+)
+
+func registerLicenseMutators(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterLicensesPackageMapper)
+	ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
+	ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
+}
+
 var licensesTests = []struct {
-	name                         string
-	fs                           map[string][]byte
-	expectedErrors               []string
-	effectiveLicenses            map[string][]string
-	effectiveInheritedLicenses   map[string][]string
-	effectivePackage             map[string]string
-	effectiveNotices             map[string][]string
-	effectiveKinds               map[string][]string
-	effectiveConditions          map[string][]string
+	name                       string
+	fs                         MockFS
+	expectedErrors             []string
+	effectiveLicenses          map[string][]string
+	effectiveInheritedLicenses map[string][]string
+	effectivePackage           map[string]string
+	effectiveNotices           map[string][]string
+	effectiveKinds             map[string][]string
+	effectiveConditions        map[string][]string
 }{
 	{
 		name: "invalid module type without licenses property",
@@ -71,28 +83,28 @@
 		},
 		effectiveLicenses: map[string][]string{
 			"libexample1": []string{"top_Apache2"},
-			"libnested": []string{"top_Apache2"},
-			"libother": []string{"top_Apache2"},
+			"libnested":   []string{"top_Apache2"},
+			"libother":    []string{"top_Apache2"},
 		},
 		effectiveKinds: map[string][]string{
 			"libexample1": []string{"notice"},
-			"libnested": []string{"notice"},
-			"libother": []string{"notice"},
+			"libnested":   []string{"notice"},
+			"libother":    []string{"notice"},
 		},
 		effectivePackage: map[string]string{
 			"libexample1": "topDog",
-			"libnested": "topDog",
-			"libother": "topDog",
+			"libnested":   "topDog",
+			"libother":    "topDog",
 		},
 		effectiveConditions: map[string][]string{
 			"libexample1": []string{"shownotice"},
-			"libnested": []string{"shownotice"},
-			"libother": []string{"shownotice"},
+			"libnested":   []string{"shownotice"},
+			"libother":    []string{"shownotice"},
 		},
 		effectiveNotices: map[string][]string{
 			"libexample1": []string{"top/LICENSE", "top/NOTICE"},
-			"libnested": []string{"top/LICENSE", "top/NOTICE"},
-			"libother": []string{"top/LICENSE", "top/NOTICE"},
+			"libnested":   []string{"top/LICENSE", "top/NOTICE"},
+			"libother":    []string{"top/LICENSE", "top/NOTICE"},
 		},
 	},
 
@@ -147,28 +159,28 @@
 				}`),
 		},
 		effectiveLicenses: map[string][]string{
-			"libexample": []string{"nested_other", "top_other"},
+			"libexample":     []string{"nested_other", "top_other"},
 			"libsamepackage": []string{},
-			"libnested": []string{},
-			"libother": []string{},
+			"libnested":      []string{},
+			"libother":       []string{},
 		},
 		effectiveInheritedLicenses: map[string][]string{
-			"libexample": []string{"nested_other", "top_other"},
+			"libexample":     []string{"nested_other", "top_other"},
 			"libsamepackage": []string{"nested_other", "top_other"},
-			"libnested": []string{"nested_other", "top_other"},
-			"libother": []string{"nested_other", "top_other"},
+			"libnested":      []string{"nested_other", "top_other"},
+			"libother":       []string{"nested_other", "top_other"},
 		},
 		effectiveKinds: map[string][]string{
-			"libexample": []string{"nested_notice", "top_notice"},
+			"libexample":     []string{"nested_notice", "top_notice"},
 			"libsamepackage": []string{},
-			"libnested": []string{},
-			"libother": []string{},
+			"libnested":      []string{},
+			"libother":       []string{},
 		},
 		effectiveConditions: map[string][]string{
-			"libexample": []string{"notice"},
+			"libexample":     []string{"notice"},
 			"libsamepackage": []string{},
-			"libnested": []string{},
-			"libother": []string{},
+			"libnested":      []string{},
+			"libother":       []string{},
 		},
 	},
 	{
@@ -218,32 +230,32 @@
 				}`),
 		},
 		effectiveLicenses: map[string][]string{
-			"libexample": []string{"other", "top_nested"},
+			"libexample":     []string{"other", "top_nested"},
 			"libsamepackage": []string{},
-			"libnested": []string{},
-			"libother": []string{},
-			"liboutsider": []string{},
+			"libnested":      []string{},
+			"libother":       []string{},
+			"liboutsider":    []string{},
 		},
 		effectiveInheritedLicenses: map[string][]string{
-			"libexample": []string{"other", "top_nested"},
+			"libexample":     []string{"other", "top_nested"},
 			"libsamepackage": []string{"other", "top_nested"},
-			"libnested": []string{"other", "top_nested"},
-			"libother": []string{"other", "top_nested"},
-			"liboutsider": []string{"other", "top_nested"},
+			"libnested":      []string{"other", "top_nested"},
+			"libother":       []string{"other", "top_nested"},
+			"liboutsider":    []string{"other", "top_nested"},
 		},
 		effectiveKinds: map[string][]string{
-			"libexample": []string{},
+			"libexample":     []string{},
 			"libsamepackage": []string{},
-			"libnested": []string{},
-			"libother": []string{},
-			"liboutsider": []string{},
+			"libnested":      []string{},
+			"libother":       []string{},
+			"liboutsider":    []string{},
 		},
 		effectiveNotices: map[string][]string{
-			"libexample": []string{"top/nested/LICENSE.txt"},
+			"libexample":     []string{"top/nested/LICENSE.txt"},
 			"libsamepackage": []string{},
-			"libnested": []string{},
-			"libother": []string{},
-			"liboutsider": []string{},
+			"libnested":      []string{},
+			"libother":       []string{},
+			"liboutsider":    []string{},
 		},
 	},
 
@@ -285,11 +297,11 @@
 				}`),
 		},
 		effectiveLicenses: map[string][]string{
-			"libexample": []string{"by_exception_only"},
+			"libexample":  []string{"by_exception_only"},
 			"libdefaults": []string{"notice"},
 		},
 		effectiveInheritedLicenses: map[string][]string{
-			"libexample": []string{"by_exception_only"},
+			"libexample":  []string{"by_exception_only"},
 			"libdefaults": []string{"notice"},
 		},
 	},
@@ -327,11 +339,11 @@
 				}`),
 		},
 		effectiveLicenses: map[string][]string{
-			"libexample": []string{"top_notice"},
+			"libexample":  []string{"top_notice"},
 			"liboutsider": []string{},
 		},
 		effectiveInheritedLicenses: map[string][]string{
-			"libexample": []string{"top_notice"},
+			"libexample":  []string{"top_notice"},
 			"liboutsider": []string{"top_notice"},
 		},
 	},
@@ -370,15 +382,15 @@
 				}`),
 		},
 		effectiveLicenses: map[string][]string{
-			"libexample": []string{"top_notice"},
-			"libnested": []string{"outsider"},
-			"libother": []string{},
+			"libexample":  []string{"top_notice"},
+			"libnested":   []string{"outsider"},
+			"libother":    []string{},
 			"liboutsider": []string{},
 		},
 		effectiveInheritedLicenses: map[string][]string{
-			"libexample": []string{"top_notice"},
-			"libnested": []string{"outsider"},
-			"libother": []string{},
+			"libexample":  []string{"top_notice"},
+			"libnested":   []string{"outsider"},
+			"libother":    []string{},
 			"liboutsider": []string{"top_notice", "outsider"},
 		},
 	},
@@ -449,7 +461,7 @@
 		},
 		effectiveInheritedLicenses: map[string][]string{
 			"module": []string{"prebuilt", "top_sources"},
-			"other": []string{"prebuilt", "top_sources"},
+			"other":  []string{"prebuilt", "top_sources"},
 		},
 	},
 }
@@ -457,40 +469,48 @@
 func TestLicenses(t *testing.T) {
 	for _, test := range licensesTests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx, errs := testLicenses(buildDir, test.fs)
-
-			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+			// Customize the common license text fixture factory.
+			result := licenseTestFixtureFactory.Extend(
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
+					ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
+					ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
+				}),
+				test.fs.AddToFixture(),
+			).
+				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+				RunTest(t)
 
 			if test.effectiveLicenses != nil {
-				checkEffectiveLicenses(t, ctx, test.effectiveLicenses)
+				checkEffectiveLicenses(result, test.effectiveLicenses)
 			}
 
 			if test.effectivePackage != nil {
-				checkEffectivePackage(t, ctx, test.effectivePackage)
+				checkEffectivePackage(result, test.effectivePackage)
 			}
 
 			if test.effectiveNotices != nil {
-				checkEffectiveNotices(t, ctx, test.effectiveNotices)
+				checkEffectiveNotices(result, test.effectiveNotices)
 			}
 
 			if test.effectiveKinds != nil {
-				checkEffectiveKinds(t, ctx, test.effectiveKinds)
+				checkEffectiveKinds(result, test.effectiveKinds)
 			}
 
 			if test.effectiveConditions != nil {
-				checkEffectiveConditions(t, ctx, test.effectiveConditions)
+				checkEffectiveConditions(result, test.effectiveConditions)
 			}
 
 			if test.effectiveInheritedLicenses != nil {
-				checkEffectiveInheritedLicenses(t, ctx, test.effectiveInheritedLicenses)
+				checkEffectiveInheritedLicenses(result, test.effectiveInheritedLicenses)
 			}
 		})
 	}
 }
 
-func checkEffectiveLicenses(t *testing.T, ctx *TestContext, effectiveLicenses map[string][]string) {
+func checkEffectiveLicenses(result *TestResult, effectiveLicenses map[string][]string) {
 	actualLicenses := make(map[string][]string)
-	ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
 		if _, ok := m.(*licenseModule); ok {
 			return
 		}
@@ -502,7 +522,7 @@
 		}
 		module, ok := m.(Module)
 		if !ok {
-			t.Errorf("%q not a module", m.Name())
+			result.Errorf("%q not a module", m.Name())
 			return
 		}
 		base := module.base()
@@ -518,14 +538,14 @@
 			licenses = []string{}
 		}
 		if !compareUnorderedStringArrays(expectedLicenses, licenses) {
-			t.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
+			result.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
 		}
 	}
 }
 
-func checkEffectiveInheritedLicenses(t *testing.T, ctx *TestContext, effectiveInheritedLicenses map[string][]string) {
+func checkEffectiveInheritedLicenses(result *TestResult, effectiveInheritedLicenses map[string][]string) {
 	actualLicenses := make(map[string][]string)
-	ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
 		if _, ok := m.(*licenseModule); ok {
 			return
 		}
@@ -537,7 +557,7 @@
 		}
 		module, ok := m.(Module)
 		if !ok {
-			t.Errorf("%q not a module", m.Name())
+			result.Errorf("%q not a module", m.Name())
 			return
 		}
 		base := module.base()
@@ -548,7 +568,7 @@
 		for _, l := range base.commonProperties.Effective_licenses {
 			inherited[l] = true
 		}
-		ctx.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
+		result.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
 			if _, ok := c.(*licenseModule); ok {
 				return
 			}
@@ -560,7 +580,7 @@
 			}
 			cmodule, ok := c.(Module)
 			if !ok {
-				t.Errorf("%q not a module", c.Name())
+				result.Errorf("%q not a module", c.Name())
 				return
 			}
 			cbase := cmodule.base()
@@ -583,14 +603,14 @@
 			licenses = []string{}
 		}
 		if !compareUnorderedStringArrays(expectedInheritedLicenses, licenses) {
-			t.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
+			result.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
 		}
 	}
 }
 
-func checkEffectivePackage(t *testing.T, ctx *TestContext, effectivePackage map[string]string) {
+func checkEffectivePackage(result *TestResult, effectivePackage map[string]string) {
 	actualPackage := make(map[string]string)
-	ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
 		if _, ok := m.(*licenseModule); ok {
 			return
 		}
@@ -602,7 +622,7 @@
 		}
 		module, ok := m.(Module)
 		if !ok {
-			t.Errorf("%q not a module", m.Name())
+			result.Errorf("%q not a module", m.Name())
 			return
 		}
 		base := module.base()
@@ -623,14 +643,14 @@
 			packageName = ""
 		}
 		if expectedPackage != packageName {
-			t.Errorf("effective package mismatch for module %q: expected %q, found %q", moduleName, expectedPackage, packageName)
+			result.Errorf("effective package mismatch for module %q: expected %q, found %q", moduleName, expectedPackage, packageName)
 		}
 	}
 }
 
-func checkEffectiveNotices(t *testing.T, ctx *TestContext, effectiveNotices map[string][]string) {
+func checkEffectiveNotices(result *TestResult, effectiveNotices map[string][]string) {
 	actualNotices := make(map[string][]string)
-	ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
 		if _, ok := m.(*licenseModule); ok {
 			return
 		}
@@ -642,7 +662,7 @@
 		}
 		module, ok := m.(Module)
 		if !ok {
-			t.Errorf("%q not a module", m.Name())
+			result.Errorf("%q not a module", m.Name())
 			return
 		}
 		base := module.base()
@@ -658,14 +678,14 @@
 			notices = []string{}
 		}
 		if !compareUnorderedStringArrays(expectedNotices, notices) {
-			t.Errorf("effective notice files mismatch for module %q: expected %q, found %q", moduleName, expectedNotices, notices)
+			result.Errorf("effective notice files mismatch for module %q: expected %q, found %q", moduleName, expectedNotices, notices)
 		}
 	}
 }
 
-func checkEffectiveKinds(t *testing.T, ctx *TestContext, effectiveKinds map[string][]string) {
+func checkEffectiveKinds(result *TestResult, effectiveKinds map[string][]string) {
 	actualKinds := make(map[string][]string)
-	ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
 		if _, ok := m.(*licenseModule); ok {
 			return
 		}
@@ -677,7 +697,7 @@
 		}
 		module, ok := m.(Module)
 		if !ok {
-			t.Errorf("%q not a module", m.Name())
+			result.Errorf("%q not a module", m.Name())
 			return
 		}
 		base := module.base()
@@ -693,14 +713,14 @@
 			kinds = []string{}
 		}
 		if !compareUnorderedStringArrays(expectedKinds, kinds) {
-			t.Errorf("effective license kinds mismatch for module %q: expected %q, found %q", moduleName, expectedKinds, kinds)
+			result.Errorf("effective license kinds mismatch for module %q: expected %q, found %q", moduleName, expectedKinds, kinds)
 		}
 	}
 }
 
-func checkEffectiveConditions(t *testing.T, ctx *TestContext, effectiveConditions map[string][]string) {
+func checkEffectiveConditions(result *TestResult, effectiveConditions map[string][]string) {
 	actualConditions := make(map[string][]string)
-	ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
 		if _, ok := m.(*licenseModule); ok {
 			return
 		}
@@ -712,7 +732,7 @@
 		}
 		module, ok := m.(Module)
 		if !ok {
-			t.Errorf("%q not a module", m.Name())
+			result.Errorf("%q not a module", m.Name())
 			return
 		}
 		base := module.base()
@@ -728,7 +748,7 @@
 			conditions = []string{}
 		}
 		if !compareUnorderedStringArrays(expectedConditions, conditions) {
-			t.Errorf("effective license conditions mismatch for module %q: expected %q, found %q", moduleName, expectedConditions, conditions)
+			result.Errorf("effective license conditions mismatch for module %q: expected %q, found %q", moduleName, expectedConditions, conditions)
 		}
 	}
 }
@@ -754,41 +774,6 @@
 	return true
 }
 
-func testLicenses(buildDir string, fs map[string][]byte) (*TestContext, []error) {
-
-	// Create a new config per test as licenses information is stored in the config.
-	env := make(map[string]string)
-	env["ANDROID_REQUIRE_LICENSES"] = "1"
-	config := TestArchConfig(buildDir, env, "", fs)
-
-	ctx := NewTestArchContext(config)
-	ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
-	ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
-	ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
-
-	// Order of the following method calls is significant.
-	RegisterPackageBuildComponents(ctx)
-	registerTestPrebuiltBuildComponents(ctx)
-	RegisterLicenseKindBuildComponents(ctx)
-	RegisterLicenseBuildComponents(ctx)
-	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
-	ctx.PreArchMutators(RegisterLicensesPackageMapper)
-	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
-	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
-	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
-	ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
-	ctx.Register()
-
-	_, errs := ctx.ParseBlueprintsFiles(".")
-	if len(errs) > 0 {
-		return ctx, errs
-	}
-
-	_, errs = ctx.PrepareBuildActions(config)
-	return ctx, errs
-}
-
 type mockLicensesBadProperties struct {
 	Visibility []string
 }
diff --git a/android/module.go b/android/module.go
index 1936d3c..9f923e2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -443,6 +443,7 @@
 	Disable()
 	Enabled() bool
 	Target() Target
+	MultiTargets() []Target
 	Owner() string
 	InstallInData() bool
 	InstallInTestcases() bool
@@ -752,7 +753,7 @@
 	// Whether this module is installed to vendor ramdisk
 	Vendor_ramdisk *bool
 
-	// Whether this module is built for non-native architecures (also known as native bridge binary)
+	// Whether this module is built for non-native architectures (also known as native bridge binary)
 	Native_bridge_supported *bool `android:"arch_variant"`
 
 	// init.rc files to be installed if this module is installed
@@ -1831,6 +1832,18 @@
 			return
 		}
 
+		m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
+		rcDir := PathForModuleInstall(ctx, "etc", "init")
+		for _, src := range m.initRcPaths {
+			ctx.PackageFile(rcDir, filepath.Base(src.String()), src)
+		}
+
+		m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
+		vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest")
+		for _, src := range m.vintfFragmentsPaths {
+			ctx.PackageFile(vintfDir, filepath.Base(src.String()), src)
+		}
+
 		// Create the set of tagged dist files after calling GenerateAndroidBuildActions
 		// as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the
 		// output paths being set which must be done before or during
@@ -1843,8 +1856,6 @@
 		m.installFiles = append(m.installFiles, ctx.installFiles...)
 		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
 		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
-		m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
-		m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
 		for k, v := range ctx.phonies {
 			m.phonies[k] = append(m.phonies[k], v...)
 		}
diff --git a/android/mutator.go b/android/mutator.go
index b023001..9552aa1 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -33,22 +33,8 @@
 //   run FinalDeps mutators (CreateVariations disallowed in this phase)
 //   continue on to GenerateAndroidBuildActions
 
-func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
-	for _, t := range mutators {
-		var handle blueprint.MutatorHandle
-		if t.bottomUpMutator != nil {
-			handle = ctx.RegisterBottomUpMutator(t.name, t.bottomUpMutator)
-		} else if t.topDownMutator != nil {
-			handle = ctx.RegisterTopDownMutator(t.name, t.topDownMutator)
-		}
-		if t.parallel {
-			handle.Parallel()
-		}
-	}
-}
-
 // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
-func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) {
+func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{
 		bazelConversionMode: true,
 	}
@@ -80,10 +66,17 @@
 		f(mctx)
 	}
 
-	registerMutatorsToContext(ctx, mctx.mutators)
+	mctx.mutators.registerAll(ctx)
 }
 
-func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
+// collateGloballyRegisteredMutators constructs the list of mutators that have been registered
+// with the InitRegistrationContext and will be used at runtime.
+func collateGloballyRegisteredMutators() sortableComponents {
+	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
+}
+
+// collateRegisteredMutators constructs a single list of mutators from the separate lists.
+func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) sortableComponents {
 	mctx := &registerMutatorsContext{}
 
 	register := func(funcs []RegisterMutatorFunc) {
@@ -103,11 +96,11 @@
 	mctx.finalPhase = true
 	register(finalDeps)
 
-	registerMutatorsToContext(ctx, mctx.mutators)
+	return mctx.mutators
 }
 
 type registerMutatorsContext struct {
-	mutators            []*mutator
+	mutators            sortableComponents
 	finalPhase          bool
 	bazelConversionMode bool
 }
@@ -473,6 +466,23 @@
 	return mutator
 }
 
+func (mutator *mutator) componentName() string {
+	return mutator.name
+}
+
+func (mutator *mutator) register(ctx *Context) {
+	blueprintCtx := ctx.Context
+	var handle blueprint.MutatorHandle
+	if mutator.bottomUpMutator != nil {
+		handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator)
+	} else if mutator.topDownMutator != nil {
+		handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
+	}
+	if mutator.parallel {
+		handle.Parallel()
+	}
+}
+
 type MutatorHandle interface {
 	Parallel() MutatorHandle
 }
diff --git a/android/override_module.go b/android/override_module.go
index fa08566..97acc5c 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -215,7 +215,14 @@
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
 	ctx.TopDown("register_override", registerOverrideMutator).Parallel()
 	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
+	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
+	// add deps from overridable properties.
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
+	// Because overridableModuleDepsMutator is run after PrebuiltPostDepsMutator,
+	// prebuilt's ReplaceDependencies doesn't affect to those deps added by overridable properties.
+	// By running PrebuiltPostDepsMutator again after overridableModuleDepsMutator, deps via overridable properties
+	// can be replaced with prebuilts.
+	ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).Parallel()
 	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel()
 }
 
diff --git a/android/package.go b/android/package.go
index 7012fc7..878e4c4 100644
--- a/android/package.go
+++ b/android/package.go
@@ -23,6 +23,8 @@
 	RegisterPackageBuildComponents(InitRegistrationContext)
 }
 
+var PrepareForTestWithPackageModule = FixtureRegisterWithContext(RegisterPackageBuildComponents)
+
 // Register the package module type.
 func RegisterPackageBuildComponents(ctx RegistrationContext) {
 	ctx.RegisterModuleType("package", PackageFactory)
diff --git a/android/path_properties.go b/android/path_properties.go
index 853e5a9..2c8d27c 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -118,6 +118,13 @@
 				*values = append(*values, v.Index(i).Field(index[0]))
 			}
 		} else {
+			// Dereference it if it's a pointer.
+			if v.Kind() == reflect.Ptr {
+				if v.IsNil() {
+					return
+				}
+				v = v.Elem()
+			}
 			*values = append(*values, v.Field(index[0]))
 		}
 		return
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index 2aab748..85c96ee 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -26,6 +26,9 @@
 		Bar []string `android:"path,arch_variant"`
 		Baz *string  `android:"path"`
 		Qux string
+		V   *struct {
+			W string `android:"path"`
+		}
 	}
 
 	// A second property struct with a duplicate property name
@@ -94,8 +97,11 @@
 						],
 					},
 				],
+				v: {
+					w: ":w",
+				},
 			}`,
-			deps: []string{"a", "b", "c", "x", "y", "z"},
+			deps: []string{"a", "b", "c", "w", "x", "y", "z"},
 		},
 		{
 			name: "arch variant",
@@ -136,6 +142,10 @@
 				}
 
 				filegroup {
+					name: "w",
+				}
+
+				filegroup {
 					name: "x",
 				}
 
diff --git a/android/paths.go b/android/paths.go
index ada4da6..3f4d3f2 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1662,9 +1662,9 @@
 			// on a device without a dedicated recovery partition, install the
 			// recovery variant.
 			if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
-				partition = "vendor-ramdisk/first_stage_ramdisk"
+				partition = "vendor_ramdisk/first_stage_ramdisk"
 			} else {
-				partition = "vendor-ramdisk"
+				partition = "vendor_ramdisk"
 			}
 			if !ctx.InstallInRoot() {
 				partition += "/system"
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 39d30c5..04864a1 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -100,7 +100,7 @@
 // more modules like this.
 func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
 	if p.srcsSupplier != nil {
-		srcs := p.srcsSupplier(ctx)
+		srcs := p.srcsSupplier(ctx, ctx.Module())
 
 		if len(srcs) == 0 {
 			ctx.PropertyErrorf(p.srcsPropertyName, "missing prebuilt source file")
@@ -128,8 +128,11 @@
 
 // Called to provide the srcs value for the prebuilt module.
 //
+// This can be called with a context for any module not just the prebuilt one itself. It can also be
+// called concurrently.
+//
 // Return the src value or nil if it is not available.
-type PrebuiltSrcsSupplier func(ctx BaseModuleContext) []string
+type PrebuiltSrcsSupplier func(ctx BaseModuleContext, prebuilt Module) []string
 
 // Initialize the module as a prebuilt module that uses the provided supplier to access the
 // prebuilt sources of the module.
@@ -163,7 +166,7 @@
 		panic(fmt.Errorf("srcs must not be nil"))
 	}
 
-	srcsSupplier := func(ctx BaseModuleContext) []string {
+	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
 		return *srcs
 	}
 
@@ -184,7 +187,7 @@
 	srcFieldIndex := srcStructField.Index
 	srcPropertyName := proptools.PropertyNameForField(srcField)
 
-	srcsSupplier := func(ctx BaseModuleContext) []string {
+	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
 		if !module.Enabled() {
 			return nil
 		}
@@ -256,12 +259,12 @@
 			panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
 		}
 		if !p.properties.SourceExists {
-			p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil)
+			p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil, m)
 		}
 	} else if s, ok := ctx.Module().(Module); ok {
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(m Module) {
 			p := m.(PrebuiltInterface).Prebuilt()
-			if p.usePrebuilt(ctx, s) {
+			if p.usePrebuilt(ctx, s, m) {
 				p.properties.UsePrebuilt = true
 				s.ReplacedByPrebuilt()
 			}
@@ -296,8 +299,8 @@
 
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
 // will be used if it is marked "prefer" or if the source module is disabled.
-func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
-	if p.srcsSupplier != nil && len(p.srcsSupplier(ctx)) == 0 {
+func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module, prebuilt Module) bool {
+	if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 {
 		return false
 	}
 
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 164b1bc..32af5df 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -388,6 +388,8 @@
 	ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
 }
 
+var prepareForTestWithFakePrebuiltModules = FixtureRegisterWithContext(registerTestPrebuiltModules)
+
 func registerTestPrebuiltModules(ctx RegistrationContext) {
 	ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
 	ctx.RegisterModuleType("source", newSourceModule)
diff --git a/android/queryview.go b/android/queryview.go
index 9e3e45a..12d14df 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -66,12 +66,20 @@
 	bazelQueryView := ctx.Rule(pctx, "bazelQueryView",
 		blueprint.RuleParams{
 			Command: fmt.Sprintf(
-				"rm -rf ${outDir}/* && "+
-					"%s --bazel_queryview_dir ${outDir} %s && "+
-					"echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d",
-				primaryBuilder.String(),
-				strings.Join(os.Args[1:], " "),
+				`rm -rf "${outDir}/"* && `+
+					`mkdir -p "${outDir}" && `+
+					`echo WORKSPACE: cat "%s" > "${outDir}/.queryview-depfile.d" && `+
+					`BUILDER="%s" && `+
+					`echo BUILDER=$$BUILDER && `+
+					`cd "$$(dirname "$$BUILDER")" && `+
+					`echo PWD=$$PWD && `+
+					`ABSBUILDER="$$PWD/$$(basename "$$BUILDER")" && `+
+					`echo ABSBUILDER=$$ABSBUILDER && `+
+					`cd / && `+
+					`env -i "$$ABSBUILDER" --bazel_queryview_dir "${outDir}" "%s"`,
 				moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
+				primaryBuilder.String(),
+				strings.Join(os.Args[1:], "\" \""),
 			),
 			CommandDeps: []string{primaryBuilder.String()},
 			Description: fmt.Sprintf(
diff --git a/android/register.go b/android/register.go
index 47df972..c9e66e9 100644
--- a/android/register.go
+++ b/android/register.go
@@ -21,21 +21,78 @@
 	"github.com/google/blueprint"
 )
 
+// A sortable component is one whose registration order affects the order in which it is executed
+// and so affects the behavior of the build system. As a result it is important for the order in
+// which they are registered during tests to match the order used at runtime and so the test
+// infrastructure will sort them to match.
+//
+// The sortable components are mutators, singletons and pre-singletons. Module types are not
+// sortable because their order of registration does not affect the runtime behavior.
+type sortableComponent interface {
+	// componentName returns the name of the component.
+	//
+	// Uniquely identifies the components within the set of components used at runtimr and during
+	// tests.
+	componentName() string
+
+	// register registers this component in the supplied context.
+	register(ctx *Context)
+}
+
+type sortableComponents []sortableComponent
+
+// registerAll registers all components in this slice with the supplied context.
+func (r sortableComponents) registerAll(ctx *Context) {
+	for _, c := range r {
+		c.register(ctx)
+	}
+}
+
 type moduleType struct {
 	name    string
 	factory ModuleFactory
 }
 
+func (t moduleType) register(ctx *Context) {
+	ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+}
+
 var moduleTypes []moduleType
 var moduleTypesForDocs = map[string]reflect.Value{}
 
 type singleton struct {
+	// True if this should be registered as a pre-singleton, false otherwise.
+	pre bool
+
 	name    string
 	factory SingletonFactory
 }
 
-var singletons []singleton
-var preSingletons []singleton
+func newSingleton(name string, factory SingletonFactory) singleton {
+	return singleton{false, name, factory}
+}
+
+func newPreSingleton(name string, factory SingletonFactory) singleton {
+	return singleton{true, name, factory}
+}
+
+func (s singleton) componentName() string {
+	return s.name
+}
+
+func (s singleton) register(ctx *Context) {
+	adaptor := SingletonFactoryAdaptor(ctx, s.factory)
+	if s.pre {
+		ctx.RegisterPreSingletonType(s.name, adaptor)
+	} else {
+		ctx.RegisterSingletonType(s.name, adaptor)
+	}
+}
+
+var _ sortableComponent = singleton{}
+
+var singletons sortableComponents
+var preSingletons sortableComponents
 
 type mutator struct {
 	name            string
@@ -44,6 +101,8 @@
 	parallel        bool
 }
 
+var _ sortableComponent = &mutator{}
+
 type ModuleFactory func() Module
 
 // ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
@@ -84,11 +143,11 @@
 }
 
 func RegisterSingletonType(name string, factory SingletonFactory) {
-	singletons = append(singletons, singleton{name, factory})
+	singletons = append(singletons, newSingleton(name, factory))
 }
 
 func RegisterPreSingletonType(name string, factory SingletonFactory) {
-	preSingletons = append(preSingletons, singleton{name, factory})
+	preSingletons = append(preSingletons, newPreSingleton(name, factory))
 }
 
 type Context struct {
@@ -107,46 +166,51 @@
 // files to semantically equivalent BUILD files.
 func (ctx *Context) RegisterForBazelConversion() {
 	for _, t := range moduleTypes {
-		ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+		t.register(ctx)
 	}
 
 	// Required for SingletonModule types, even though we are not using them.
 	for _, t := range singletons {
-		ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
+		t.register(ctx)
 	}
 
-	RegisterMutatorsForBazelConversion(ctx.Context, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators)
+	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators)
 }
 
 // Register the pipeline of singletons, module types, and mutators for
 // generating build.ninja and other files for Kati, from Android.bp files.
 func (ctx *Context) Register() {
-	for _, t := range preSingletons {
-		ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
-	}
+	preSingletons.registerAll(ctx)
 
 	for _, t := range moduleTypes {
-		ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+		t.register(ctx)
 	}
 
-	for _, t := range singletons {
-		ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
-	}
+	mutators := collateGloballyRegisteredMutators()
+	mutators.registerAll(ctx)
 
-	registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
+	singletons := collateGloballyRegisteredSingletons()
+	singletons.registerAll(ctx)
+}
 
-	ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(ctx, BazelSingleton))
+func collateGloballyRegisteredSingletons() sortableComponents {
+	allSingletons := append(sortableComponents(nil), singletons...)
+	allSingletons = append(allSingletons,
+		singleton{false, "bazeldeps", BazelSingleton},
 
-	// Register phony just before makevars so it can write out its phony rules as Make rules
-	ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(ctx, phonySingletonFactory))
+		// Register phony just before makevars so it can write out its phony rules as Make rules
+		singleton{false, "phony", phonySingletonFactory},
 
-	// Register makevars after other singletons so they can export values through makevars
-	ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(ctx, makeVarsSingletonFunc))
+		// Register makevars after other singletons so they can export values through makevars
+		singleton{false, "makevars", makeVarsSingletonFunc},
 
-	// Register env and ninjadeps last so that they can track all used environment variables and
-	// Ninja file dependencies stored in the config.
-	ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(ctx, EnvSingleton))
-	ctx.RegisterSingletonType("ninjadeps", SingletonFactoryAdaptor(ctx, ninjaDepsSingletonFactory))
+		// Register env and ninjadeps last so that they can track all used environment variables and
+		// Ninja file dependencies stored in the config.
+		singleton{false, "env", EnvSingleton},
+		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
+	)
+
+	return allSingletons
 }
 
 func ModuleTypeFactories() map[string]ModuleFactory {
diff --git a/android/sandbox.go b/android/sandbox.go
index ed022fb..28e903a 100644
--- a/android/sandbox.go
+++ b/android/sandbox.go
@@ -14,29 +14,8 @@
 
 package android
 
-import (
-	"fmt"
-	"os"
-)
-
-func init() {
-	// Stash the working directory in a private variable and then change the working directory
-	// to "/", which will prevent untracked accesses to files by Go Soong plugins. The
-	// SOONG_SANDBOX_SOONG_BUILD environment variable is set by soong_ui, and is not
-	// overrideable on the command line.
-
-	orig, err := os.Getwd()
-	if err != nil {
-		panic(fmt.Errorf("failed to get working directory: %s", err))
-	}
-	absSrcDir = orig
-
-	if getenv("SOONG_SANDBOX_SOONG_BUILD") == "true" {
-		err = os.Chdir("/")
-		if err != nil {
-			panic(fmt.Errorf("failed to change working directory to '/': %s", err))
-		}
-	}
+func InitSandbox(topDir string) {
+	absSrcDir = topDir
 }
 
 // DO NOT USE THIS FUNCTION IN NEW CODE.
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index c62e76d..34b180d 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -295,18 +295,25 @@
 	recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
 		var fields []reflect.StructField
 
+		// Iterate while the list is non-empty so it can be modified in the loop.
 		for len(affectableProperties) > 0 {
 			p := affectableProperties[0]
 			if !strings.HasPrefix(affectableProperties[0], prefix) {
+				// The properties are sorted and recurse is always called with a prefix that matches
+				// the first property in the list, so if we've reached one that doesn't match the
+				// prefix we are done with this prefix.
 				break
 			}
-			affectableProperties = affectableProperties[1:]
 
 			nestedProperty := strings.TrimPrefix(p, prefix)
 			if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
 				var nestedType reflect.Type
 				nestedPrefix := nestedProperty[:i+1]
 
+				// Recurse to handle the properties with the found prefix.  This will return
+				// an updated affectableProperties with the handled entries removed from the front
+				// of the list, and the type that contains the handled entries.  The type may be
+				// nil if none of the entries matched factoryProps.
 				affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
 
 				if nestedType != nil {
@@ -325,6 +332,8 @@
 						Type: typ,
 					})
 				}
+				// The first element in the list has been handled, remove it from the list.
+				affectableProperties = affectableProperties[1:]
 			}
 		}
 
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b824c78..48cdfe7 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -235,6 +235,44 @@
 			}{},
 			want: "",
 		},
+		{
+			name:                 "nested",
+			affectableProperties: []string{"multilib.lib32.cflags"},
+			factoryProps: struct {
+				Multilib struct {
+					Lib32 struct {
+						Cflags string
+					}
+				}
+			}{},
+			want: "*struct { Multilib struct { Lib32 struct { Cflags string } } }",
+		},
+		{
+			name: "complex",
+			affectableProperties: []string{
+				"cflags",
+				"multilib.lib32.cflags",
+				"multilib.lib32.ldflags",
+				"multilib.lib64.cflags",
+				"multilib.lib64.ldflags",
+				"zflags",
+			},
+			factoryProps: struct {
+				Cflags   string
+				Multilib struct {
+					Lib32 struct {
+						Cflags  string
+						Ldflags string
+					}
+					Lib64 struct {
+						Cflags  string
+						Ldflags string
+					}
+				}
+				Zflags string
+			}{},
+			want: "*struct { Cflags string; Multilib struct { Lib32 struct { Cflags string; Ldflags string }; Lib64 struct { Cflags string; Ldflags string } }; Zflags string }",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/android/testing.go b/android/testing.go
index 5832796..dd3d607 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -20,9 +20,11 @@
 	"regexp"
 	"sort"
 	"strings"
+	"sync"
 	"testing"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func NewTestContext(config Config) *TestContext {
@@ -48,7 +50,7 @@
 	return ctx
 }
 
-var PrepareForTestWithArchMutator = FixturePreparers(
+var PrepareForTestWithArchMutator = GroupFixturePreparers(
 	// Configure architecture targets in the fixture config.
 	FixtureModifyConfig(modifyTestConfigToSupportArchMutator),
 
@@ -72,17 +74,47 @@
 	ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
 })
 
-// Prepares an integration test with build components from the android package.
-var PrepareForIntegrationTestWithAndroid = FixturePreparers(
-	// Mutators. Must match order in mutator.go.
+// Test fixture preparer that will register most java build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of java
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+//
+// The mutators in this group were chosen because they are needed by the vast majority of tests.
+var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers(
+	// Sorted alphabetically as the actual order does not matter as tests automatically enforce the
+	// correct order.
 	PrepareForTestWithArchMutator,
-	PrepareForTestWithDefaults,
 	PrepareForTestWithComponentsMutator,
-	PrepareForTestWithPrebuilts,
-	PrepareForTestWithOverrides,
-
-	// Modules
+	PrepareForTestWithDefaults,
 	PrepareForTestWithFilegroup,
+	PrepareForTestWithOverrides,
+	PrepareForTestWithPackageModule,
+	PrepareForTestWithPrebuilts,
+	PrepareForTestWithVisibility,
+)
+
+// Prepares an integration test with all build components from the android package.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithAndroid = GroupFixturePreparers(
+	PrepareForTestWithAndroidBuildComponents,
+)
+
+// Prepares a test that may be missing dependencies by setting allow_missing_dependencies to
+// true.
+var PrepareForTestWithAllowMissingDependencies = GroupFixturePreparers(
+	FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+		variables.Allow_missing_dependencies = proptools.BoolPtr(true)
+	}),
+	FixtureModifyContext(func(ctx *TestContext) {
+		ctx.SetAllowMissingDependencies(true)
+	}),
 )
 
 func NewTestArchContext(config Config) *TestContext {
@@ -96,6 +128,13 @@
 	preArch, preDeps, postDeps, finalDeps           []RegisterMutatorFunc
 	bp2buildPreArch, bp2buildDeps, bp2buildMutators []RegisterMutatorFunc
 	NameResolver                                    *NameResolver
+
+	// The list of pre-singletons and singletons registered for the test.
+	preSingletons, singletons sortableComponents
+
+	// The order in which the pre-singletons, mutators and singletons will be run in this test
+	// context; for debugging.
+	preSingletonOrder, mutatorOrder, singletonOrder []string
 }
 
 func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -140,15 +179,244 @@
 	ctx.bp2buildDeps = append(ctx.bp2buildDeps, f)
 }
 
-func (ctx *TestContext) Register() {
-	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+// registeredComponentOrder defines the order in which a sortableComponent type is registered at
+// runtime and provides support for reordering the components registered for a test in the same
+// way.
+type registeredComponentOrder struct {
+	// The name of the component type, used for error messages.
+	componentType string
 
+	// The names of the registered components in the order in which they were registered.
+	namesInOrder []string
+
+	// Maps from the component name to its position in the runtime ordering.
+	namesToIndex map[string]int
+
+	// A function that defines the order between two named components that can be used to sort a slice
+	// of component names into the same order as they appear in namesInOrder.
+	less func(string, string) bool
+}
+
+// registeredComponentOrderFromExistingOrder takes an existing slice of sortableComponents and
+// creates a registeredComponentOrder that contains a less function that can be used to sort a
+// subset of that list of names so it is in the same order as the original sortableComponents.
+func registeredComponentOrderFromExistingOrder(componentType string, existingOrder sortableComponents) registeredComponentOrder {
+	// Only the names from the existing order are needed for this so create a list of component names
+	// in the correct order.
+	namesInOrder := componentsToNames(existingOrder)
+
+	// Populate the map from name to position in the list.
+	nameToIndex := make(map[string]int)
+	for i, n := range namesInOrder {
+		nameToIndex[n] = i
+	}
+
+	// A function to use to map from a name to an index in the original order.
+	indexOf := func(name string) int {
+		index, ok := nameToIndex[name]
+		if !ok {
+			// Should never happen as tests that use components that are not known at runtime do not sort
+			// so should never use this function.
+			panic(fmt.Errorf("internal error: unknown %s %q should be one of %s", componentType, name, strings.Join(namesInOrder, ", ")))
+		}
+		return index
+	}
+
+	// The less function.
+	less := func(n1, n2 string) bool {
+		i1 := indexOf(n1)
+		i2 := indexOf(n2)
+		return i1 < i2
+	}
+
+	return registeredComponentOrder{
+		componentType: componentType,
+		namesInOrder:  namesInOrder,
+		namesToIndex:  nameToIndex,
+		less:          less,
+	}
+}
+
+// componentsToNames maps from the slice of components to a slice of their names.
+func componentsToNames(components sortableComponents) []string {
+	names := make([]string, len(components))
+	for i, c := range components {
+		names[i] = c.componentName()
+	}
+	return names
+}
+
+// enforceOrdering enforces the supplied components are in the same order as is defined in this
+// object.
+//
+// If the supplied components contains any components that are not registered at runtime, i.e. test
+// specific components, then it is impossible to sort them into an order that both matches the
+// runtime and also preserves the implicit ordering defined in the test. In that case it will not
+// sort the components, instead it will just check that the components are in the correct order.
+//
+// Otherwise, this will sort the supplied components in place.
+func (o *registeredComponentOrder) enforceOrdering(components sortableComponents) {
+	// Check to see if the list of components contains any components that are
+	// not registered at runtime.
+	var unknownComponents []string
+	testOrder := componentsToNames(components)
+	for _, name := range testOrder {
+		if _, ok := o.namesToIndex[name]; !ok {
+			unknownComponents = append(unknownComponents, name)
+			break
+		}
+	}
+
+	// If the slice contains some unknown components then it is not possible to
+	// sort them into an order that matches the runtime while also preserving the
+	// order expected from the test, so in that case don't sort just check that
+	// the order of the known mutators does match.
+	if len(unknownComponents) > 0 {
+		// Check order.
+		o.checkTestOrder(testOrder, unknownComponents)
+	} else {
+		// Sort the components.
+		sort.Slice(components, func(i, j int) bool {
+			n1 := components[i].componentName()
+			n2 := components[j].componentName()
+			return o.less(n1, n2)
+		})
+	}
+}
+
+// checkTestOrder checks that the supplied testOrder matches the one defined by this object,
+// panicking if it does not.
+func (o *registeredComponentOrder) checkTestOrder(testOrder []string, unknownComponents []string) {
+	lastMatchingTest := -1
+	matchCount := 0
+	// Take a copy of the runtime order as it is modified during the comparison.
+	runtimeOrder := append([]string(nil), o.namesInOrder...)
+	componentType := o.componentType
+	for i, j := 0, 0; i < len(testOrder) && j < len(runtimeOrder); {
+		test := testOrder[i]
+		runtime := runtimeOrder[j]
+
+		if test == runtime {
+			testOrder[i] = test + fmt.Sprintf(" <-- matched with runtime %s %d", componentType, j)
+			runtimeOrder[j] = runtime + fmt.Sprintf(" <-- matched with test %s %d", componentType, i)
+			lastMatchingTest = i
+			i += 1
+			j += 1
+			matchCount += 1
+		} else if _, ok := o.namesToIndex[test]; !ok {
+			// The test component is not registered globally so assume it is the correct place, treat it
+			// as having matched and skip it.
+			i += 1
+			matchCount += 1
+		} else {
+			// Assume that the test list is in the same order as the runtime list but the runtime list
+			// contains some components that are not present in the tests. So, skip the runtime component
+			// to try and find the next one that matches the current test component.
+			j += 1
+		}
+	}
+
+	// If every item in the test order was either test specific or matched one in the runtime then
+	// it is in the correct order. Otherwise, it was not so fail.
+	if matchCount != len(testOrder) {
+		// The test component names were not all matched with a runtime component name so there must
+		// either be a component present in the test that is not present in the runtime or they must be
+		// in the wrong order.
+		testOrder[lastMatchingTest+1] = testOrder[lastMatchingTest+1] + " <--- unmatched"
+		panic(fmt.Errorf("the tests uses test specific components %q and so cannot be automatically sorted."+
+			" Unfortunately it uses %s components in the wrong order.\n"+
+			"test order:\n    %s\n"+
+			"runtime order\n    %s\n",
+			SortedUniqueStrings(unknownComponents),
+			componentType,
+			strings.Join(testOrder, "\n    "),
+			strings.Join(runtimeOrder, "\n    ")))
+	}
+}
+
+// registrationSorter encapsulates the information needed to ensure that the test mutators are
+// registered, and thereby executed, in the same order as they are at runtime.
+//
+// It MUST be populated lazily AFTER all package initialization has been done otherwise it will
+// only define the order for a subset of all the registered build components that are available for
+// the packages being tested.
+//
+// e.g if this is initialized during say the cc package initialization then any tests run in the
+// java package will not sort build components registered by the java package's init() functions.
+type registrationSorter struct {
+	// Used to ensure that this is only created once.
+	once sync.Once
+
+	// The order of pre-singletons
+	preSingletonOrder registeredComponentOrder
+
+	// The order of mutators
+	mutatorOrder registeredComponentOrder
+
+	// The order of singletons
+	singletonOrder registeredComponentOrder
+}
+
+// populate initializes this structure from globally registered build components.
+//
+// Only the first call has any effect.
+func (s *registrationSorter) populate() {
+	s.once.Do(func() {
+		// Create an ordering from the globally registered pre-singletons.
+		s.preSingletonOrder = registeredComponentOrderFromExistingOrder("pre-singleton", preSingletons)
+
+		// Created an ordering from the globally registered mutators.
+		globallyRegisteredMutators := collateGloballyRegisteredMutators()
+		s.mutatorOrder = registeredComponentOrderFromExistingOrder("mutator", globallyRegisteredMutators)
+
+		// Create an ordering from the globally registered singletons.
+		globallyRegisteredSingletons := collateGloballyRegisteredSingletons()
+		s.singletonOrder = registeredComponentOrderFromExistingOrder("singleton", globallyRegisteredSingletons)
+	})
+}
+
+// Provides support for enforcing the same order in which build components are registered globally
+// to the order in which they are registered during tests.
+//
+// MUST only be accessed via the globallyRegisteredComponentsOrder func.
+var globalRegistrationSorter registrationSorter
+
+// globallyRegisteredComponentsOrder returns the globalRegistrationSorter after ensuring it is
+// correctly populated.
+func globallyRegisteredComponentsOrder() *registrationSorter {
+	globalRegistrationSorter.populate()
+	return &globalRegistrationSorter
+}
+
+func (ctx *TestContext) Register() {
+	globalOrder := globallyRegisteredComponentsOrder()
+
+	// Ensure that the pre-singletons used in the test are in the same order as they are used at
+	// runtime.
+	globalOrder.preSingletonOrder.enforceOrdering(ctx.preSingletons)
+	ctx.preSingletons.registerAll(ctx.Context)
+
+	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+	// Ensure that the mutators used in the test are in the same order as they are used at runtime.
+	globalOrder.mutatorOrder.enforceOrdering(mutators)
+	mutators.registerAll(ctx.Context)
+
+	// Register the env singleton with this context before sorting.
 	ctx.RegisterSingletonType("env", EnvSingleton)
+
+	// Ensure that the singletons used in the test are in the same order as they are used at runtime.
+	globalOrder.singletonOrder.enforceOrdering(ctx.singletons)
+	ctx.singletons.registerAll(ctx.Context)
+
+	// Save the sorted components order away to make them easy to access while debugging.
+	ctx.preSingletonOrder = componentsToNames(preSingletons)
+	ctx.mutatorOrder = componentsToNames(mutators)
+	ctx.singletonOrder = componentsToNames(singletons)
 }
 
 // RegisterForBazelConversion prepares a test context for bp2build conversion.
 func (ctx *TestContext) RegisterForBazelConversion() {
-	RegisterMutatorsForBazelConversion(ctx.Context.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators)
+	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators)
 }
 
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
@@ -174,11 +442,11 @@
 }
 
 func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
-	ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory))
 }
 
 func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
-	ctx.Context.RegisterPreSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
+	ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory))
 }
 
 func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
@@ -456,12 +724,15 @@
 	}
 }
 
-func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
+// Fail if no errors that matched the regular expression were found.
+//
+// Returns true if a matching error was found, false otherwise.
+func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) bool {
 	t.Helper()
 
 	matcher, err := regexp.Compile(pattern)
 	if err != nil {
-		t.Errorf("failed to compile regular expression %q because %s", pattern, err)
+		t.Fatalf("failed to compile regular expression %q because %s", pattern, err)
 	}
 
 	found := false
@@ -477,6 +748,8 @@
 			t.Errorf("errs[%d] = %q", i, err)
 		}
 	}
+
+	return found
 }
 
 func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
@@ -497,9 +770,9 @@
 			for i, err := range errs {
 				t.Errorf("errs[%d] = %s", i, err)
 			}
+			t.FailNow()
 		}
 	}
-
 }
 
 func SetKatiEnabledForTests(config Config) {
@@ -545,10 +818,6 @@
 		return "<nil path>"
 	}
 	p := path.String()
-	// Allow absolute paths to /dev/
-	if strings.HasPrefix(p, "/dev/") {
-		return p
-	}
 	if w, ok := path.(WritablePath); ok {
 		rel, err := filepath.Rel(w.buildDir(), p)
 		if err != nil {
diff --git a/android/variable.go b/android/variable.go
index dd000ad..be12a0a 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -24,11 +24,17 @@
 )
 
 func init() {
-	PreDepsMutators(func(ctx RegisterMutatorsContext) {
+	registerVariableBuildComponents(InitRegistrationContext)
+}
+
+func registerVariableBuildComponents(ctx RegistrationContext) {
+	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
 		ctx.BottomUp("variable", VariableMutator).Parallel()
 	})
 }
 
+var PrepareForTestWithVariables = FixtureRegisterWithContext(registerVariableBuildComponents)
+
 type variableProperties struct {
 	Product_variables struct {
 		Platform_sdk_version struct {
diff --git a/android/visibility.go b/android/visibility.go
index 7eac471..5d1be6b 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -202,6 +202,15 @@
 	ExcludeFromVisibilityEnforcement()
 }
 
+// The visibility mutators.
+var PrepareForTestWithVisibility = FixtureRegisterWithContext(registerVisibilityMutators)
+
+func registerVisibilityMutators(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+}
+
 // The rule checker needs to be registered before defaults expansion to correctly check that
 // //visibility:xxx isn't combined with other packages in the same list in any one module.
 func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 87a295e..eb4071e 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -9,13 +9,13 @@
 
 var visibilityTests = []struct {
 	name                string
-	fs                  map[string][]byte
+	fs                  MockFS
 	expectedErrors      []string
 	effectiveVisibility map[qualifiedModuleName][]string
 }{
 	{
 		name: "invalid visibility: empty list",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -26,7 +26,7 @@
 	},
 	{
 		name: "invalid visibility: empty rule",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -37,7 +37,7 @@
 	},
 	{
 		name: "invalid visibility: unqualified",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -48,7 +48,7 @@
 	},
 	{
 		name: "invalid visibility: empty namespace",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -59,7 +59,7 @@
 	},
 	{
 		name: "invalid visibility: empty module",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -70,7 +70,7 @@
 	},
 	{
 		name: "invalid visibility: empty namespace and module",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -81,7 +81,7 @@
 	},
 	{
 		name: "//visibility:unknown",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -92,7 +92,7 @@
 	},
 	{
 		name: "//visibility:xxx mixed",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -113,7 +113,7 @@
 	},
 	{
 		name: "//visibility:legacy_public",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -129,7 +129,7 @@
 		// Verify that //visibility:public will allow the module to be referenced from anywhere, e.g.
 		// the current directory, a nested directory and a directory in a separate tree.
 		name: "//visibility:public",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -156,7 +156,7 @@
 		// Verify that //visibility:private allows the module to be referenced from the current
 		// directory only.
 		name: "//visibility:private",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -188,7 +188,7 @@
 	{
 		// Verify that :__pkg__ allows the module to be referenced from the current directory only.
 		name: ":__pkg__",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -221,7 +221,7 @@
 		// Verify that //top/nested allows the module to be referenced from the current directory and
 		// the top/nested directory only, not a subdirectory of top/nested and not peak directory.
 		name: "//top/nested",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -259,7 +259,7 @@
 		// Verify that :__subpackages__ allows the module to be referenced from the current directory
 		// and sub directories but nowhere else.
 		name: ":__subpackages__",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -290,7 +290,7 @@
 		// Verify that //top/nested:__subpackages__ allows the module to be referenced from the current
 		// directory and sub directories but nowhere else.
 		name: "//top/nested:__subpackages__",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -321,7 +321,7 @@
 		// Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from
 		// the current directory, top/nested and peak and all its subpackages.
 		name: `["//top/nested", "//peak:__subpackages__"]`,
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -347,7 +347,7 @@
 	{
 		// Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__
 		name: `//vendor`,
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -381,7 +381,7 @@
 	{
 		// Check that visibility is the union of the defaults modules.
 		name: "defaults union, basic",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -419,7 +419,7 @@
 	},
 	{
 		name: "defaults union, multiple defaults",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
@@ -460,7 +460,7 @@
 	},
 	{
 		name: "//visibility:public mixed with other in defaults",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -478,7 +478,7 @@
 	},
 	{
 		name: "//visibility:public overriding defaults",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -501,7 +501,7 @@
 	},
 	{
 		name: "//visibility:public mixed with other from different defaults 1",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
@@ -524,7 +524,7 @@
 	},
 	{
 		name: "//visibility:public mixed with other from different defaults 2",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
@@ -547,7 +547,7 @@
 	},
 	{
 		name: "//visibility:private in defaults",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -581,7 +581,7 @@
 	},
 	{
 		name: "//visibility:private mixed with other in defaults",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -599,7 +599,7 @@
 	},
 	{
 		name: "//visibility:private overriding defaults",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -618,7 +618,7 @@
 	},
 	{
 		name: "//visibility:private in defaults overridden",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -637,7 +637,7 @@
 	},
 	{
 		name: "//visibility:private override //visibility:public",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -655,7 +655,7 @@
 	},
 	{
 		name: "//visibility:public override //visibility:private",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -673,7 +673,7 @@
 	},
 	{
 		name: "//visibility:override must be first in the list",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_library {
 					name: "libexample",
@@ -686,7 +686,7 @@
 	},
 	{
 		name: "//visibility:override discards //visibility:private",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -707,7 +707,7 @@
 	},
 	{
 		name: "//visibility:override discards //visibility:public",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -736,7 +736,7 @@
 	},
 	{
 		name: "//visibility:override discards defaults supplied rules",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -765,7 +765,7 @@
 	},
 	{
 		name: "//visibility:override can override //visibility:public with //visibility:private",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -788,7 +788,7 @@
 	},
 	{
 		name: "//visibility:override can override //visibility:private with //visibility:public",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
@@ -808,7 +808,7 @@
 	},
 	{
 		name: "//visibility:private mixed with itself",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
@@ -838,7 +838,7 @@
 	// Defaults module's defaults_visibility tests
 	{
 		name: "defaults_visibility invalid",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_defaults {
 					name: "top_defaults",
@@ -851,7 +851,7 @@
 	},
 	{
 		name: "defaults_visibility overrides package default",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
@@ -871,7 +871,7 @@
 	// Package default_visibility tests
 	{
 		name: "package default_visibility property is checked",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//visibility:invalid"],
@@ -882,7 +882,7 @@
 	{
 		// This test relies on the default visibility being legacy_public.
 		name: "package default_visibility property used when no visibility specified",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
@@ -904,7 +904,7 @@
 	},
 	{
 		name: "package default_visibility public does not override visibility private",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//visibility:public"],
@@ -927,7 +927,7 @@
 	},
 	{
 		name: "package default_visibility private does not override visibility public",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
@@ -946,7 +946,7 @@
 	},
 	{
 		name: "package default_visibility :__subpackages__",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: [":__subpackages__"],
@@ -973,7 +973,7 @@
 	},
 	{
 		name: "package default_visibility inherited to subpackages",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//outsider"],
@@ -1001,7 +1001,7 @@
 	},
 	{
 		name: "package default_visibility inherited to subpackages",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
@@ -1031,7 +1031,7 @@
 	},
 	{
 		name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"prebuilts/Blueprints": []byte(`
 				prebuilt {
 					name: "module",
@@ -1053,7 +1053,7 @@
 	},
 	{
 		name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"prebuilts/Blueprints": []byte(`
 				prebuilt {
 					name: "module",
@@ -1076,7 +1076,7 @@
 	},
 	{
 		name: "ensure visibility properties are checked for correctness",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_parent {
 					name: "parent",
@@ -1093,7 +1093,7 @@
 	},
 	{
 		name: "invalid visibility added to child detected during gather phase",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_parent {
 					name: "parent",
@@ -1115,7 +1115,7 @@
 	},
 	{
 		name: "automatic visibility inheritance enabled",
-		fs: map[string][]byte{
+		fs: MockFS{
 			"top/Blueprints": []byte(`
 				mock_parent {
 					name: "parent",
@@ -1142,55 +1142,44 @@
 func TestVisibility(t *testing.T) {
 	for _, test := range visibilityTests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx, errs := testVisibility(buildDir, test.fs)
+			result := emptyTestFixtureFactory.Extend(
+				// General preparers in alphabetical order as test infrastructure will enforce correct
+				// registration order.
+				PrepareForTestWithArchMutator,
+				PrepareForTestWithDefaults,
+				PrepareForTestWithOverrides,
+				PrepareForTestWithPackageModule,
+				PrepareForTestWithPrebuilts,
+				PrepareForTestWithVisibility,
 
-			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+				// Additional test specific preparers.
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("mock_library", newMockLibraryModule)
+					ctx.RegisterModuleType("mock_parent", newMockParentFactory)
+					ctx.RegisterModuleType("mock_defaults", defaultsFactory)
+				}),
+				prepareForTestWithFakePrebuiltModules,
+				// Add additional files to the mock filesystem
+				test.fs.AddToFixture(),
+			).
+				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+				RunTest(t)
 
 			if test.effectiveVisibility != nil {
-				checkEffectiveVisibility(t, ctx, test.effectiveVisibility)
+				checkEffectiveVisibility(result, test.effectiveVisibility)
 			}
 		})
 	}
 }
 
-func checkEffectiveVisibility(t *testing.T, ctx *TestContext, effectiveVisibility map[qualifiedModuleName][]string) {
+func checkEffectiveVisibility(result *TestResult, effectiveVisibility map[qualifiedModuleName][]string) {
 	for moduleName, expectedRules := range effectiveVisibility {
-		rule := effectiveVisibilityRules(ctx.config, moduleName)
+		rule := effectiveVisibilityRules(result.Config, moduleName)
 		stringRules := rule.Strings()
-		if !reflect.DeepEqual(expectedRules, stringRules) {
-			t.Errorf("effective rules mismatch: expected %q, found %q", expectedRules, stringRules)
-		}
+		result.AssertDeepEquals("effective rules mismatch", expectedRules, stringRules)
 	}
 }
 
-func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) {
-
-	// Create a new config per test as visibility information is stored in the config.
-	config := TestArchConfig(buildDir, nil, "", fs)
-
-	ctx := NewTestArchContext(config)
-	ctx.RegisterModuleType("mock_library", newMockLibraryModule)
-	ctx.RegisterModuleType("mock_parent", newMockParentFactory)
-	ctx.RegisterModuleType("mock_defaults", defaultsFactory)
-
-	// Order of the following method calls is significant.
-	RegisterPackageBuildComponents(ctx)
-	registerTestPrebuiltBuildComponents(ctx)
-	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
-	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
-	ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
-	ctx.Register()
-
-	_, errs := ctx.ParseBlueprintsFiles(".")
-	if len(errs) > 0 {
-		return ctx, errs
-	}
-
-	_, errs = ctx.PrepareBuildActions(config)
-	return ctx, errs
-}
-
 type mockLibraryProperties struct {
 	Deps []string
 }
diff --git a/android/writedocs.go b/android/writedocs.go
index 91c2318..67b9aa3 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -34,10 +34,12 @@
 type docsSingleton struct{}
 
 func primaryBuilderPath(ctx SingletonContext) Path {
-	primaryBuilder, err := filepath.Rel(ctx.Config().BuildDir(), os.Args[0])
+	buildDir := absolutePath(ctx.Config().BuildDir())
+	binary := absolutePath(os.Args[0])
+	primaryBuilder, err := filepath.Rel(buildDir, binary)
 	if err != nil {
-		ctx.Errorf("path to primary builder %q is not in build dir %q",
-			os.Args[0], ctx.Config().BuildDir())
+		ctx.Errorf("path to primary builder %q is not in build dir %q (%q)",
+			os.Args[0], ctx.Config().BuildDir(), err)
 	}
 
 	return PathForOutput(ctx, primaryBuilder)
@@ -65,7 +67,9 @@
 	soongDocs := ctx.Rule(pctx, "soongDocs",
 		blueprint.RuleParams{
 			Command: fmt.Sprintf("rm -f ${outDir}/* && %s --soong_docs %s %s",
-				primaryBuilder.String(), docsFile.String(), strings.Join(os.Args[1:], " ")),
+				primaryBuilder.String(),
+				docsFile.String(),
+				"\""+strings.Join(os.Args[1:], "\" \"")+"\""),
 			CommandDeps: []string{primaryBuilder.String()},
 			Description: fmt.Sprintf("%s docs $out", primaryBuilder.Base()),
 		},
diff --git a/apex/Android.bp b/apex/Android.bp
index 8a2edeb..1890b89 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -25,6 +25,7 @@
         "deapexer.go",
         "key.go",
         "prebuilt.go",
+        "testing.go",
         "vndk.go",
     ],
     testSrcs: [
diff --git a/apex/OWNERS b/apex/OWNERS
index 793f3ed..fee739b 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,4 +1,4 @@
 per-file * = jiyong@google.com
 
 per-file allowed_deps.txt = set noparent
-per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,omakoto@google.com,jham@google.com
+per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,jham@google.com
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index ca02b30..154b9aa 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -163,11 +163,14 @@
 crtend_so(minSdkVersion:16)
 crtend_so(minSdkVersion:apex_inherit)
 datastallprotosnano(minSdkVersion:29)
+derive_classpath(minSdkVersion:30)
 derive_sdk(minSdkVersion:30)
 derive_sdk(minSdkVersion:current)
 derive_sdk_prefer32(minSdkVersion:30)
 derive_sdk_prefer32(minSdkVersion:current)
+dnsresolver_aidl_interface-lateststable-ndk_platform(minSdkVersion:29)
 dnsresolver_aidl_interface-unstable-ndk_platform(minSdkVersion:29)
+dnsresolver_aidl_interface-V7-ndk_platform(minSdkVersion:29)
 dnsresolver_aidl_interface-V8-ndk_platform(minSdkVersion:29)
 DocumentsUI-res-lib(minSdkVersion:29)
 exoplayer2-extractor(minSdkVersion:16)
@@ -192,7 +195,7 @@
 GoogleCellBroadcastApp(minSdkVersion:29)
 GoogleCellBroadcastServiceModule(minSdkVersion:29)
 GoogleExtServices(minSdkVersion:current)
-GooglePermissionController(minSdkVersion:28)
+GooglePermissionController(minSdkVersion:30)
 guava(minSdkVersion:current)
 gwp_asan_headers(minSdkVersion:(no version))
 i18n.module.public.api.stubs(minSdkVersion:(no version))
@@ -306,6 +309,7 @@
 libcutils(minSdkVersion:29)
 libcutils_headers(minSdkVersion:29)
 libcutils_sockets(minSdkVersion:29)
+libderive_classpath(minSdkVersion:30)
 libderive_sdk(minSdkVersion:30)
 libdiagnose_usb(minSdkVersion:(no version))
 libdl(minSdkVersion:(no version))
@@ -361,6 +365,7 @@
 libmedia_headers(minSdkVersion:29)
 libmedia_helper_headers(minSdkVersion:29)
 libmedia_midiiowrapper(minSdkVersion:29)
+libmediaparser-jni(minSdkVersion:29)
 libmidiextractor(minSdkVersion:29)
 libminijail(minSdkVersion:29)
 libminijail_gen_constants(minSdkVersion:(no version))
@@ -478,6 +483,7 @@
 MediaProvider(minSdkVersion:30)
 mediaswcodec(minSdkVersion:29)
 metrics-constants-protos(minSdkVersion:29)
+modules-annotation-minsdk(minSdkVersion:29)
 modules-utils-build(minSdkVersion:29)
 modules-utils-build_system(minSdkVersion:29)
 modules-utils-os(minSdkVersion:30)
@@ -501,8 +507,10 @@
 netd_aidl_interface-V5-java(minSdkVersion:29)
 netd_aidl_interface-V6-java(minSdkVersion:29)
 netd_event_listener_interface-java(minSdkVersion:29)
+netd_event_listener_interface-lateststable-ndk_platform(minSdkVersion:29)
 netd_event_listener_interface-ndk_platform(minSdkVersion:29)
 netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
+netd_event_listener_interface-V1-ndk_platform(minSdkVersion:29)
 netd_event_listener_interface-V2-ndk_platform(minSdkVersion:29)
 netlink-client(minSdkVersion:29)
 networkstack-aidl-interfaces-unstable-java(minSdkVersion:29)
@@ -526,7 +534,7 @@
 no_op(minSdkVersion:current)
 note_memtag_heap_async(minSdkVersion:16)
 note_memtag_heap_sync(minSdkVersion:16)
-PermissionController(minSdkVersion:28)
+PermissionController(minSdkVersion:30)
 permissioncontroller-statsd(minSdkVersion:current)
 philox_random(minSdkVersion:(no version))
 philox_random_headers(minSdkVersion:(no version))
@@ -635,6 +643,7 @@
 Tethering(minSdkVersion:current)
 TetheringApiCurrentLib(minSdkVersion:30)
 TetheringApiCurrentLib(minSdkVersion:current)
+TetheringGoogle(minSdkVersion:30)
 TetheringGoogle(minSdkVersion:current)
 textclassifier-statsd(minSdkVersion:current)
 TextClassifierNotificationLibNoManifest(minSdkVersion:29)
diff --git a/apex/apex.go b/apex/apex.go
index 662bbbd..e5b5c92 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -39,23 +39,25 @@
 )
 
 func init() {
-	android.RegisterModuleType("apex", BundleFactory)
-	android.RegisterModuleType("apex_test", testApexBundleFactory)
-	android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
-	android.RegisterModuleType("apex_defaults", defaultsFactory)
-	android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
-	android.RegisterModuleType("override_apex", overrideApexFactory)
-	android.RegisterModuleType("apex_set", apexSetFactory)
+	registerApexBuildComponents(android.InitRegistrationContext)
+}
 
-	android.PreDepsMutators(RegisterPreDepsMutators)
-	android.PostDepsMutators(RegisterPostDepsMutators)
+func registerApexBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("apex", BundleFactory)
+	ctx.RegisterModuleType("apex_test", testApexBundleFactory)
+	ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
+	ctx.RegisterModuleType("apex_defaults", defaultsFactory)
+	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+	ctx.RegisterModuleType("override_apex", overrideApexFactory)
+	ctx.RegisterModuleType("apex_set", apexSetFactory)
+
+	ctx.PreDepsMutators(RegisterPreDepsMutators)
+	ctx.PostDepsMutators(RegisterPostDepsMutators)
 }
 
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
 	ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
 	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
-	ctx.BottomUp("prebuilt_apex_select_source", prebuiltSelectSourceMutator).Parallel()
-	ctx.BottomUp("deapexer_select_source", deapexerSelectSourceMutator).Parallel()
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
@@ -123,7 +125,7 @@
 	// Whether this APEX is considered updatable or not. When set to true, this will enforce
 	// additional rules for making sure that the APEX is truly updatable. To be updatable,
 	// min_sdk_version should be set as well. This will also disable the size optimizations like
-	// symlinking to the system libs. Default is false.
+	// symlinking to the system libs. Default is true.
 	Updatable *bool
 
 	// Whether this APEX is installable to one of the partitions like system, vendor, etc.
@@ -972,6 +974,10 @@
 	// If any of the dep is not available to platform, this module is also considered as being
 	// not available to platform even if it has "//apex_available:platform"
 	mctx.VisitDirectDeps(func(child android.Module) {
+		depTag := mctx.OtherModuleDependencyTag(child)
+		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+			return
+		}
 		if !am.DepIsInSameApex(mctx, child) {
 			// if the dependency crosses apex boundary, don't consider it
 			return
@@ -1234,7 +1240,7 @@
 
 // Implements android.ApexBudleDepsInfoIntf
 func (a *apexBundle) Updatable() bool {
-	return proptools.Bool(a.properties.Updatable)
+	return proptools.BoolDefault(a.properties.Updatable, true)
 }
 
 // getCertString returns the name of the cert that should be used to sign this APEX. This is
@@ -2246,8 +2252,10 @@
 		if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
 			return true
 		}
-		ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s",
-			fromName, toName, ctx.GetPathString(true))
+		ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+
+			"\n\nDependency path:%s\n\n"+
+			"Consider adding %q to 'apex_available' property of %q",
+			fromName, toName, ctx.GetPathString(true), apexName, toName)
 		// Visit this module's dependencies to check and report any issues with their availability.
 		return true
 	})
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 3f56047..3fde144 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -19,6 +19,7 @@
 	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 	"reflect"
 	"regexp"
 	"sort"
@@ -66,14 +67,14 @@
 	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
 }
 
-func testApex(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+func testApex(t *testing.T, bp string, handlers ...testCustomizer) *android.TestContext {
 	t.Helper()
 	ctx, config := testApexContext(t, bp, handlers...)
 	_, errs := ctx.ParseBlueprintsFiles(".")
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
 	android.FailIfErrored(t, errs)
-	return ctx, config
+	return ctx
 }
 
 type testCustomizer func(fs map[string][]byte, config android.Config)
@@ -126,34 +127,42 @@
 	config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
 }
 
-func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
-	bp = bp + `
+var apexFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	// General preparers in alphabetical order as test infrastructure will enforce correct
+	// registration order.
+	android.PrepareForTestWithAndroidBuildComponents,
+	bpf.PrepareForTestWithBpf,
+	cc.PrepareForTestWithCcBuildComponents,
+	java.PrepareForTestWithJavaDefaultModules,
+	prebuilt_etc.PrepareForTestWithPrebuiltEtc,
+	rust.PrepareForTestWithRustDefaultModules,
+	sh.PrepareForTestWithShBuildComponents,
+
+	PrepareForTestWithApexBuildComponents,
+
+	// Additional apex test specific preparers.
+	android.FixtureAddTextFile("system/sepolicy/Android.bp", `
 		filegroup {
 			name: "myapex-file_contexts",
 			srcs: [
-				"system/sepolicy/apex/myapex-file_contexts",
+				"apex/myapex-file_contexts",
 			],
 		}
-	`
-
-	bp = bp + cc.GatherRequiredDepsForTest(android.Android)
-
-	bp = bp + rust.GatherRequiredDepsForTest()
-
-	bp = bp + java.GatherRequiredDepsForTest()
-
-	fs := map[string][]byte{
-		"a.java":                                              nil,
-		"PrebuiltAppFoo.apk":                                  nil,
-		"PrebuiltAppFooPriv.apk":                              nil,
-		"build/make/target/product/security":                  nil,
-		"apex_manifest.json":                                  nil,
-		"AndroidManifest.xml":                                 nil,
-		"system/sepolicy/apex/myapex-file_contexts":           nil,
-		"system/sepolicy/apex/myapex.updatable-file_contexts": nil,
-		"system/sepolicy/apex/myapex2-file_contexts":          nil,
-		"system/sepolicy/apex/otherapex-file_contexts":        nil,
-		"system/sepolicy/apex/com.android.vndk-file_contexts": nil,
+	`),
+	android.FixtureMergeMockFs(android.MockFS{
+		"a.java":                                                      nil,
+		"PrebuiltAppFoo.apk":                                          nil,
+		"PrebuiltAppFooPriv.apk":                                      nil,
+		"build/make/target/product/security":                          nil,
+		"apex_manifest.json":                                          nil,
+		"AndroidManifest.xml":                                         nil,
+		"system/sepolicy/apex/myapex-file_contexts":                   nil,
+		"system/sepolicy/apex/myapex.updatable-file_contexts":         nil,
+		"system/sepolicy/apex/myapex2-file_contexts":                  nil,
+		"system/sepolicy/apex/otherapex-file_contexts":                nil,
+		"system/sepolicy/apex/com.android.vndk-file_contexts":         nil,
+		"system/sepolicy/apex/com.android.vndk.current-file_contexts": nil,
 		"mylib.cpp":                                  nil,
 		"mytest.cpp":                                 nil,
 		"mytest1.cpp":                                nil,
@@ -191,6 +200,88 @@
 		"AppSet.apks":                                nil,
 		"foo.rs":                                     nil,
 		"libfoo.jar":                                 nil,
+		"libbar.jar":                                 nil,
+	},
+	),
+
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.DeviceVndkVersion = proptools.StringPtr("current")
+		variables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
+		variables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
+		variables.Platform_sdk_codename = proptools.StringPtr("Q")
+		variables.Platform_sdk_final = proptools.BoolPtr(false)
+		variables.Platform_version_active_codenames = []string{"Q"}
+		variables.Platform_vndk_version = proptools.StringPtr("VER")
+	}),
+)
+
+func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+	bp = bp + `
+		filegroup {
+			name: "myapex-file_contexts",
+			srcs: [
+				"system/sepolicy/apex/myapex-file_contexts",
+			],
+		}
+	`
+
+	bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+
+	bp = bp + rust.GatherRequiredDepsForTest()
+
+	bp = bp + java.GatherRequiredDepsForTest()
+
+	fs := map[string][]byte{
+		"a.java":                                                      nil,
+		"PrebuiltAppFoo.apk":                                          nil,
+		"PrebuiltAppFooPriv.apk":                                      nil,
+		"build/make/target/product/security":                          nil,
+		"apex_manifest.json":                                          nil,
+		"AndroidManifest.xml":                                         nil,
+		"system/sepolicy/apex/myapex-file_contexts":                   nil,
+		"system/sepolicy/apex/myapex.updatable-file_contexts":         nil,
+		"system/sepolicy/apex/myapex2-file_contexts":                  nil,
+		"system/sepolicy/apex/otherapex-file_contexts":                nil,
+		"system/sepolicy/apex/com.android.vndk-file_contexts":         nil,
+		"system/sepolicy/apex/com.android.vndk.current-file_contexts": nil,
+		"mylib.cpp":                                  nil,
+		"mytest.cpp":                                 nil,
+		"mytest1.cpp":                                nil,
+		"mytest2.cpp":                                nil,
+		"mytest3.cpp":                                nil,
+		"myprebuilt":                                 nil,
+		"my_include":                                 nil,
+		"foo/bar/MyClass.java":                       nil,
+		"prebuilt.jar":                               nil,
+		"prebuilt.so":                                nil,
+		"vendor/foo/devkeys/test.x509.pem":           nil,
+		"vendor/foo/devkeys/test.pk8":                nil,
+		"testkey.x509.pem":                           nil,
+		"testkey.pk8":                                nil,
+		"testkey.override.x509.pem":                  nil,
+		"testkey.override.pk8":                       nil,
+		"vendor/foo/devkeys/testkey.avbpubkey":       nil,
+		"vendor/foo/devkeys/testkey.pem":             nil,
+		"NOTICE":                                     nil,
+		"custom_notice":                              nil,
+		"custom_notice_for_static_lib":               nil,
+		"testkey2.avbpubkey":                         nil,
+		"testkey2.pem":                               nil,
+		"myapex-arm64.apex":                          nil,
+		"myapex-arm.apex":                            nil,
+		"myapex.apks":                                nil,
+		"frameworks/base/api/current.txt":            nil,
+		"framework/aidl/a.aidl":                      nil,
+		"build/make/core/proguard.flags":             nil,
+		"build/make/core/proguard_basic_keeps.flags": nil,
+		"dummy.txt":                                  nil,
+		"baz":                                        nil,
+		"bar/baz":                                    nil,
+		"testdata/baz":                               nil,
+		"AppSet.apks":                                nil,
+		"foo.rs":                                     nil,
+		"libfoo.jar":                                 nil,
+		"libbar.jar":                                 nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -226,18 +317,11 @@
 	android.RegisterPackageBuildComponents(ctx)
 	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
 
-	ctx.RegisterModuleType("apex", BundleFactory)
-	ctx.RegisterModuleType("apex_test", testApexBundleFactory)
-	ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
-	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.RegisterModuleType("apex_defaults", defaultsFactory)
-	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
-	ctx.RegisterModuleType("override_apex", overrideApexFactory)
-	ctx.RegisterModuleType("apex_set", apexSetFactory)
+	registerApexBuildComponents(ctx)
+	registerApexKeyBuildComponents(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 
 	android.RegisterPrebuiltMutators(ctx)
 
@@ -246,6 +330,10 @@
 	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
+	// These must come after prebuilts and visibility rules to match runtime.
+	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
+
+	// These must come after override rules to match the runtime.
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	rust.RegisterRequiredBuildComponentsForTest(ctx)
 	java.RegisterRequiredBuildComponentsForTest(ctx)
@@ -257,12 +345,8 @@
 	ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
 	ctx.RegisterModuleType("bpf", bpf.BpfFactory)
 
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-
 	ctx.Register()
 
 	return ctx, config
@@ -354,7 +438,7 @@
 
 // Minimal test
 func TestBasicApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
 			manifest: ":myapex.manifest",
@@ -375,6 +459,7 @@
 				"myjar",
 				"myjar_dex",
 			],
+			updatable: false,
 		}
 
 		apex {
@@ -475,6 +560,7 @@
 			binaries: ["foo"],
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		cc_library_shared {
@@ -658,7 +744,7 @@
 }
 
 func TestDefaults(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
 			key: "myapex.key",
@@ -668,6 +754,7 @@
 			apps: ["AppFoo"],
 			rros: ["rro"],
 			bpfs: ["bpf"],
+			updatable: false,
 		}
 
 		prebuilt_etc {
@@ -732,10 +819,11 @@
 }
 
 func TestApexManifest(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 
 		apex_key {
@@ -753,12 +841,13 @@
 }
 
 func TestBasicZipApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			payload_type: "zip",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -803,11 +892,12 @@
 }
 
 func TestApexWithStubs(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib3"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -897,7 +987,7 @@
 
 func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
 	t.Parallel()
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -970,8 +1060,8 @@
 
 	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"]
 
-	// Ensure that mylib is linking with the version 29 stubs for mylib2
-	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_29/mylib2.so")
+	// Ensure that mylib is linking with the latest version of stub for mylib2
+	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")
 
@@ -1004,7 +1094,7 @@
 	//                                  |
 	//   <platform>                     |
 	//      libplatform ----------------'
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1055,11 +1145,11 @@
 		config.TestProductVariables.Platform_version_active_codenames = []string{"Z"}
 	})
 
-	// Ensure that mylib from myapex is built against "min_sdk_version" stub ("Z"), which is non-final
+	// Ensure that mylib from myapex is built against the latest stub (current)
 	mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
-	ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=9000 ")
+	ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=10000 ")
 	mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
-	ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_Z/libstub.so ")
+	ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ")
 
 	// Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex
 	libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
@@ -1069,11 +1159,12 @@
 }
 
 func TestApexWithExplicitStubsDependency(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex2",
 			key: "myapex2.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1164,11 +1255,12 @@
 			    |
 			    `------> libbar
 	*/
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1225,12 +1317,13 @@
 }
 
 func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
-	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+	ctx := testApex(t, "", func(fs map[string][]byte, config android.Config) {
 		bp := `
 		apex {
 			name: "com.android.runtime",
 			key: "com.android.runtime.key",
 			native_shared_libs: ["libc"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1288,12 +1381,13 @@
 }
 
 func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
-	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+	ctx := testApex(t, "", func(fs map[string][]byte, config android.Config) {
 		bp := `
 		apex {
 			name: "com.android.runtime",
 			key: "com.android.runtime.key",
 			native_shared_libs: ["libc"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1357,28 +1451,29 @@
 		shouldNotLink []string
 	}{
 		{
-			name:          "should link to the latest",
+			name:          "unspecified version links to the latest",
 			minSdkVersion: "",
 			apexVariant:   "apex10000",
 			shouldLink:    "30",
 			shouldNotLink: []string{"29"},
 		},
 		{
-			name:          "should link to llndk#29",
+			name:          "always use the latest",
 			minSdkVersion: "min_sdk_version: \"29\",",
 			apexVariant:   "apex29",
-			shouldLink:    "29",
-			shouldNotLink: []string{"30"},
+			shouldLink:    "30",
+			shouldNotLink: []string{"29"},
 		},
 	}
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
-			ctx, _ := testApex(t, `
+			ctx := testApex(t, `
 			apex {
 				name: "myapex",
 				key: "myapex.key",
 				use_vendor: true,
 				native_shared_libs: ["mylib"],
+				updatable: false,
 				`+tc.minSdkVersion+`
 			}
 
@@ -1439,11 +1534,12 @@
 }
 
 func TestApexWithSystemLibsStubs(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1530,11 +1626,11 @@
 }
 
 func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) {
-	// there are three links between liba --> libz
-	// 1) myapex -> libx -> liba -> libz    : this should be #29 link, but fallback to #28
+	// there are three links between liba --> libz.
+	// 1) myapex -> libx -> liba -> libz    : this should be #30 link
 	// 2) otherapex -> liby -> liba -> libz : this should be #30 link
 	// 3) (platform) -> liba -> libz        : this should be non-stub link
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1605,9 +1701,9 @@
 	}
 	// platform liba is linked to non-stub version
 	expectLink("liba", "shared", "libz", "shared")
-	// liba in myapex is linked to #28
-	expectLink("liba", "shared_apex29", "libz", "shared_28")
-	expectNoLink("liba", "shared_apex29", "libz", "shared_30")
+	// liba in myapex is linked to #30
+	expectLink("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")
@@ -1616,7 +1712,7 @@
 }
 
 func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1665,11 +1761,12 @@
 }
 
 func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["libx"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1710,11 +1807,12 @@
 }
 
 func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["libx"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1757,7 +1855,7 @@
 }
 
 func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1796,7 +1894,7 @@
 }
 
 func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -1825,41 +1923,6 @@
 	ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
 }
 
-func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) {
-	testApexError(t, `"libz" .*: not found a version\(<=29\)`, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libx"],
-			min_sdk_version: "29",
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libx",
-			shared_libs: ["libz"],
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "myapex" ],
-			min_sdk_version: "29",
-		}
-
-		cc_library {
-			name: "libz",
-			system_shared_libs: [],
-			stl: "none",
-			stubs: {
-				versions: ["30"],
-			},
-		}
-	`)
-}
-
 func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) {
 	testApexError(t, `module "mylib".*: should support min_sdk_version\(29\)`, `
 		apex {
@@ -1976,6 +2039,7 @@
 					name: "myapex",
 					java_libs: ["myjar"],
 					key: "myapex.key",
+					updatable: false,
 				}
 				apex_key {
 					name: "myapex.key",
@@ -2157,7 +2221,7 @@
 }
 
 func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -2171,7 +2235,7 @@
 			private_key: "testkey.pem",
 		}
 
-		// mylib in myapex will link to mylib2#29
+		// mylib in myapex will link to mylib2#30
 		// mylib in otherapex will link to mylib2(non-stub) in otherapex as well
 		cc_library {
 			name: "mylib",
@@ -2205,7 +2269,7 @@
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
-	expectLink("mylib", "shared_apex29", "mylib2", "shared_29")
+	expectLink("mylib", "shared_apex29", "mylib2", "shared_30")
 	expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30")
 }
 
@@ -2244,7 +2308,7 @@
 		config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("S")
 		config.TestProductVariables.Platform_version_active_codenames = []string{"S", "T"}
 	}
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -2274,11 +2338,11 @@
 	// ensure libfoo is linked with "S" 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_S/libbar.so")
+	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_T/libbar.so")
 }
 
 func TestFilesInSubDir(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -2286,6 +2350,7 @@
 			binaries: ["mybin"],
 			prebuilts: ["myetc"],
 			compile_multilib: "both",
+			updatable: false,
 		}
 
 		apex_key {
@@ -2340,7 +2405,7 @@
 }
 
 func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -2352,6 +2417,7 @@
 			},
 			compile_multilib: "both",
 			native_bridge_supported: true,
+			updatable: false,
 		}
 
 		apex_key {
@@ -2398,12 +2464,13 @@
 }
 
 func TestUseVendor(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
 			use_vendor: true,
+			updatable: false,
 		}
 
 		apex_key {
@@ -2472,6 +2539,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			use_vendor: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2490,6 +2558,7 @@
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
 			use_vendor: true,
+			updatable: false,
 		}
 
 		apex_key {
@@ -2508,12 +2577,13 @@
 }
 
 func TestVendorApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			binaries: ["mybin"],
 			vendor: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2545,7 +2615,8 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, `LOCAL_MODULE_PATH := /tmp/target/product/test_device/vendor/apex`)
+	installPath := path.Join(buildDir, "../target/product/test_device/vendor/apex")
+	ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
 
 	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
 	requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
@@ -2553,13 +2624,14 @@
 }
 
 func TestVendorApex_use_vndk_as_stable(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			binaries: ["mybin"],
 			vendor: true,
 			use_vndk_as_stable: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2616,11 +2688,12 @@
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			ctx, _ := testApex(t, `
+			ctx := testApex(t, `
 				apex {
 					name: "myapex",
 					key: "myapex.key",
 					prebuilts: ["myfirmware"],
+					updatable: false,
 					`+tc.additionalProp+`
 				}
 				apex_key {
@@ -2643,12 +2716,13 @@
 }
 
 func TestAndroidMk_UseVendorRequired(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			use_vendor: true,
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -2677,12 +2751,13 @@
 }
 
 func TestAndroidMk_VendorApexRequired(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			vendor: true,
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -2708,12 +2783,13 @@
 }
 
 func TestAndroidMkWritesCommonProperties(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			vintf_fragments: ["fragment.xml"],
 			init_rc: ["init.rc"],
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2737,11 +2813,12 @@
 }
 
 func TestStaticLinking(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -2781,13 +2858,14 @@
 }
 
 func TestKeys(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex_keytest",
 			key: "myapex.key",
 			certificate: ":myapex.certificate",
 			native_shared_libs: ["mylib"],
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		cc_library {
@@ -2838,10 +2916,11 @@
 
 func TestCertificate(t *testing.T) {
 	t.Run("if unspecified, it defaults to DefaultAppCertificate", func(t *testing.T) {
-		ctx, _ := testApex(t, `
+		ctx := testApex(t, `
 			apex {
 				name: "myapex",
 				key: "myapex.key",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2855,11 +2934,12 @@
 		}
 	})
 	t.Run("override when unspecified", func(t *testing.T) {
-		ctx, _ := testApex(t, `
+		ctx := testApex(t, `
 			apex {
 				name: "myapex_keytest",
 				key: "myapex.key",
 				file_contexts: ":myapex-file_contexts",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2877,11 +2957,12 @@
 		}
 	})
 	t.Run("if specified as :module, it respects the prop", func(t *testing.T) {
-		ctx, _ := testApex(t, `
+		ctx := testApex(t, `
 			apex {
 				name: "myapex",
 				key: "myapex.key",
 				certificate: ":myapex.certificate",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2899,12 +2980,13 @@
 		}
 	})
 	t.Run("override when specifiec as <:module>", func(t *testing.T) {
-		ctx, _ := testApex(t, `
+		ctx := testApex(t, `
 			apex {
 				name: "myapex_keytest",
 				key: "myapex.key",
 				file_contexts: ":myapex-file_contexts",
 				certificate: ":myapex.certificate",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2922,11 +3004,12 @@
 		}
 	})
 	t.Run("if specified as name, finds it from DefaultDevKeyDir", func(t *testing.T) {
-		ctx, _ := testApex(t, `
+		ctx := testApex(t, `
 			apex {
 				name: "myapex",
 				key: "myapex.key",
 				certificate: "testkey",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2940,12 +3023,13 @@
 		}
 	})
 	t.Run("override when specified as <name>", func(t *testing.T) {
-		ctx, _ := testApex(t, `
+		ctx := testApex(t, `
 			apex {
 				name: "myapex_keytest",
 				key: "myapex.key",
 				file_contexts: ":myapex-file_contexts",
 				certificate: "testkey",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2965,11 +3049,12 @@
 }
 
 func TestMacro(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib2"],
+			updatable: false,
 		}
 
 		apex {
@@ -3092,11 +3177,12 @@
 }
 
 func TestHeaderLibsDependency(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3235,14 +3321,15 @@
 }
 
 func TestVndkApexCurrent(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex",
-			key: "myapex.key",
+			name: "com.android.vndk.current",
+			key: "com.android.vndk.current.key",
+			updatable: false,
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "com.android.vndk.current.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -3257,7 +3344,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 
 		cc_library {
@@ -3271,11 +3358,11 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 	`+vndkLibrariesTxtFiles("current"))
 
-	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
 		"lib/libvndk.so",
 		"lib/libvndksp.so",
 		"lib/libc++.so",
@@ -3291,14 +3378,15 @@
 }
 
 func TestVndkApexWithPrebuilt(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex",
-			key: "myapex.key",
+			name: "com.android.vndk.current",
+			key: "com.android.vndk.current.key",
+			updatable: false,
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "com.android.vndk.current.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -3313,7 +3401,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 
 		cc_prebuilt_library_shared {
@@ -3332,15 +3420,14 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 		`+vndkLibrariesTxtFiles("current"),
 		withFiles(map[string][]byte{
 			"libvndk.so":     nil,
 			"libvndk.arm.so": nil,
 		}))
-
-	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
 		"lib/libvndk.so",
 		"lib/libvndk.arm.so",
 		"lib64/libvndk.so",
@@ -3375,12 +3462,13 @@
 }
 
 func TestVndkApexVersion(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex_v27",
+			name: "com.android.vndk.v27",
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
 			vndk_version: "27",
+			updatable: false,
 		}
 
 		apex_key {
@@ -3406,7 +3494,7 @@
 					srcs: ["libvndk27_arm64.so"],
 				},
 			},
-			apex_available: [ "myapex_v27" ],
+			apex_available: [ "com.android.vndk.v27" ],
 		}
 
 		vndk_prebuilt_shared {
@@ -3435,73 +3523,27 @@
 			"libvndk27_x86_64.so": nil,
 		}))
 
-	ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{
 		"lib/libvndk27_arm.so",
 		"lib64/libvndk27_arm64.so",
 		"etc/*",
 	})
 }
 
-func TestVndkApexErrorWithDuplicateVersion(t *testing.T) {
-	testApexError(t, `module "myapex_v27.*" .*: vndk_version: 27 is already defined in "myapex_v27.*"`, `
-		apex_vndk {
-			name: "myapex_v27",
-			key: "myapex.key",
-			file_contexts: ":myapex-file_contexts",
-			vndk_version: "27",
-		}
-		apex_vndk {
-			name: "myapex_v27_other",
-			key: "myapex.key",
-			file_contexts: ":myapex-file_contexts",
-			vndk_version: "27",
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libvndk",
-			srcs: ["mylib.cpp"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-		}
-
-		vndk_prebuilt_shared {
-			name: "libvndk",
-			version: "27",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			srcs: ["libvndk.so"],
-		}
-	`, withFiles(map[string][]byte{
-		"libvndk.so": nil,
-	}))
-}
-
 func TestVndkApexNameRule(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex",
+			name: "com.android.vndk.current",
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 		apex_vndk {
-			name: "myapex_v28",
+			name: "com.android.vndk.v28",
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
 			vndk_version: "28",
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -3517,20 +3559,21 @@
 		}
 	}
 
-	assertApexName("com.android.vndk.vVER", "myapex")
-	assertApexName("com.android.vndk.v28", "myapex_v28")
+	assertApexName("com.android.vndk.vVER", "com.android.vndk.current")
+	assertApexName("com.android.vndk.v28", "com.android.vndk.v28")
 }
 
 func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex",
-			key: "myapex.key",
+			name: "com.android.vndk.current",
+			key: "com.android.vndk.current.key",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "com.android.vndk.current.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -3547,11 +3590,12 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
-		`+vndkLibrariesTxtFiles("current"), withNativeBridgeEnabled)
+		`+vndkLibrariesTxtFiles("current"),
+		withNativeBridgeEnabled)
 
-	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
 		"lib/libvndk.so",
 		"lib64/libvndk.so",
 		"lib/libc++.so",
@@ -3561,16 +3605,16 @@
 }
 
 func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) {
-	testApexError(t, `module "myapex" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
+	testApexError(t, `module "com.android.vndk.current" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
 		apex_vndk {
-			name: "myapex",
-			key: "myapex.key",
+			name: "com.android.vndk.current",
+			key: "com.android.vndk.current.key",
 			file_contexts: ":myapex-file_contexts",
 			native_bridge_supported: true,
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "com.android.vndk.current.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -3592,12 +3636,13 @@
 }
 
 func TestVndkApexWithBinder32(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex_v27",
+			name: "com.android.vndk.v27",
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
 			vndk_version: "27",
+			updatable: false,
 		}
 
 		apex_key {
@@ -3637,7 +3682,7 @@
 					srcs: ["libvndk27binder32.so"],
 				}
 			},
-			apex_available: [ "myapex_v27" ],
+			apex_available: [ "com.android.vndk.v27" ],
 		}
 		`+vndkLibrariesTxtFiles("27"),
 		withFiles(map[string][]byte{
@@ -3653,22 +3698,23 @@
 		}),
 	)
 
-	ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{
 		"lib/libvndk27binder32.so",
 		"etc/*",
 	})
 }
 
 func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex",
-			key: "myapex.key",
+			name: "com.android.vndk.current",
+			key: "com.android.vndk.current.key",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "com.android.vndk.current.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -3689,19 +3735,20 @@
 		"libz.map.txt": nil,
 	}))
 
-	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_image").Rule("apexManifestRule")
+	apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common_image").Rule("apexManifestRule")
 	provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"])
 	ensureListEmpty(t, provideNativeLibs)
 }
 
 func TestDependenciesInApexManifest(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex_nodep",
 			key: "myapex.key",
 			native_shared_libs: ["lib_nodep"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex {
@@ -3710,6 +3757,7 @@
 			native_shared_libs: ["lib_dep"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex {
@@ -3718,6 +3766,7 @@
 			native_shared_libs: ["libfoo"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex {
@@ -3726,6 +3775,7 @@
 			native_shared_libs: ["lib_dep", "libfoo"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -3799,12 +3849,13 @@
 }
 
 func TestApexName(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apex_name: "com.android.myapex",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3843,11 +3894,12 @@
 }
 
 func TestNonTestApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib_common"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3895,11 +3947,12 @@
 }
 
 func TestTestApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_test {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib_common_test"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3943,10 +3996,11 @@
 }
 
 func TestApexWithTarget(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 			multilib: {
 				first: {
 					native_shared_libs: ["mylib_common"],
@@ -4033,10 +4087,11 @@
 }
 
 func TestApexWithArch(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 			arch: {
 				arm64: {
 					native_shared_libs: ["mylib.arm64"],
@@ -4091,11 +4146,12 @@
 }
 
 func TestApexWithShBinary(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			binaries: ["myscript"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4131,10 +4187,11 @@
 	}
 	for _, tc := range testcases {
 		t.Run(tc.propName+":"+tc.parition, func(t *testing.T) {
-			ctx, _ := testApex(t, `
+			ctx := testApex(t, `
 				apex {
 					name: "myapex",
 					key: "myapex.key",
+					updatable: false,
 					`+tc.propName+`
 				}
 
@@ -4163,10 +4220,11 @@
 }
 
 func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4186,6 +4244,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			file_contexts: "my_own_file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4205,6 +4264,7 @@
 			key: "myapex.key",
 			product_specific: true,
 			file_contexts: "product_specific_file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4214,12 +4274,13 @@
 		}
 	`)
 
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			product_specific: true,
 			file_contexts: "product_specific_file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4236,12 +4297,13 @@
 }
 
 func TestFileContexts_SetViaFileGroup(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			product_specific: true,
 			file_contexts: ":my-file-contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4263,7 +4325,7 @@
 }
 
 func TestApexKeyFromOtherModule(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_key {
 			name: "myapex.key",
 			public_key: ":my.avbpubkey",
@@ -4296,7 +4358,7 @@
 }
 
 func TestPrebuilt(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		prebuilt_apex {
 			name: "myapex",
 			arch: {
@@ -4319,7 +4381,7 @@
 }
 
 func TestPrebuiltFilenameOverride(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		prebuilt_apex {
 			name: "myapex",
 			src: "myapex-arm.apex",
@@ -4336,7 +4398,7 @@
 }
 
 func TestPrebuiltOverrides(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		prebuilt_apex {
 			name: "myapex.prebuilt",
 			src: "myapex-arm.apex",
@@ -4366,14 +4428,15 @@
 		// Make sure the import has been given the correct path to the dex jar.
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
 		dexJarBuildPath := p.DexJarBuildPath()
-		if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
+		stem := android.RemoveOptionalPrebuiltPrefix(name)
+		if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
 			t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
 		}
 	}
 
-	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext) {
+	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
 		// Make sure that an apex variant is not created for the source module.
-		if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests("libfoo"); !reflect.DeepEqual(expected, actual) {
+		if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests(name); !reflect.DeepEqual(expected, actual) {
 			t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
 		}
 	}
@@ -4390,19 +4453,42 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			public: {
+				jars: ["libbar.jar"],
+			},
+		}
 	`
 
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
+		// Make sure that the deapexer has the correct input APEX.
+		deapexer := ctx.ModuleForTests("myapex.deapexer", "android_common")
+		rule := deapexer.Rule("deapexer")
+		if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) {
+			t.Errorf("expected: %q, found: %q", expected, actual)
+		}
+
+		// Make sure that the prebuilt_apex has the correct input APEX.
+		prebuiltApex := ctx.ModuleForTests("myapex", "android_common")
+		rule = prebuiltApex.Rule("android/soong/android.Cp")
+		if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
+			t.Errorf("expected: %q, found: %q", expected, actual)
+		}
+
 		checkDexJarBuildPath(t, ctx, "libfoo")
+
+		checkDexJarBuildPath(t, ctx, "libbar")
 	})
 
 	t.Run("prebuilt with source preferred", func(t *testing.T) {
@@ -4418,7 +4504,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4429,13 +4515,29 @@
 		java_library {
 			name: "libfoo",
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			public: {
+				jars: ["libbar.jar"],
+			},
+		}
+
+		java_sdk_library {
+			name: "libbar",
+			srcs: ["foo/bar/MyClass.java"],
+			unsafe_ignore_missing_latest_api: true,
+		}
 	`
 
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(t, ctx)
+		ensureNoSourceVariant(t, ctx, "libfoo")
+
+		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+		ensureNoSourceVariant(t, ctx, "libbar")
 	})
 
 	t.Run("prebuilt preferred with source", func(t *testing.T) {
@@ -4450,7 +4552,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4462,26 +4564,45 @@
 		java_library {
 			name: "libfoo",
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			prefer: true,
+			public: {
+				jars: ["libbar.jar"],
+			},
+		}
+
+		java_sdk_library {
+			name: "libbar",
+			srcs: ["foo/bar/MyClass.java"],
+			unsafe_ignore_missing_latest_api: true,
+		}
 	`
 
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(t, ctx)
+		ensureNoSourceVariant(t, ctx, "libfoo")
+
+		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+		ensureNoSourceVariant(t, ctx, "libbar")
 	})
 }
 
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
 	transform := func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
+		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo", "myapex:libbar"})
 	}
 
-	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, bootDexJarPath string) {
+	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
+		t.Helper()
 		s := ctx.SingletonForTests("dex_bootjars")
 		foundLibfooJar := false
+		base := stem + ".jar"
 		for _, output := range s.AllOutputs() {
-			if strings.HasSuffix(output, "/libfoo.jar") {
+			if filepath.Base(output) == base {
 				foundLibfooJar = true
 				buildRule := s.Output(output)
 				actual := android.NormalizePathForTesting(buildRule.Input)
@@ -4496,6 +4617,7 @@
 	}
 
 	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
+		t.Helper()
 		hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index")
 		indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
 		java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
@@ -4513,7 +4635,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4521,13 +4643,23 @@
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			public: {
+				jars: ["libbar.jar"],
+			},
+			apex_available: ["myapex"],
+		}
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		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")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/libbar/android_common_myapex/hiddenapi/index.csv
 .intermediates/libfoo/android_common_myapex/hiddenapi/index.csv
 `)
 	})
@@ -4544,7 +4676,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4558,6 +4690,21 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			public: {
+				jars: ["libbar.jar"],
+			},
+			apex_available: ["myapex"],
+		}
+
+		java_sdk_library {
+			name: "libbar",
+			srcs: ["foo/bar/MyClass.java"],
+			unsafe_ignore_missing_latest_api: true,
+			apex_available: ["myapex"],
+		}
 	`
 
 		// In this test the source (java_library) libfoo is active since the
@@ -4580,7 +4727,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4595,13 +4742,31 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			prefer: true,
+			public: {
+				jars: ["libbar.jar"],
+			},
+			apex_available: ["myapex"],
+		}
+
+		java_sdk_library {
+			name: "libbar",
+			srcs: ["foo/bar/MyClass.java"],
+			unsafe_ignore_missing_latest_api: true,
+			apex_available: ["myapex"],
+		}
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		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")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv
 .intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv
 `)
 	})
@@ -4611,7 +4776,8 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			java_libs: ["libfoo"],
+			java_libs: ["libfoo", "libbar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4630,7 +4796,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4644,13 +4810,30 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			public: {
+				jars: ["libbar.jar"],
+			},
+			apex_available: ["myapex"],
+		}
+
+		java_sdk_library {
+			name: "libbar",
+			srcs: ["foo/bar/MyClass.java"],
+			unsafe_ignore_missing_latest_api: true,
+			apex_available: ["myapex"],
+		}
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", ".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, `
+.intermediates/libbar/android_common_myapex/hiddenapi/index.csv
 .intermediates/libfoo/android_common_apex10000/hiddenapi/index.csv
 `)
 	})
@@ -4680,7 +4863,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4695,23 +4878,42 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 		}
+
+		java_sdk_library_import {
+			name: "libbar",
+			prefer: true,
+			public: {
+				jars: ["libbar.jar"],
+			},
+			apex_available: ["myapex"],
+		}
+
+		java_sdk_library {
+			name: "libbar",
+			srcs: ["foo/bar/MyClass.java"],
+			unsafe_ignore_missing_latest_api: true,
+			apex_available: ["myapex"],
+		}
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-		checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		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")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/prebuilt_libbar/android_common_prebuilt_myapex/hiddenapi/index.csv
 .intermediates/prebuilt_libfoo/android_common_prebuilt_myapex/hiddenapi/index.csv
 `)
 	})
 }
 
 func TestApexWithTests(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_test {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 			tests: [
 				"mytest",
 				"mytests",
@@ -4819,10 +5021,11 @@
 }
 
 func TestInstallExtraFlattenedApexes(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -4888,11 +5091,12 @@
 }
 
 func TestApexWithJavaImport(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["myjavaimport"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4916,7 +5120,7 @@
 }
 
 func TestApexWithApps(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -4924,6 +5128,7 @@
 				"AppFoo",
 				"AppFooPriv",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4994,7 +5199,7 @@
 }
 
 func TestApexWithAppImports(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -5002,6 +5207,7 @@
 				"AppFooPrebuilt",
 				"AppFooPrivPrebuilt",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5042,13 +5248,14 @@
 }
 
 func TestApexWithAppImportsPrefer(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: [
 				"AppFoo",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5083,13 +5290,14 @@
 }
 
 func TestApexWithTestHelperApp(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: [
 				"TesterHelpAppFoo",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5120,6 +5328,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5132,6 +5341,7 @@
 		name: "otherapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	cc_defaults {
@@ -5154,6 +5364,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5166,6 +5377,7 @@
 		name: "otherapex",
 		key: "otherapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5184,7 +5396,7 @@
 
 func TestApexAvailable_IndirectDep(t *testing.T) {
 	// libbbaz is an indirect dep
-	testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'. Dependency path:
+	testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path:
 .*via tag apex\.dependencyTag.*name:sharedLib.*
 .*-> libfoo.*link:shared.*
 .*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.*
@@ -5195,6 +5407,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5232,6 +5445,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5252,6 +5466,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo", "libbar"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5286,11 +5501,12 @@
 }
 
 func TestApexAvailable_CheckForPlatform(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 	apex {
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libbar", "libbaz"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5348,11 +5564,12 @@
 }
 
 func TestApexAvailable_CreatedForApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 	apex {
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5382,12 +5599,13 @@
 }
 
 func TestOverrideApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
 			overrides: ["oldapex"],
+			updatable: false,
 		}
 
 		override_apex {
@@ -5465,7 +5683,7 @@
 }
 
 func TestLegacyAndroid10Support(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -5525,11 +5743,12 @@
 }
 
 func TestJavaSDKLibrary(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5562,11 +5781,12 @@
 }
 
 func TestJavaSDKLibrary_WithinApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo", "bar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5614,11 +5834,12 @@
 }
 
 func TestJavaSDKLibrary_CrossBoundary(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5664,7 +5885,7 @@
 }
 
 func TestJavaSDKLibrary_ImportPreferred(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		prebuilt_apis {
 			name: "sdk",
 			api_dirs: ["100"],
@@ -5681,6 +5902,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo", "bar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5758,6 +5980,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5779,12 +6002,13 @@
 }
 
 func TestCompatConfig(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			prebuilts: ["myjar-platform-compat-config"],
 			java_libs: ["myjar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5818,6 +6042,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["myjar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5838,11 +6063,12 @@
 }
 
 func TestCarryRequiredModuleNames(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5882,6 +6108,7 @@
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
 			java_libs: ["myjar"],
+			updatable: false,
 		}
 
 		apex {
@@ -5980,7 +6207,7 @@
 
 	// For unbundled build, symlink shouldn't exist regardless of whether an APEX
 	// is updatable or not
-	ctx, _ := testApex(t, bp, withUnbundledBuild)
+	ctx := testApex(t, bp, withUnbundledBuild)
 	files := getFiles(t, ctx, "myapex", "android_common_myapex_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
@@ -5992,7 +6219,7 @@
 	ensureRealfileExists(t, files, "lib64/myotherlib.so")
 
 	// For bundled build, symlink to the system for the non-updatable APEXes only
-	ctx, _ = testApex(t, bp)
+	ctx = testApex(t, bp)
 	files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
@@ -6005,11 +6232,12 @@
 }
 
 func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6056,11 +6284,12 @@
 }
 
 func TestApexWithJniLibs(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			jni_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6097,10 +6326,11 @@
 }
 
 func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -6118,11 +6348,12 @@
 }
 
 func TestAppBundle(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["AppFoo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6148,11 +6379,12 @@
 }
 
 func TestAppSetBundle(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["AppSet"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6180,7 +6412,7 @@
 }
 
 func TestAppSetBundlePrebuilt(t *testing.T) {
-	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+	ctx := testApex(t, "", func(fs map[string][]byte, config android.Config) {
 		bp := `
 		apex_set {
 			name: "myapex",
@@ -6259,6 +6491,7 @@
 			name: "some-non-updatable-apex",
 			key: "some-non-updatable-apex.key",
 			java_libs: ["some-non-updatable-apex-lib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6319,6 +6552,9 @@
 	}
 	cc.GatherRequiredFilesForTest(fs)
 
+	for k, v := range filesForSdkLibrary {
+		fs[k] = v
+	}
 	config := android.TestArchConfig(buildDir, nil, bp, fs)
 
 	ctx := android.NewTestArchContext(config)
@@ -6327,6 +6563,7 @@
 	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)
@@ -6378,6 +6615,21 @@
 	`)
 }
 
+func TestUpdatableDefault_should_set_min_sdk_version(t *testing.T) {
+	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
 	var err string
 	var transform func(*dexpreopt.GlobalConfig)
@@ -6592,6 +6844,7 @@
 					name: "myapex",
 					key: "myapex.key",
 					java_libs: ["bcp_lib1", "nonbcp_lib2"],
+					updatable: false,
 				}`,
 			bootJars: []string{"bcp_lib1"},
 			modulesPackages: map[string][]string{
@@ -6624,6 +6877,7 @@
 					name: "myapex",
 					key: "myapex.key",
 					java_libs: ["bcp_lib1", "bcp_lib2"],
+					updatable: false,
 				}
 			`,
 			bootJars: []string{"bcp_lib1", "bcp_lib2"},
@@ -6643,11 +6897,12 @@
 }
 
 func TestTestFor(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "myprivlib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6728,7 +6983,7 @@
 }
 
 func TestApexSet(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_set {
 			name: "myapex",
 			set: "myapex.apks",
@@ -6772,6 +7027,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6803,10 +7059,11 @@
 }
 
 func TestApexKeysTxt(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 
 		apex_key {
@@ -6843,12 +7100,13 @@
 }
 
 func TestAllowedFiles(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
 			allowed_files: "allowed.txt",
+			updatable: false,
 		}
 
 		apex_key {
@@ -6898,11 +7156,12 @@
 }
 
 func TestNonPreferredPrebuiltDependency(t *testing.T) {
-	_, _ = testApex(t, `
+	testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6933,11 +7192,12 @@
 }
 
 func TestCompressedApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			compressible: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -6967,11 +7227,12 @@
 }
 
 func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -7018,11 +7279,12 @@
 }
 
 func TestExcludeDependency(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -7072,6 +7334,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -7090,6 +7353,7 @@
 			enabled: %s,
 			key: "myapex.key",
 			native_shared_libs: ["stublib"],
+			updatable: false,
 		}
 	`
 
@@ -7159,7 +7423,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			for _, otherApexEnabled := range test.otherApexEnabled {
 				t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
-					ctx, _ := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
+					ctx := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
 
 					type modAndMkEntries struct {
 						mod       *cc.Module
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 27a1562..2e6ed82 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -28,7 +28,7 @@
 // modules from the ART apex.
 
 func TestBootImages(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		java_sdk_library {
 			name: "foo",
 			srcs: ["b.java"],
@@ -48,6 +48,7 @@
 				"baz",
 				"quuz",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -180,13 +181,14 @@
 }
 
 func TestBootImageInApex(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			boot_images: [
 				"mybootimage",
 			],
+			updatable: false,
 		}
 
 		apex_key {
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 8f4a285..46ce41f 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -65,7 +65,7 @@
 		&module.properties,
 		&module.apexFileProperties,
 	)
-	android.InitSingleSourcePrebuiltModule(module, &module.apexFileProperties, "Source")
+	android.InitPrebuiltModuleWithSrcSupplier(module, module.apexFileProperties.prebuiltApexSelector, "src")
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
 }
@@ -78,16 +78,6 @@
 	return p.prebuilt.Name(p.ModuleBase.Name())
 }
 
-func deapexerSelectSourceMutator(ctx android.BottomUpMutatorContext) {
-	p, ok := ctx.Module().(*Deapexer)
-	if !ok {
-		return
-	}
-	if err := p.apexFileProperties.selectSource(ctx); err != nil {
-		ctx.ModuleErrorf("%s", err)
-	}
-}
-
 func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies from the java modules to which this exports files from the `.apex` file onto
 	// this module so that they can access the `DeapexerInfo` object that this provides.
diff --git a/apex/key.go b/apex/key.go
index 752888d..8b33b59 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -27,8 +27,12 @@
 var String = proptools.String
 
 func init() {
-	android.RegisterModuleType("apex_key", ApexKeyFactory)
-	android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+	registerApexKeyBuildComponents(android.InitRegistrationContext)
+}
+
+func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
 }
 
 type apexKey struct {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ec7f253..3280cd8 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -109,8 +109,10 @@
 
 type ApexFileProperties struct {
 	// the path to the prebuilt .apex file to import.
-	Source string `blueprint:"mutated"`
-
+	//
+	// This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
+	// for android_common. That is so that it will have the same arch variant as, and so be compatible
+	// with, the source `apex` module type that it replaces.
 	Src  *string
 	Arch struct {
 		Arm struct {
@@ -128,15 +130,20 @@
 	}
 }
 
-func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) error {
-	// This is called before prebuilt_select and prebuilt_postdeps mutators
-	// The mutators requires that src to be set correctly for each arch so that
-	// arch variants are disabled when src is not provided for the arch.
-	if len(ctx.MultiTargets()) != 1 {
-		return fmt.Errorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+// prebuiltApexSelector selects the correct prebuilt APEX file for the build target.
+//
+// The ctx parameter can be for any module not just the prebuilt module so care must be taken not
+// to use methods on it that are specific to the current module.
+//
+// See the ApexFileProperties.Src property.
+func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+	multiTargets := prebuilt.MultiTargets()
+	if len(multiTargets) != 1 {
+		ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
+		return nil
 	}
 	var src string
-	switch ctx.MultiTargets()[0].Arch.ArchType {
+	switch multiTargets[0].Arch.ArchType {
 	case android.Arm:
 		src = String(p.Arch.Arm.Src)
 	case android.Arm64:
@@ -146,14 +153,14 @@
 	case android.X86_64:
 		src = String(p.Arch.X86_64.Src)
 	default:
-		return fmt.Errorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+		ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+		return nil
 	}
 	if src == "" {
 		src = String(p.Src)
 	}
-	p.Source = src
 
-	return nil
+	return []string{src}
 }
 
 type PrebuiltProperties struct {
@@ -217,7 +224,7 @@
 func PrebuiltFactory() android.Module {
 	module := &Prebuilt{}
 	module.AddProperties(&module.properties)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
+	android.InitPrebuiltModuleWithSrcSupplier(module, module.properties.prebuiltApexSelector, "src")
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
@@ -250,16 +257,6 @@
 	return name
 }
 
-func prebuiltSelectSourceMutator(ctx android.BottomUpMutatorContext) {
-	p, ok := ctx.Module().(*Prebuilt)
-	if !ok {
-		return
-	}
-	if err := p.properties.selectSource(ctx); err != nil {
-		ctx.ModuleErrorf("%s", err)
-	}
-}
-
 type exportedDependencyTag struct {
 	blueprint.BaseDependencyTag
 	name string
@@ -535,7 +532,7 @@
 	module := &ApexSet{}
 	module.AddProperties(&module.properties)
 
-	srcsSupplier := func(ctx android.BaseModuleContext) []string {
+	srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
 		return module.prebuiltSrcs(ctx)
 	}
 
diff --git a/cmd/soong_env/Android.bp b/apex/testing.go
similarity index 63%
rename from cmd/soong_env/Android.bp
rename to apex/testing.go
index ad717d0..e662cad 100644
--- a/cmd/soong_env/Android.bp
+++ b/apex/testing.go
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All rights reserved.
+// 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.
@@ -12,17 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
+package apex
 
-bootstrap_go_binary {
-    name: "soong_env",
-    deps: [
-        "soong-env",
-    ],
-    srcs: [
-        "soong_env.go",
-    ],
-    default: true,
-}
+import "android/soong/android"
+
+var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerApexBuildComponents),
+	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+)
diff --git a/apex/vndk.go b/apex/vndk.go
index f4b12b5..75c0fb0 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -17,7 +17,6 @@
 import (
 	"path/filepath"
 	"strings"
-	"sync"
 
 	"android/soong/android"
 	"android/soong/cc"
@@ -60,17 +59,6 @@
 	Vndk_version *string
 }
 
-var (
-	vndkApexListKey   = android.NewOnceKey("vndkApexList")
-	vndkApexListMutex sync.Mutex
-)
-
-func vndkApexList(config android.Config) map[string]string {
-	return config.Once(vndkApexListKey, func() interface{} {
-		return map[string]string{}
-	}).(map[string]string)
-}
-
 func apexVndkMutator(mctx android.TopDownMutatorContext) {
 	if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex {
 		if ab.IsNativeBridgeSupported() {
@@ -80,15 +68,6 @@
 		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
 		// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
 		ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
-
-		// vndk_version should be unique
-		vndkApexListMutex.Lock()
-		defer vndkApexListMutex.Unlock()
-		vndkApexList := vndkApexList(mctx.Config())
-		if other, ok := vndkApexList[vndkVersion]; ok {
-			mctx.PropertyErrorf("vndk_version", "%v is already defined in %q", vndkVersion, other)
-		}
-		vndkApexList[vndkVersion] = mctx.ModuleName()
 	}
 }
 
@@ -99,9 +78,16 @@
 		if vndkVersion == "" {
 			vndkVersion = mctx.DeviceConfig().PlatformVndkVersion()
 		}
-		vndkApexList := vndkApexList(mctx.Config())
-		if vndkApex, ok := vndkApexList[vndkVersion]; ok {
-			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex)
+		if vndkVersion == mctx.DeviceConfig().PlatformVndkVersion() {
+			vndkVersion = "current"
+		} else {
+			vndkVersion = "v" + vndkVersion
+		}
+
+		vndkApexName := "com.android.vndk." + vndkVersion
+
+		if mctx.OtherModuleExists(vndkApexName) {
+			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName)
 		}
 	} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
 		vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index ccf4e57..34b9408 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -9,14 +9,15 @@
 )
 
 func TestVndkApexForVndkLite(t *testing.T) {
-	ctx, _ := testApex(t, `
+	ctx := testApex(t, `
 		apex_vndk {
-			name: "myapex",
-			key: "myapex.key",
+			name: "com.android.vndk.current",
+			key: "com.android.vndk.current.key",
+			updatable: false,
 		}
 
 		apex_key {
-			name: "myapex.key",
+			name: "com.android.vndk.current.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
@@ -31,7 +32,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 
 		cc_library {
@@ -45,13 +46,13 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 	`+vndkLibrariesTxtFiles("current"), func(fs map[string][]byte, config android.Config) {
 		config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("")
 	})
 	// VNDK-Lite contains only core variants of VNDK-Sp libraries
-	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
 		"lib/libvndksp.so",
 		"lib/libc++.so",
 		"lib64/libvndksp.so",
@@ -67,8 +68,9 @@
 func TestVndkApexUsesVendorVariant(t *testing.T) {
 	bp := `
 		apex_vndk {
-			name: "myapex",
+			name: "com.android.vndk.current",
 			key: "mykey",
+			updatable: false,
 		}
 		apex_key {
 			name: "mykey",
@@ -94,11 +96,11 @@
 				return
 			}
 		}
-		t.Fail()
+		t.Errorf("expected path %q not found", path)
 	}
 
 	t.Run("VNDK lib doesn't have an apex variant", func(t *testing.T) {
-		ctx, _ := testApex(t, bp)
+		ctx := testApex(t, bp)
 
 		// libfoo doesn't have apex variants
 		for _, variant := range ctx.ModuleVariantsForTests("libfoo") {
@@ -106,30 +108,30 @@
 		}
 
 		// VNDK APEX doesn't create apex variant
-		files := getFiles(t, ctx, "myapex", "android_common_image")
+		files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image")
 		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
 	})
 
 	t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) {
-		ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
+		ctx := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
 			// Now product variant is available
 			config.TestProductVariables.ProductVndkVersion = proptools.StringPtr("current")
 		})
 
-		files := getFiles(t, ctx, "myapex", "android_common_image")
+		files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image")
 		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
 	})
 
 	t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
-		ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
+		ctx := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
 			config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
 			config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
 		})
 
-		files := getFiles(t, ctx, "myapex", "android_common_image")
+		files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image")
 		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
 
-		files = getFiles(t, ctx, "myapex", "android_common_cov_image")
+		files = getFiles(t, ctx, "com.android.vndk.current", "android_common_cov_image")
 		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared_cov/libfoo.so")
 	})
 }
diff --git a/bazel/aquery.go b/bazel/aquery.go
index eb4bdfe..c82b464 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -115,7 +115,23 @@
 	// may be an expensive operation.
 	depsetIdToArtifactIdsCache := map[int][]int{}
 
+	// Do a pass through all actions to identify which artifacts are middleman artifacts.
+	// These will be omitted from the inputs of other actions.
+	// TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated
+	// headers may cause build failures.
+	middlemanArtifactIds := map[int]bool{}
 	for _, actionEntry := range aqueryResult.Actions {
+		if actionEntry.Mnemonic == "Middleman" {
+			for _, outputId := range actionEntry.OutputIds {
+				middlemanArtifactIds[outputId] = true
+			}
+		}
+	}
+
+	for _, actionEntry := range aqueryResult.Actions {
+		if shouldSkipAction(actionEntry) {
+			continue
+		}
 		outputPaths := []string{}
 		for _, outputId := range actionEntry.OutputIds {
 			outputPath, exists := artifactIdToPath[outputId]
@@ -132,6 +148,10 @@
 				return nil, err
 			}
 			for _, inputId := range inputArtifacts {
+				if _, isMiddlemanArtifact := middlemanArtifactIds[inputId]; isMiddlemanArtifact {
+					// Omit middleman artifacts.
+					continue
+				}
 				inputPath, exists := artifactIdToPath[inputId]
 				if !exists {
 					return nil, fmt.Errorf("undefined input artifactId %d", inputId)
@@ -145,12 +165,38 @@
 			InputPaths:  inputPaths,
 			Env:         actionEntry.EnvironmentVariables,
 			Mnemonic:    actionEntry.Mnemonic}
+		if len(actionEntry.Arguments) < 1 {
+			return nil, fmt.Errorf("received action with no command: [%s]", buildStatement)
+			continue
+		}
 		buildStatements = append(buildStatements, buildStatement)
 	}
 
 	return buildStatements, nil
 }
 
+func shouldSkipAction(a action) bool {
+	// TODO(b/180945121): Handle symlink actions.
+	if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" {
+		return true
+	}
+	// TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated
+	// headers may cause build failures.
+	if a.Mnemonic == "Middleman" {
+		return true
+	}
+	// Skip "Fail" actions, which are placeholder actions designed to always fail.
+	if a.Mnemonic == "Fail" {
+		return true
+	}
+	// TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
+	// about the contents that are written.
+	if a.Mnemonic == "FileWrite" {
+		return true
+	}
+	return false
+}
+
 func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles,
 	depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) {
 	if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists {
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 8deb5a2..99d706c 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -19,6 +19,7 @@
         "soong-bazel",
         "soong-cc",
         "soong-genrule",
+        "soong-python",
         "soong-sh",
     ],
     testSrcs: [
@@ -27,6 +28,7 @@
         "cc_library_headers_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
+        "python_binary_conversion_test.go",
         "sh_conversion_test.go",
         "testing.go",
     ],
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
new file mode 100644
index 0000000..7600e36
--- /dev/null
+++ b/bp2build/python_binary_conversion_test.go
@@ -0,0 +1,170 @@
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/python"
+	"fmt"
+	"strings"
+	"testing"
+)
+
+func TestPythonBinaryHost(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		blueprint                          string
+		expectedBazelTargets               []string
+		filesystem                         map[string]string
+	}{
+		{
+			description:                        "simple python_binary_host converts to a native py_binary",
+			moduleTypeUnderTest:                "python_binary_host",
+			moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
+			moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+			filesystem: map[string]string{
+				"a.py":           "",
+				"b/c.py":         "",
+				"b/d.py":         "",
+				"b/e.py":         "",
+				"files/data.txt": "",
+			},
+			blueprint: `python_binary_host {
+    name: "foo",
+    main: "a.py",
+    srcs: [
+        "**/*.py"
+    ],
+    exclude_srcs: [
+        "b/e.py"
+    ],
+    data: [
+        "files/data.txt",
+    ],
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`py_binary(
+    name = "foo",
+    data = [
+        "files/data.txt",
+    ],
+    main = "a.py",
+    srcs = [
+        "a.py",
+        "b/c.py",
+        "b/d.py",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "py2 python_binary_host",
+			moduleTypeUnderTest:                "python_binary_host",
+			moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
+			moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+			blueprint: `python_binary_host {
+    name: "foo",
+    srcs: ["a.py"],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`py_binary(
+    name = "foo",
+    python_version = "PY2",
+    srcs = [
+        "a.py",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "py3 python_binary_host",
+			moduleTypeUnderTest:                "python_binary_host",
+			moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
+			moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+			blueprint: `python_binary_host {
+    name: "foo",
+    srcs: ["a.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{
+				// python_version is PY3 by default.
+				`py_binary(
+    name = "foo",
+    srcs = [
+        "a.py",
+    ],
+)`,
+			},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		filesystem := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.filesystem {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			filesystem[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
+		ctx := android.NewTestContext(config)
+
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			fmt.Println(bazelTargets)
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 8142f10..fa1a84d 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -26,7 +26,7 @@
 )
 
 func init() {
-	android.RegisterModuleType("bpf", BpfFactory)
+	registerBpfBuildComponents(android.InitRegistrationContext)
 	pctx.Import("android/soong/cc/config")
 }
 
@@ -43,6 +43,12 @@
 		"ccCmd", "cFlags")
 )
 
+func registerBpfBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("bpf", BpfFactory)
+}
+
+var PrepareForTestWithBpf = android.FixtureRegisterWithContext(registerBpfBuildComponents)
+
 // BpfModule interface is used by the apex package to gather information from a bpf module.
 type BpfModule interface {
 	android.Module
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index be9e36e..eb0d8c8 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -46,24 +46,20 @@
 	}
 
 	os.Exit(run())
+
 }
 
-func testConfig(buildDir string, env map[string]string, bp string) android.Config {
-	mockFS := map[string][]byte{
-		"bpf.c":       nil,
-		"BpfTest.cpp": nil,
-	}
-
-	return cc.TestConfig(buildDir, android.Android, env, bp, mockFS)
-}
-
-func testContext(config android.Config) *android.TestContext {
-	ctx := cc.CreateTestContext(config)
-	ctx.RegisterModuleType("bpf", BpfFactory)
-	ctx.Register()
-
-	return ctx
-}
+var bpfFactory = android.NewFixtureFactory(
+	&buildDir,
+	cc.PrepareForTestWithCcDefaultModules,
+	android.FixtureMergeMockFs(
+		map[string][]byte{
+			"bpf.c":       nil,
+			"BpfTest.cpp": nil,
+		},
+	),
+	PrepareForTestWithBpf,
+)
 
 func TestBpfDataDependency(t *testing.T) {
 	bp := `
@@ -80,16 +76,7 @@
 		}
 	`
 
-	config := testConfig(buildDir, nil, bp)
-	ctx := testContext(config)
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if errs == nil {
-		_, errs = ctx.PrepareBuildActions(config)
-	}
-	if errs != nil {
-		t.Fatal(errs)
-	}
+	bpfFactory.RunTestWithBp(t, bp)
 
 	// We only verify the above BP configuration is processed successfully since the data property
 	// value is not available for testing from this package.
diff --git a/build_kzip.bash b/build_kzip.bash
index 9564723..a4659d4 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -16,6 +16,7 @@
 : ${BUILD_NUMBER:=$(uuidgen)}
 : ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500}
 : ${KYTHE_KZIP_ENCODING:=proto}
+: ${XREF_CORPUS:?should be set}
 export KYTHE_JAVA_SOURCE_BATCH_SIZE KYTHE_KZIP_ENCODING
 
 # The extraction might fail for some source files, so run with -k and then check that
@@ -29,11 +30,15 @@
 declare -r abspath_out=$(realpath "${out}")
 declare -r go_extractor=$(realpath prebuilts/build-tools/linux-x86/bin/go_extractor)
 declare -r go_root=$(realpath prebuilts/go/linux-x86)
-declare -r vnames_path=$(realpath build/soong/vnames.go.json)
 declare -r source_root=$PWD
+
+# TODO(asmundak): Until b/182183061 is fixed, default corpus has to be specified 
+# in the rules file. Generate this file on the fly with corpus value set from the
+# environment variable.
 for dir in blueprint soong; do
   (cd "build/$dir";
-   KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" --rules="${vnames_path}" \
+   KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" \
+   --rules=<(printf '[{"pattern": "(.*)","vname": {"path": "@1@", "corpus":"%s"}}]' "${XREF_CORPUS}") \
    --canonicalize_package_corpus --output "${abspath_out}/soong/build_${dir}.go.kzip" ./...
   )
 done
diff --git a/cc/cc.go b/cc/cc.go
index 11d8718..c335dac 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -577,6 +577,17 @@
 	makeUninstallable(mod *Module)
 }
 
+// bazelHandler is the interface for a helper object related to deferring to Bazel for
+// processing a module (during Bazel mixed builds). Individual module types should define
+// their own bazel handler if they support deferring to Bazel.
+type bazelHandler interface {
+	// Issue query to Bazel to retrieve information about Bazel's view of the current module.
+	// If Bazel returns this information, set module properties on the current module to reflect
+	// the returned information.
+	// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
+	generateBazelBuildActions(ctx android.ModuleContext, label string) bool
+}
+
 type xref interface {
 	XrefCcFiles() android.Paths
 }
@@ -779,9 +790,10 @@
 	// type-specific logic. These members may reference different objects or the same object.
 	// Functions of these decorators will be invoked to initialize and register type-specific
 	// build statements.
-	compiler  compiler
-	linker    linker
-	installer installer
+	compiler     compiler
+	linker       linker
+	installer    installer
+	bazelHandler bazelHandler
 
 	features []feature
 	stl      *stl
@@ -1559,24 +1571,7 @@
 	return nameSuffix
 }
 
-func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
-	// Handle the case of a test module split by `test_per_src` mutator.
-	//
-	// The `test_per_src` mutator adds an extra variation named "", depending on all the other
-	// `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
-	// module and return early, as this module does not produce an output file per se.
-	if c.IsTestPerSrcAllTestsVariation() {
-		c.outputFile = android.OptionalPath{}
-		return
-	}
-
-	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	if !apexInfo.IsForPlatform() {
-		c.hideApexVariantFromMake = true
-	}
-
-	c.makeLinkType = GetMakeLinkType(actx, c)
-
+func (c *Module) setSubnameProperty(actx android.ModuleContext) {
 	c.Properties.SubName = ""
 
 	if c.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -1606,6 +1601,43 @@
 			c.Properties.SubName += "." + c.SdkVersion()
 		}
 	}
+}
+
+// Returns true if Bazel was successfully used for the analysis of this module.
+func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
+	bazelModuleLabel := c.GetBazelLabel()
+	bazelActionsUsed := false
+	if c.bazelHandler != nil && actx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
+		bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel)
+	}
+	return bazelActionsUsed
+}
+
+func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+	// TODO(cparsons): Any logic in this method occurring prior to querying Bazel should be
+	// requested from Bazel instead.
+
+	// Handle the case of a test module split by `test_per_src` mutator.
+	//
+	// The `test_per_src` mutator adds an extra variation named "", depending on all the other
+	// `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
+	// module and return early, as this module does not produce an output file per se.
+	if c.IsTestPerSrcAllTestsVariation() {
+		c.outputFile = android.OptionalPath{}
+		return
+	}
+
+	c.setSubnameProperty(actx)
+	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	if !apexInfo.IsForPlatform() {
+		c.hideApexVariantFromMake = true
+	}
+
+	if c.maybeGenerateBazelActions(actx) {
+		return
+	}
+
+	c.makeLinkType = GetMakeLinkType(actx, c)
 
 	ctx := &moduleContext{
 		ModuleContext: actx,
@@ -2420,36 +2452,6 @@
 	}
 }
 
-// Returns the highest version which is <= maxSdkVersion.
-// For example, with maxSdkVersion is 10 and versionList is [9,11]
-// it returns 9 as string.  The list of stubs must be in order from
-// oldest to newest.
-func (c *Module) chooseSdkVersion(ctx android.PathContext, stubsInfo []SharedStubLibrary,
-	maxSdkVersion android.ApiLevel) (SharedStubLibrary, error) {
-
-	for i := range stubsInfo {
-		stubInfo := stubsInfo[len(stubsInfo)-i-1]
-		var ver android.ApiLevel
-		if stubInfo.Version == "" {
-			ver = android.FutureApiLevel
-		} else {
-			var err error
-			ver, err = android.ApiLevelFromUser(ctx, stubInfo.Version)
-			if err != nil {
-				return SharedStubLibrary{}, err
-			}
-		}
-		if ver.LessThanOrEqualTo(maxSdkVersion) {
-			return stubInfo, nil
-		}
-	}
-	var versionList []string
-	for _, stubInfo := range stubsInfo {
-		versionList = append(versionList, stubInfo.Version)
-	}
-	return SharedStubLibrary{}, fmt.Errorf("not found a version(<=%s) in versionList: %v", maxSdkVersion.String(), versionList)
-}
-
 // Convert dependencies to paths.  Returns a PathDeps containing paths
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
@@ -2633,16 +2635,12 @@
 						useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
 					}
 
-					// when to use (unspecified) stubs, check min_sdk_version and choose the right one
+					// when to use (unspecified) stubs, use the latest one.
 					if useStubs {
-						sharedLibraryStubsInfo, err :=
-							c.chooseSdkVersion(ctx, sharedLibraryStubsInfo.SharedStubLibraries, c.apexSdkVersion)
-						if err != nil {
-							ctx.OtherModuleErrorf(dep, err.Error())
-							return
-						}
-						sharedLibraryInfo = sharedLibraryStubsInfo.SharedLibraryInfo
-						depExporterInfo = sharedLibraryStubsInfo.FlagExporterInfo
+						stubs := sharedLibraryStubsInfo.SharedStubLibraries
+						toUse := stubs[len(stubs)-1]
+						sharedLibraryInfo = toUse.SharedLibraryInfo
+						depExporterInfo = toUse.FlagExporterInfo
 					}
 				}
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index e640f12..7d9fa47 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -52,29 +52,49 @@
 	os.Exit(run())
 }
 
+var ccFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	PrepareForTestWithCcIncludeVndk,
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.DeviceVndkVersion = StringPtr("current")
+		variables.ProductVndkVersion = StringPtr("current")
+		variables.Platform_vndk_version = StringPtr("VER")
+	}),
+)
+
+// testCcWithConfig runs tests using the ccFixtureFactory
+//
+// 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()
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	return ctx
+	result := ccFixtureFactory.RunTestWithConfig(t, config)
+	return result.TestContext
 }
 
+// testCc runs tests using the ccFixtureFactory
+//
+// Do not add any new usages of this, instead use the ccFixtureFactory 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
+// 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()
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.ProductVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-
-	return testCcWithConfig(t, config)
+	result := ccFixtureFactory.RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
+// testCcNoVndk runs tests using the ccFixtureFactory
+//
+// 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)
@@ -83,6 +103,11 @@
 	return testCcWithConfig(t, config)
 }
 
+// testCcNoProductVndk runs tests using the ccFixtureFactory
+//
+// 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)
@@ -92,27 +117,24 @@
 	return testCcWithConfig(t, config)
 }
 
+// testCcErrorWithConfig runs tests using the ccFixtureFactory
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
 	t.Helper()
 
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	_, errs = ctx.PrepareBuildActions(config)
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+	ccFixtureFactory.Extend().
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+		RunTestWithConfig(t, config)
 }
 
+// testCcError runs tests using the ccFixtureFactory
+//
+// 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)
@@ -122,6 +144,11 @@
 	return
 }
 
+// testCcErrorProductVndk runs tests using the ccFixtureFactory
+//
+// 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)
@@ -153,13 +180,12 @@
 			},
 		}`
 
-	config := TestConfig(buildDir, android.Fuchsia, nil, bp, nil)
-	ctx := testCcWithConfig(t, config)
+	result := ccFixtureFactory.Extend(PrepareForTestOnFuchsia).RunTestWithBp(t, bp)
 
 	rt := false
 	fb := false
 
-	ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
+	ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
 	implicits := ld.Implicits
 	for _, lib := range implicits {
 		if strings.Contains(lib.Rel(), "libcompiler_rt") {
@@ -190,16 +216,13 @@
 			},
 		}`
 
-	config := TestConfig(buildDir, android.Fuchsia, nil, bp, nil)
-	ctx := testCcWithConfig(t, config)
-	ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
+	result := ccFixtureFactory.Extend(PrepareForTestOnFuchsia).RunTestWithBp(t, bp)
+	ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
 	var objs []string
 	for _, o := range ld.Inputs {
 		objs = append(objs, o.Base())
 	}
-	if len(objs) != 2 || objs[0] != "foo.o" || objs[1] != "bar.o" {
-		t.Errorf("inputs of libTest must be []string{\"foo.o\", \"bar.o\"}, but was %#v.", objs)
-	}
+	result.AssertArrayString("libTest inputs", []string{"foo.o", "bar.o"}, objs)
 }
 
 func TestVendorSrc(t *testing.T) {
@@ -3397,24 +3420,16 @@
 		}
 	`
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.Debuggable = BoolPtr(true)
+	result := ccFixtureFactory.Extend(
+		android.PrepareForTestWithVariables,
 
-	ctx := CreateTestContext(config)
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("variable", android.VariableMutator).Parallel()
-	})
-	ctx.Register()
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Debuggable = BoolPtr(true)
+		}),
+	).RunTestWithBp(t, bp)
 
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module)
-	if !android.InList("-DBAR", libfoo.flags.Local.CppFlags) {
-		t.Errorf("expected -DBAR in cppflags, got %q", libfoo.flags.Local.CppFlags)
-	}
+	libfoo := result.Module("libfoo", "android_arm64_armv8-a_static").(*Module)
+	result.AssertStringListContains("cppflags", libfoo.flags.Local.CppFlags, "-DBAR")
 }
 
 func TestEmptyWholeStaticLibsAllowMissingDependencies(t *testing.T) {
@@ -3432,32 +3447,17 @@
 		}
 	`
 
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.Allow_missing_dependencies = BoolPtr(true)
+	result := ccFixtureFactory.Extend(
+		android.PrepareForTestWithAllowMissingDependencies,
+	).RunTestWithBp(t, bp)
 
-	ctx := CreateTestContext(config)
-	ctx.SetAllowMissingDependencies(true)
-	ctx.Register()
+	libbar := result.ModuleForTests("libbar", "android_arm64_armv8-a_static").Output("libbar.a")
+	result.AssertDeepEquals("libbar rule", android.ErrorRule, libbar.Rule)
 
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
+	result.AssertStringDoesContain("libbar error", libbar.Args["error"], "missing dependencies: libmissing")
 
-	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Output("libbar.a")
-	if g, w := libbar.Rule, android.ErrorRule; g != w {
-		t.Fatalf("Expected libbar rule to be %q, got %q", w, g)
-	}
-
-	if g, w := libbar.Args["error"], "missing dependencies: libmissing"; !strings.Contains(g, w) {
-		t.Errorf("Expected libbar error to contain %q, was %q", w, g)
-	}
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
-	if g, w := libfoo.Inputs.Strings(), libbar.Output.String(); !android.InList(w, g) {
-		t.Errorf("Expected libfoo.a to depend on %q, got %q", w, g)
-	}
-
+	libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
+	result.AssertStringListContains("libfoo.a dependencies", libfoo.Inputs.Strings(), libbar.Output.String())
 }
 
 func TestInstallSharedLibs(t *testing.T) {
@@ -3647,8 +3647,9 @@
 	}
 }
 
-func makeMemtagTestConfig(t *testing.T) android.Config {
-	templateBp := `
+var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
+	android.FixtureModifyMockFS(func(fs android.MockFS) {
+		templateBp := `
 		cc_test {
 			name: "%[1]s_test",
 			gtest: false,
@@ -3702,35 +3703,30 @@
 			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
 		}
 		`
-	subdirDefaultBp := fmt.Sprintf(templateBp, "default")
-	subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
-	subdirSyncBp := fmt.Sprintf(templateBp, "sync")
-	subdirAsyncBp := fmt.Sprintf(templateBp, "async")
+		subdirDefaultBp := fmt.Sprintf(templateBp, "default")
+		subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
+		subdirSyncBp := fmt.Sprintf(templateBp, "sync")
+		subdirAsyncBp := fmt.Sprintf(templateBp, "async")
 
-	mockFS := map[string][]byte{
-		"subdir_default/Android.bp": []byte(subdirDefaultBp),
-		"subdir_exclude/Android.bp": []byte(subdirExcludeBp),
-		"subdir_sync/Android.bp":    []byte(subdirSyncBp),
-		"subdir_async/Android.bp":   []byte(subdirAsyncBp),
-	}
-
-	return TestConfig(buildDir, android.Android, nil, "", mockFS)
-}
+		fs.Merge(android.MockFS{
+			"subdir_default/Android.bp": []byte(subdirDefaultBp),
+			"subdir_exclude/Android.bp": []byte(subdirExcludeBp),
+			"subdir_sync/Android.bp":    []byte(subdirSyncBp),
+			"subdir_async/Android.bp":   []byte(subdirAsyncBp),
+		})
+	}),
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+	}),
+)
 
 func TestSanitizeMemtagHeap(t *testing.T) {
 	variant := "android_arm64_armv8-a"
 
-	config := makeMemtagTestConfig(t)
-	config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
-	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
-	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
+	result := ccFixtureFactory.Extend(prepareForTestWithMemtagHeap).RunTest(t)
+	ctx := result.TestContext
 
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
@@ -3784,18 +3780,13 @@
 func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
 	variant := "android_arm64_armv8-a"
 
-	config := makeMemtagTestConfig(t)
-	config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
-	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
-	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
-	config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
+	result := ccFixtureFactory.Extend(
+		prepareForTestWithMemtagHeap,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.SanitizeDevice = []string{"memtag_heap"}
+		}),
+	).RunTest(t)
+	ctx := result.TestContext
 
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
@@ -3849,19 +3840,14 @@
 func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
 	variant := "android_arm64_armv8-a"
 
-	config := makeMemtagTestConfig(t)
-	config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
-	config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
-	config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
-	config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
-	config.TestProductVariables.SanitizeDeviceDiag = []string{"memtag_heap"}
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
+	result := ccFixtureFactory.Extend(
+		prepareForTestWithMemtagHeap,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.SanitizeDevice = []string{"memtag_heap"}
+			variables.SanitizeDeviceDiag = []string{"memtag_heap"}
+		}),
+	).RunTest(t)
+	ctx := result.TestContext
 
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
 	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index d441c57..04536fc 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -319,6 +319,9 @@
 	if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
 		return relativeFilePathFlag
 	}
+	if strings.HasPrefix(parameter, "-fprofile-sample-use") {
+		return relativeFilePathFlag
+	}
 	return flag
 }
 
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 7c20dd5..c4563e2 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -31,7 +31,6 @@
 		}
 		checks := strings.Join([]string{
 			"-*",
-			"abseil-*",
 			"android-*",
 			"bugprone-*",
 			"cert-*",
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 1035df3..b0344af 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -136,7 +136,7 @@
 
 func getMacTools(ctx android.PackageVarContext) *macPlatformTools {
 	macTools.once.Do(func() {
-		xcrunTool := ctx.Config().NonHermeticHostSystemTool("xcrun")
+		xcrunTool := "/usr/bin/xcrun"
 
 		xcrun := func(args ...string) string {
 			if macTools.err != nil {
diff --git a/cc/library.go b/cc/library.go
index 0e6e107..6a3b876 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -230,6 +230,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
 	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+	module.bazelHandler = &staticLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -406,6 +407,49 @@
 	collectedSnapshotHeaders android.Paths
 }
 
+type staticLibraryBazelHandler struct {
+	bazelHandler
+
+	module *Module
+}
+
+func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	bazelCtx := ctx.Config().BazelContext
+	outputPaths, objPaths, ok := bazelCtx.GetAllFilesAndCcObjectFiles(label, ctx.Arch().ArchType)
+	if ok {
+		if len(outputPaths) != 1 {
+			// TODO(cparsons): This is actually expected behavior for static libraries with no srcs.
+			// We should support this.
+			ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, objPaths)
+			return false
+		}
+		outputFilePath := android.PathForBazelOut(ctx, outputPaths[0])
+		handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+
+		objFiles := make(android.Paths, len(objPaths))
+		for i, objPath := range objPaths {
+			objFiles[i] = android.PathForBazelOut(ctx, objPath)
+		}
+		objects := Objects{
+			objFiles: objFiles,
+		}
+
+		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+			StaticLibrary: outputFilePath,
+			ReuseObjects:  objects,
+			Objects:       objects,
+
+			// TODO(cparsons): Include transitive static libraries in this provider to support
+			// static libraries with deps.
+			TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
+				Direct(outputFilePath).
+				Build(),
+		})
+		handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
+	}
+	return ok
+}
+
 // collectHeadersForSnapshot collects all exported headers from library.
 // It globs header files in the source tree for exported include directories,
 // and tracks generated header files separately.
diff --git a/cc/object.go b/cc/object.go
index 32347b8..126bd65 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -46,6 +46,26 @@
 	Properties ObjectLinkerProperties
 }
 
+type objectBazelHandler struct {
+	bazelHandler
+
+	module *Module
+}
+
+func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	bazelCtx := ctx.Config().BazelContext
+	objPaths, ok := bazelCtx.GetCcObjectFiles(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 {
 	// list of modules that should only provide headers for this module.
 	Header_libs []string `android:"arch_variant,variant_prepend"`
@@ -80,6 +100,7 @@
 		baseLinker: NewBaseLinker(module.sanitize),
 	}
 	module.compiler = NewBaseCompiler()
+	module.bazelHandler = &objectBazelHandler{module: module}
 
 	// Clang's address-significance tables are incompatible with ld -r.
 	module.compiler.appendCflags([]string{"-fno-addrsig"})
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 2cd18cb..6b9a3d5 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -246,7 +246,7 @@
 
 	module.AddProperties(&prebuilt.properties)
 
-	srcsSupplier := func(ctx android.BaseModuleContext) []string {
+	srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
 		return prebuilt.prebuiltSrcs(ctx)
 	}
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index ee4de6e..20274b2 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -23,27 +23,16 @@
 	"github.com/google/blueprint"
 )
 
-func testPrebuilt(t *testing.T, bp string, fs map[string][]byte, handlers ...configCustomizer) *android.TestContext {
-	config := TestConfig(buildDir, android.Android, nil, bp, fs)
-	ctx := CreateTestContext(config)
+var prebuiltFixtureFactory = ccFixtureFactory.Extend(
+	android.PrepareForTestWithAndroidMk,
+)
 
-	// Enable androidmk support.
-	// * Register the singleton
-	// * Configure that we are inside make
-	// * Add CommonOS to ensure that androidmk processing works.
-	android.RegisterAndroidMkBuildComponents(ctx)
-	android.SetKatiEnabledForTests(config)
+func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
+	result := prebuiltFixtureFactory.Extend(
+		fs.AddToFixture(),
+	).Extend(handlers...).RunTestWithBp(t, bp)
 
-	for _, handler := range handlers {
-		handler(config)
-	}
-
-	ctx.Register()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-	return ctx
+	return result.TestContext
 }
 
 type configCustomizer func(config android.Config)
@@ -370,9 +359,11 @@
 	assertString(t, static2.OutputFile().Path().Base(), "libf.a")
 
 	// With SANITIZE_TARGET=hwaddress
-	ctx = testPrebuilt(t, bp, fs, func(config android.Config) {
-		config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
-	})
+	ctx = testPrebuilt(t, bp, fs,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.SanitizeDevice = []string{"hwaddress"}
+		}),
+	)
 
 	shared_rule = ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip")
 	assertString(t, shared_rule.Input.String(), "hwasan/libf.so")
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 8218d97..cd09e6e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -404,12 +404,6 @@
 		s.Diag.Cfi = boolPtr(false)
 	}
 
-	// Also disable CFI for arm32 until b/35157333 is fixed.
-	if ctx.Arch().ArchType == android.Arm {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
-	}
-
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
 	if ctx.Arch().ArchType != android.Arm64 {
 		s.Hwaddress = nil
diff --git a/cc/testing.go b/cc/testing.go
index 45e5312..d8adc61 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -25,7 +25,6 @@
 	RegisterBinaryBuildComponents(ctx)
 	RegisterLibraryBuildComponents(ctx)
 	RegisterLibraryHeadersBuildComponents(ctx)
-	genrule.RegisterGenruleBuildComponents(ctx)
 
 	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
 	ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
@@ -38,7 +37,31 @@
 }
 
 func GatherRequiredDepsForTest(oses ...android.OsType) string {
-	ret := `
+	ret := commonDefaultModules()
+
+	supportLinuxBionic := false
+	for _, os := range oses {
+		if os == android.Fuchsia {
+			ret += withFuchsiaModules()
+		}
+		if os == android.Windows {
+			ret += withWindowsModules()
+		}
+		if os == android.LinuxBionic {
+			supportLinuxBionic = true
+			ret += withLinuxBionic()
+		}
+	}
+
+	if !supportLinuxBionic {
+		ret += withoutLinuxBionic()
+	}
+
+	return ret
+}
+
+func commonDefaultModules() string {
+	return `
 		toolchain_library {
 			name: "libatomic",
 			defaults: ["linux_bionic_supported"],
@@ -476,23 +499,10 @@
 			name: "note_memtag_heap_sync",
 		}
 	`
+}
 
-	supportLinuxBionic := false
-	for _, os := range oses {
-		if os == android.Fuchsia {
-			ret += `
-		cc_library {
-			name: "libbioniccompat",
-			stl: "none",
-		}
-		cc_library {
-			name: "libcompiler_rt",
-			stl: "none",
-		}
-		`
-		}
-		if os == android.Windows {
-			ret += `
+func withWindowsModules() string {
+	return `
 		toolchain_library {
 			name: "libwinpthread",
 			host_supported: true,
@@ -505,10 +515,23 @@
 			src: "",
 		}
 		`
+}
+
+func withFuchsiaModules() string {
+	return `
+		cc_library {
+			name: "libbioniccompat",
+			stl: "none",
 		}
-		if os == android.LinuxBionic {
-			supportLinuxBionic = true
-			ret += `
+		cc_library {
+			name: "libcompiler_rt",
+			stl: "none",
+		}
+		`
+}
+
+func withLinuxBionic() string {
+	return `
 				cc_binary {
 					name: "linker",
 					defaults: ["linux_bionic_supported"],
@@ -548,23 +571,115 @@
 					},
 				}
 			`
-		}
-	}
+}
 
-	if !supportLinuxBionic {
-		ret += `
+func withoutLinuxBionic() string {
+	return `
 			cc_defaults {
 				name: "linux_bionic_supported",
 			}
 		`
-	}
-
-	return ret
 }
 
 func GatherRequiredFilesForTest(fs map[string][]byte) {
 }
 
+// The directory in which cc linux bionic default modules will be defined.
+//
+// Placing them here ensures that their location does not conflict with default test modules
+// defined by other packages.
+const linuxBionicDefaultsPath = "defaults/cc/linux-bionic/Android.bp"
+
+// The directory in which the default cc common test modules will be defined.
+//
+// Placing them here ensures that their location does not conflict with default test modules
+// defined by other packages.
+const DefaultCcCommonTestModulesDir = "defaults/cc/common/"
+
+// Test fixture preparer that will register most cc build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of cc
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithCcBuildComponents = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+		ctx.RegisterModuleType("cc_test", TestFactory)
+		ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
+		ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
+		ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
+		ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
+
+		RegisterVndkLibraryTxtTypes(ctx)
+	}),
+)
+
+// Preparer that will define default cc modules, e.g. standard prebuilt modules.
+var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers(
+	PrepareForTestWithCcBuildComponents,
+	// Place the default cc test modules that are common to all platforms in a location that will not
+	// conflict with default test modules defined by other packages.
+	android.FixtureAddTextFile(DefaultCcCommonTestModulesDir+"Android.bp", commonDefaultModules()),
+	// Disable linux bionic by default.
+	android.FixtureAddTextFile(linuxBionicDefaultsPath, withoutLinuxBionic()),
+)
+
+// Prepare a fixture to use all cc module types, mutators and singletons fully.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithCc = android.GroupFixturePreparers(
+	android.PrepareForIntegrationTestWithAndroid,
+	genrule.PrepareForIntegrationTestWithGenrule,
+	PrepareForTestWithCcDefaultModules,
+)
+
+// The preparer to include if running a cc related test for windows.
+var PrepareForTestOnWindows = android.GroupFixturePreparers(
+	// Place the default cc test modules for windows platforms in a location that will not conflict
+	// with default test modules defined by other packages.
+	android.FixtureAddTextFile("defaults/cc/windows/Android.bp", withWindowsModules()),
+)
+
+// The preparer to include if running a cc related test for linux bionic.
+var PrepareForTestOnLinuxBionic = android.GroupFixturePreparers(
+	// Enable linux bionic
+	//
+	// Can be used after PrepareForTestWithCcDefaultModules to override its default behavior of
+	// disabling linux bionic, hence why this uses FixtureOverrideTextFile.
+	android.FixtureOverrideTextFile(linuxBionicDefaultsPath, withLinuxBionic()),
+)
+
+// The preparer to include if running a cc related test for fuchsia.
+var PrepareForTestOnFuchsia = android.GroupFixturePreparers(
+	// Place the default cc test modules for fuschia in a location that will not conflict with default
+	// test modules defined by other packages.
+	android.FixtureAddTextFile("defaults/cc/fuschia/Android.bp", withFuchsiaModules()),
+	android.PrepareForTestSetDeviceToFuchsia,
+)
+
+// This adds some additional modules and singletons which might negatively impact the performance
+// of tests so they are not included in the PrepareForIntegrationTestWithCc.
+var PrepareForTestWithCcIncludeVndk = android.GroupFixturePreparers(
+	PrepareForIntegrationTestWithCc,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		vendorSnapshotImageSingleton.init(ctx)
+		recoverySnapshotImageSingleton.init(ctx)
+		ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
+	}),
+)
+
+// TestConfig is the legacy way of creating a test Config for testing cc modules.
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func TestConfig(buildDir string, os android.OsType, env map[string]string,
 	bp string, fs map[string][]byte) android.Config {
 
@@ -581,7 +696,7 @@
 
 	var config android.Config
 	if os == android.Fuchsia {
-		config = android.TestArchConfigFuchsia(buildDir, env, bp, mockFS)
+		panic("Fuchsia not supported use test fixture instead")
 	} else {
 		config = android.TestArchConfig(buildDir, env, bp, mockFS)
 	}
@@ -589,8 +704,14 @@
 	return config
 }
 
+// CreateTestContext is the legacy way of creating a TestContext for testing cc modules.
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func CreateTestContext(config android.Config) *android.TestContext {
 	ctx := android.NewTestArchContext(config)
+	genrule.RegisterGenruleBuildComponents(ctx)
 	ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
 	ctx.RegisterModuleType("cc_test", TestFactory)
 	ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
@@ -598,13 +719,15 @@
 	ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
+
 	vendorSnapshotImageSingleton.init(ctx)
 	recoverySnapshotImageSingleton.init(ctx)
+	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	RegisterVndkLibraryTxtTypes(ctx)
+
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	android.RegisterPrebuiltMutators(ctx)
 	RegisterRequiredBuildComponentsForTest(ctx)
-	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 
 	return ctx
 }
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 6a0a87b..9f09224 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -25,7 +25,6 @@
         "soong",
         "soong-android",
         "soong-bp2build",
-        "soong-env",
         "soong-ui-metrics_proto",
     ],
     srcs: [
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 3a6feca..4586f44 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -21,6 +21,7 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/shared"
 	"github.com/google/blueprint/bootstrap"
 
 	"android/soong/android"
@@ -28,13 +29,21 @@
 )
 
 var (
+	topDir            string
+	outDir            string
 	docFile           string
 	bazelQueryViewDir string
+	delveListen       string
+	delvePath         string
 )
 
 func init() {
+	flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
+	flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
+	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
+	flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
-	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory")
+	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -80,18 +89,23 @@
 }
 
 func main() {
-	android.ReexecWithDelveMaybe()
 	flag.Parse()
 
+	shared.ReexecWithDelveMaybe(delveListen, delvePath)
+	android.InitSandbox(topDir)
+	android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
+
 	// The top-level Blueprints file is passed as the first argument.
 	srcDir := filepath.Dir(flag.Arg(0))
 	var ctx *android.Context
 	configuration := newConfig(srcDir)
 	extraNinjaDeps := []string{configuration.ProductVariablesFileName}
 
-	// Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable
-	// and soong_build will rerun when it is set for the first time.
-	if listen := configuration.Getenv("SOONG_DELVE"); listen != "" {
+	// These two are here so that we restart a non-debugged soong_build when the
+	// user sets SOONG_DELVE the first time.
+	configuration.Getenv("SOONG_DELVE")
+	configuration.Getenv("SOONG_DELVE_PATH")
+	if shared.IsDebugging() {
 		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
 		// enabled even if it completed successfully.
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
@@ -136,7 +150,8 @@
 	if bazelQueryViewDir != "" {
 		// Run the code-generation phase to convert BazelTargetModules to BUILD files.
 		codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
-		if err := createBazelQueryView(codegenContext, bazelQueryViewDir); err != nil {
+		absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
+		if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
 		}
diff --git a/cmd/soong_env/soong_env.go b/cmd/soong_env/soong_env.go
deleted file mode 100644
index 8020b17..0000000
--- a/cmd/soong_env/soong_env.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2015 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.
-
-// soong_env determines if the given soong environment file (usually ".soong.environment") is stale
-// by comparing its contents to the current corresponding environment variable values.
-// It fails if the file cannot be opened or corrupted, or its contents differ from the current
-// values.
-
-package main
-
-import (
-	"flag"
-	"fmt"
-	"os"
-
-	"android/soong/env"
-)
-
-func usage() {
-	fmt.Fprintf(os.Stderr, "usage: soong_env env_file\n")
-	fmt.Fprintf(os.Stderr, "exits with success if the environment varibles in env_file match\n")
-	fmt.Fprintf(os.Stderr, "the current environment\n")
-	flag.PrintDefaults()
-	os.Exit(2)
-}
-
-// This is a simple executable packaging, and the real work happens in env.StaleEnvFile.
-func main() {
-	flag.Parse()
-
-	if flag.NArg() != 1 {
-		usage()
-	}
-
-	stale, err := env.StaleEnvFile(flag.Arg(0))
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
-		os.Exit(1)
-	}
-
-	if stale {
-		os.Exit(1)
-	}
-
-	os.Exit(0)
-}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 74ede68..1c5e78a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -25,6 +25,7 @@
 	"strings"
 	"time"
 
+	"android/soong/shared"
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
@@ -118,6 +119,8 @@
 // Command is the type of soong_ui execution. Only one type of
 // execution is specified. The args are specific to the command.
 func main() {
+	shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
+
 	buildStarted := time.Now()
 
 	c, args, err := getCommand(os.Args)
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 36a5e2a..888466a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -85,7 +85,7 @@
 	Dex2oatImageXmx   string        // max heap size for dex2oat for the boot image
 	Dex2oatImageXms   string        // initial heap size for dex2oat for the boot image
 
-	// If true, downgrade the compiler filter of dexpreopt to "extract" when verify_uses_libraries
+	// If true, downgrade the compiler filter of dexpreopt to "verify" when verify_uses_libraries
 	// check fails, instead of failing the build. This will disable any AOT-compilation.
 	//
 	// The intended use case for this flag is to have a smoother migration path for the Java
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 6e0fe01..fdb00bd 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -288,12 +288,9 @@
 
 	} else {
 		// Other libraries or APKs for which the exact <uses-library> list is unknown.
-		// Pass special class loader context to skip the classpath and collision check.
-		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
-		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
-		// to the &.
+		// We assume the class loader context is empty.
 		rule.Command().
-			Text(`class_loader_context_arg=--class-loader-context=\&`).
+			Text(`class_loader_context_arg=--class-loader-context=PCL[]`).
 			Text(`stored_class_loader_context_arg=""`)
 	}
 
@@ -369,11 +366,11 @@
 		}
 		if module.EnforceUsesLibraries {
 			// If the verify_uses_libraries check failed (in this case status file contains a
-			// non-empty error message), then use "extract" compiler filter to avoid compiling any
+			// non-empty error message), then use "verify" compiler filter to avoid compiling any
 			// code (it would be rejected on device because of a class loader context mismatch).
 			cmd.Text("--compiler-filter=$(if test -s ").
 				Input(module.EnforceUsesLibrariesStatusFile).
-				Text(" ; then echo extract ; else echo " + compilerFilter + " ; fi)")
+				Text(" ; then echo verify ; else echo " + compilerFilter + " ; fi)")
 		} else {
 			cmd.FlagWithArg("--compiler-filter=", compilerFilter)
 		}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index bccbfc1..8e90295 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -45,3 +45,36 @@
 		}
 	`
 }
+
+// Prepares a test fixture by enabling dexpreopt.
+var PrepareForTestWithDexpreopt = FixtureModifyGlobalConfig(func(*GlobalConfig) {})
+
+// FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
+// configuration.
+func FixtureModifyGlobalConfig(configModifier func(dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
+	return android.FixtureModifyConfig(func(config android.Config) {
+		// Initialize the dexpreopt GlobalConfig to an empty structure. This has no effect if it has
+		// already been set.
+		pathCtx := android.PathContextForTesting(config)
+		dexpreoptConfig := GlobalConfigForTests(pathCtx)
+		SetTestGlobalConfig(config, dexpreoptConfig)
+
+		// Retrieve the existing configuration and modify it.
+		dexpreoptConfig = GetGlobalConfig(pathCtx)
+		configModifier(dexpreoptConfig)
+	})
+}
+
+// FixtureSetArtBootJars enables dexpreopt and sets the ArtApexJars property.
+func FixtureSetArtBootJars(bootJars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.ArtApexJars = android.CreateTestConfiguredJarList(bootJars)
+	})
+}
+
+// FixtureSetBootJars enables dexpreopt and sets the BootJars property.
+func FixtureSetBootJars(bootJars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars)
+	})
+}
diff --git a/env/Android.bp b/env/Android.bp
deleted file mode 100644
index c6a97b1..0000000
--- a/env/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-env",
-    pkgPath: "android/soong/env",
-    srcs: [
-        "env.go",
-    ],
-}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index b07ad91..6291325 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -54,6 +54,8 @@
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 }
 
+var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
+
 type prebuiltEtcProperties struct {
 	// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	Src *string `android:"path,arch_variant"`
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 585760d..6727e59 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -18,7 +18,6 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"reflect"
 	"testing"
 
 	"android/soong/android"
@@ -49,60 +48,19 @@
 	os.Exit(run())
 }
 
-func testPrebuiltEtcContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
-	fs := map[string][]byte{
+var prebuiltEtcFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	android.PrepareForTestWithArchMutator,
+	PrepareForTestWithPrebuiltEtc,
+	android.FixtureMergeMockFs(android.MockFS{
 		"foo.conf": nil,
 		"bar.conf": nil,
 		"baz.conf": nil,
-	}
+	}),
+)
 
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
-
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
-	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
-	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
-	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
-	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
-	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
-	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
-	ctx.Register()
-
-	return ctx, config
-}
-
-func testPrebuiltEtc(t *testing.T, bp string) (*android.TestContext, android.Config) {
-	t.Helper()
-
-	ctx, config := testPrebuiltEtcContext(t, bp)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	return ctx, config
-}
-
-func testPrebuiltEtcError(t *testing.T, pattern, bp string) {
-	t.Helper()
-
-	ctx, config := testPrebuiltEtcContext(t, bp)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	_, errs = ctx.PrepareBuildActions(config)
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
-}
 func TestPrebuiltEtcVariants(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -119,24 +77,24 @@
 		}
 	`)
 
-	foo_variants := ctx.ModuleVariantsForTests("foo.conf")
+	foo_variants := result.ModuleVariantsForTests("foo.conf")
 	if len(foo_variants) != 1 {
 		t.Errorf("expected 1, got %#v", foo_variants)
 	}
 
-	bar_variants := ctx.ModuleVariantsForTests("bar.conf")
+	bar_variants := result.ModuleVariantsForTests("bar.conf")
 	if len(bar_variants) != 2 {
 		t.Errorf("expected 2, got %#v", bar_variants)
 	}
 
-	baz_variants := ctx.ModuleVariantsForTests("baz.conf")
+	baz_variants := result.ModuleVariantsForTests("baz.conf")
 	if len(baz_variants) != 1 {
 		t.Errorf("expected 1, got %#v", bar_variants)
 	}
 }
 
 func TestPrebuiltEtcOutputPath(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -144,14 +102,12 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-	if p.outputFilePath.Base() != "foo.installed.conf" {
-		t.Errorf("expected foo.installed.conf, got %q", p.outputFilePath.Base())
-	}
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	result.AssertStringEquals("output file path", "foo.installed.conf", p.outputFilePath.Base())
 }
 
 func TestPrebuiltEtcGlob(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "my_foo",
 			src: "foo.*",
@@ -163,19 +119,15 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("my_foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-	if p.outputFilePath.Base() != "my_foo" {
-		t.Errorf("expected my_foo, got %q", p.outputFilePath.Base())
-	}
+	p := result.Module("my_foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	result.AssertStringEquals("my_foo output file path", "my_foo", p.outputFilePath.Base())
 
-	p = ctx.ModuleForTests("my_bar", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-	if p.outputFilePath.Base() != "bar.conf" {
-		t.Errorf("expected bar.conf, got %q", p.outputFilePath.Base())
-	}
+	p = result.Module("my_bar", "android_arm64_armv8-a").(*PrebuiltEtc)
+	result.AssertStringEquals("my_bar output file path", "bar.conf", p.outputFilePath.Base())
 }
 
 func TestPrebuiltEtcAndroidMk(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo",
 			src: "foo.conf",
@@ -197,13 +149,11 @@
 		"LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"},
 	}
 
-	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	mod := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
-			if !reflect.DeepEqual(value, expectedValue) {
-				t.Errorf("Incorrect %s '%s', expected '%s'", k, value, expectedValue)
-			}
+			result.AssertDeepEquals(k, expectedValue, value)
 		} else {
 			t.Errorf("No %s defined, saw %q", k, entries.EntryMap)
 		}
@@ -211,7 +161,7 @@
 }
 
 func TestPrebuiltEtcRelativeInstallPathInstallDirPath(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_etc {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -219,26 +169,26 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := buildDir + "/target/product/test_device/system/etc/bar"
-	if p.installDirPath.String() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
-	}
+	result.AssertStringEquals("install dir", expected, p.installDirPath.String())
 }
 
 func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
-	testPrebuiltEtcError(t, "relative_install_path is set. Cannot set sub_dir", `
-		prebuilt_etc {
-			name: "foo.conf",
-			src: "foo.conf",
-			sub_dir: "bar",
-			relative_install_path: "bar",
-		}
-	`)
+	prebuiltEtcFixtureFactory.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("relative_install_path is set. Cannot set sub_dir")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo.conf",
+				src: "foo.conf",
+				sub_dir: "bar",
+				relative_install_path: "bar",
+			}
+		`)
 }
 
 func TestPrebuiltEtcHost(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_etc_host {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -246,14 +196,14 @@
 	`)
 
 	buildOS := android.BuildOs.String()
-	p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
+	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
 	if !p.Host() {
 		t.Errorf("host bit is not set for a prebuilt_etc_host module.")
 	}
 }
 
 func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_usr_share {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -261,15 +211,13 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := buildDir + "/target/product/test_device/system/usr/share/bar"
-	if p.installDirPath.String() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
-	}
+	result.AssertStringEquals("install dir", expected, p.installDirPath.String())
 }
 
 func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
-	ctx, config := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_usr_share_host {
 			name: "foo.conf",
 			src: "foo.conf",
@@ -278,26 +226,22 @@
 	`)
 
 	buildOS := android.BuildOs.String()
-	p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
-	expected := filepath.Join(buildDir, "host", config.PrebuiltOS(), "usr", "share", "bar")
-	if p.installDirPath.String() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
-	}
+	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
+	expected := filepath.Join(buildDir, "host", result.Config.PrebuiltOS(), "usr", "share", "bar")
+	result.AssertStringEquals("install dir", expected, p.installDirPath.String())
 }
 
 func TestPrebuiltFontInstallDirPath(t *testing.T) {
-	ctx, _ := testPrebuiltEtc(t, `
+	result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
 		prebuilt_font {
 			name: "foo.conf",
 			src: "foo.conf",
 		}
 	`)
 
-	p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := buildDir + "/target/product/test_device/system/fonts"
-	if p.installDirPath.String() != expected {
-		t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
-	}
+	result.AssertStringEquals("install dir", expected, p.installDirPath.String())
 }
 
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
@@ -327,11 +271,9 @@
 	}}
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
-			ctx, _ := testPrebuiltEtc(t, tt.config)
-			p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-			if p.installDirPath.String() != tt.expectedPath {
-				t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath)
-			}
+			result := prebuiltEtcFixtureFactory.RunTestWithBp(t, tt.config)
+			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+			result.AssertStringEquals("install dir", tt.expectedPath, p.installDirPath.String())
 		})
 	}
 }
@@ -363,11 +305,9 @@
 	}}
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
-			ctx, _ := testPrebuiltEtc(t, tt.config)
-			p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-			if p.installDirPath.String() != tt.expectedPath {
-				t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath)
-			}
+			result := prebuiltEtcFixtureFactory.RunTestWithBp(t, tt.config)
+			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+			result.AssertStringEquals("install dir", tt.expectedPath, p.installDirPath.String())
 		})
 	}
 }
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 764f045..876e189 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -153,7 +153,7 @@
 		if vendor {
 			flag = "--vendor_cmdline "
 		}
-		cmd.FlagWithArg(flag, "\""+proptools.ShellEscape(cmdline)+"\"")
+		cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline))
 	}
 
 	headerVersion := proptools.String(b.properties.Header_version)
@@ -237,3 +237,13 @@
 func (b *bootimg) OutputPath() android.Path {
 	return b.output
 }
+
+var _ android.OutputFileProducer = (*bootimg)(nil)
+
+// Implements android.OutputFileProducer
+func (b *bootimg) OutputFiles(tag string) (android.Paths, error) {
+	if tag == "" {
+		return []android.Path{b.output}, nil
+	}
+	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index e547203..16b6037 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -208,3 +208,13 @@
 func (l *logicalPartition) OutputPath() android.Path {
 	return l.output
 }
+
+var _ android.OutputFileProducer = (*logicalPartition)(nil)
+
+// Implements android.OutputFileProducer
+func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) {
+	if tag == "" {
+		return []android.Path{l.output}, nil
+	}
+	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b4303a6..5349906 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -37,6 +37,27 @@
 	RegisterGenruleBuildComponents(android.InitRegistrationContext)
 }
 
+// Test fixture preparer that will register most genrule build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of genrule
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithGenRuleBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(RegisterGenruleBuildComponents),
+)
+
+// Prepare a fixture to use all genrule module types, mutators and singletons fully.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithGenrule = android.GroupFixturePreparers(
+	PrepareForTestWithGenRuleBuildComponents,
+)
+
 func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
 
@@ -206,7 +227,7 @@
 // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
 func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	filePaths, ok := bazelCtx.GetAllFiles(label)
+	filePaths, ok := bazelCtx.GetAllFiles(label, ctx.Arch().ArchType)
 	if ok {
 		var bazelOutputFiles android.Paths
 		for _, bazelOutputFile := range filePaths {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 2f5605e..0873704 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -17,8 +17,7 @@
 import (
 	"io/ioutil"
 	"os"
-	"reflect"
-	"strings"
+	"regexp"
 	"testing"
 
 	"android/soong/android"
@@ -51,22 +50,30 @@
 	os.Exit(run())
 }
 
-func testContext(config android.Config) *android.TestContext {
+var genruleFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	android.PrepareForTestWithArchMutator,
+	android.PrepareForTestWithDefaults,
 
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.RegisterModuleType("tool", toolFactory)
+	android.PrepareForTestWithFilegroup,
+	PrepareForTestWithGenRuleBuildComponents,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("tool", toolFactory)
+	}),
+	android.FixtureMergeMockFs(android.MockFS{
+		"tool":       nil,
+		"tool_file1": nil,
+		"tool_file2": nil,
+		"in1":        nil,
+		"in2":        nil,
+		"in1.txt":    nil,
+		"in2.txt":    nil,
+		"in3.txt":    nil,
+	}),
+)
 
-	RegisterGenruleBuildComponents(ctx)
-
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.Register()
-
-	return ctx
-}
-
-func testConfig(bp string, fs map[string][]byte) android.Config {
-	bp += `
+func testGenruleBp() string {
+	return `
 		tool {
 			name: "tool",
 		}
@@ -105,23 +112,6 @@
 			name: "empty",
 		}
 	`
-
-	mockFS := map[string][]byte{
-		"tool":       nil,
-		"tool_file1": nil,
-		"tool_file2": nil,
-		"in1":        nil,
-		"in2":        nil,
-		"in1.txt":    nil,
-		"in2.txt":    nil,
-		"in3.txt":    nil,
-	}
-
-	for k, v := range fs {
-		mockFS[k] = v
-	}
-
-	return android.TestArchConfig(buildDir, nil, bp, mockFS)
 }
 
 func TestGenruleCmd(t *testing.T) {
@@ -466,38 +456,28 @@
 			bp += test.prop
 			bp += "}\n"
 
-			config := testConfig(bp, nil)
-			config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
-
-			ctx := testContext(config)
-			ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
-
-			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-			if errs == nil {
-				_, errs = ctx.PrepareBuildActions(config)
+			var expectedErrors []string
+			if test.err != "" {
+				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
 			}
-			if errs == nil && test.err != "" {
-				t.Fatalf("want error %q, got no error", test.err)
-			} else if errs != nil && test.err == "" {
-				android.FailIfErrored(t, errs)
-			} else if test.err != "" {
-				if len(errs) != 1 {
-					t.Errorf("want 1 error, got %d errors:", len(errs))
-					for _, err := range errs {
-						t.Errorf("   %s", err.Error())
-					}
-					t.FailNow()
-				}
-				if !strings.Contains(errs[0].Error(), test.err) {
-					t.Fatalf("want %q, got %q", test.err, errs[0].Error())
-				}
+
+			result := genruleFixtureFactory.Extend(
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
+				}),
+				android.FixtureModifyContext(func(ctx *android.TestContext) {
+					ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
+				}),
+			).
+				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+				RunTestWithBp(t, testGenruleBp()+bp)
+
+			if expectedErrors != nil {
 				return
 			}
 
-			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
-			if g, w := gen.rawCommands[0], test.expect; w != g {
-				t.Errorf("want %q, got %q", w, g)
-			}
+			gen := result.Module("gen", "").(*Module)
+			result.AssertStringEquals("raw commands", test.expect, gen.rawCommands[0])
 		})
 	}
 }
@@ -557,25 +537,16 @@
 		},
 	}
 
-	config := testConfig(bp, nil)
-	ctx := testContext(config)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if errs == nil {
-		_, errs = ctx.PrepareBuildActions(config)
-	}
-	if errs != nil {
-		t.Fatal(errs)
-	}
+	result := genruleFixtureFactory.RunTestWithBp(t, testGenruleBp()+bp)
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
-			gen := ctx.ModuleForTests(test.name, "")
+			subResult := result.ResultForSubTest(t)
+			gen := subResult.ModuleForTests(test.name, "")
 			manifest := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
 			hash := manifest.Commands[0].GetInputHash()
 
-			if g, w := hash, test.expectedHash; g != w {
-				t.Errorf("Expected has %q, got %q", w, g)
-			}
+			subResult.AssertStringEquals("hash", test.expectedHash, hash)
 		})
 	}
 }
@@ -630,46 +601,27 @@
 			bp += test.prop
 			bp += "}\n"
 
-			config := testConfig(bp, nil)
-			ctx := testContext(config)
-
-			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-			if errs == nil {
-				_, errs = ctx.PrepareBuildActions(config)
+			var expectedErrors []string
+			if test.err != "" {
+				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
 			}
-			if errs == nil && test.err != "" {
-				t.Fatalf("want error %q, got no error", test.err)
-			} else if errs != nil && test.err == "" {
-				android.FailIfErrored(t, errs)
-			} else if test.err != "" {
-				if len(errs) != 1 {
-					t.Errorf("want 1 error, got %d errors:", len(errs))
-					for _, err := range errs {
-						t.Errorf("   %s", err.Error())
-					}
-					t.FailNow()
-				}
-				if !strings.Contains(errs[0].Error(), test.err) {
-					t.Fatalf("want %q, got %q", test.err, errs[0].Error())
-				}
+
+			result := genruleFixtureFactory.
+				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+				RunTestWithBp(t, testGenruleBp()+bp)
+
+			if expectedErrors != nil {
 				return
 			}
 
-			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
-			if g, w := gen.rawCommands, test.cmds; !reflect.DeepEqual(w, g) {
-				t.Errorf("want %q, got %q", w, g)
-			}
+			gen := result.Module("gen", "").(*Module)
+			result.AssertDeepEquals("cmd", test.cmds, gen.rawCommands)
 
-			if g, w := gen.outputDeps.Strings(), test.deps; !reflect.DeepEqual(w, g) {
-				t.Errorf("want deps %q, got %q", w, g)
-			}
+			result.AssertDeepEquals("deps", test.deps, gen.outputDeps.Strings())
 
-			if g, w := gen.outputFiles.Strings(), test.files; !reflect.DeepEqual(w, g) {
-				t.Errorf("want files %q, got %q", w, g)
-			}
+			result.AssertDeepEquals("files", test.files, gen.outputFiles.Strings())
 		})
 	}
-
 }
 
 func TestGenruleDefaults(t *testing.T) {
@@ -690,26 +642,16 @@
 					defaults: ["gen_defaults1", "gen_defaults2"],
 				}
 			`
-	config := testConfig(bp, nil)
-	ctx := testContext(config)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if errs == nil {
-		_, errs = ctx.PrepareBuildActions(config)
-	}
-	if errs != nil {
-		t.Fatal(errs)
-	}
-	gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+
+	result := genruleFixtureFactory.RunTestWithBp(t, testGenruleBp()+bp)
+
+	gen := result.Module("gen", "").(*Module)
 
 	expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
-	if gen.rawCommands[0] != expectedCmd {
-		t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
-	}
+	result.AssertStringEquals("cmd", expectedCmd, gen.rawCommands[0])
 
 	expectedSrcs := []string{"in1"}
-	if !reflect.DeepEqual(expectedSrcs, gen.properties.Srcs) {
-		t.Errorf("Expected srcs: %q, actual: %q", expectedSrcs, gen.properties.Srcs)
-	}
+	result.AssertDeepEquals("srcs", expectedSrcs, gen.properties.Srcs)
 }
 
 func TestGenruleWithBazel(t *testing.T) {
@@ -721,29 +663,18 @@
 		}
 	`
 
-	config := testConfig(bp, nil)
-	config.BazelContext = android.MockBazelContext{
-		AllFiles: map[string][]string{
-			"//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
+	result := genruleFixtureFactory.Extend(android.FixtureModifyConfig(func(config android.Config) {
+		config.BazelContext = android.MockBazelContext{
+			AllFiles: map[string][]string{
+				"//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
+	})).RunTestWithBp(t, testGenruleBp()+bp)
 
-	ctx := testContext(config)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if errs == nil {
-		_, errs = ctx.PrepareBuildActions(config)
-	}
-	if errs != nil {
-		t.Fatal(errs)
-	}
-	gen := ctx.ModuleForTests("foo", "").Module().(*Module)
+	gen := result.Module("foo", "").(*Module)
 
 	expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
 		"outputbase/execroot/__main__/bazeltwo.txt"}
-	if !reflect.DeepEqual(gen.outputFiles.Strings(), expectedOutputFiles) {
-		t.Errorf("Expected output files: %q, actual: %q", expectedOutputFiles, gen.outputFiles)
-	}
-	if !reflect.DeepEqual(gen.outputDeps.Strings(), expectedOutputFiles) {
-		t.Errorf("Expected output deps: %q, actual: %q", expectedOutputFiles, gen.outputDeps)
-	}
+	result.AssertDeepEquals("output files", expectedOutputFiles, gen.outputFiles.Strings())
+	result.AssertDeepEquals("output deps", expectedOutputFiles, gen.outputDeps.Strings())
 }
 
 type testTool struct {
diff --git a/java/Android.bp b/java/Android.bp
index 461b16d..9e2db83 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -78,6 +78,7 @@
         "plugin_test.go",
         "rro_test.go",
         "sdk_test.go",
+        "system_modules_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/java/aar.go b/java/aar.go
index 602d2c4..554ea67 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -805,14 +805,6 @@
 	return android.Paths{a.classpathFile}
 }
 
-func (a *AARImport) ImplementationJars() android.Paths {
-	return android.Paths{a.classpathFile}
-}
-
-func (a *AARImport) ResourceJars() android.Paths {
-	return nil
-}
-
 func (a *AARImport) ImplementationAndResourcesJars() android.Paths {
 	return android.Paths{a.classpathFile}
 }
@@ -825,22 +817,10 @@
 	return nil
 }
 
-func (a *AARImport) AidlIncludeDirs() android.Paths {
-	return nil
-}
-
 func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
 	return nil
 }
 
-func (d *AARImport) ExportedPlugins() (android.Paths, []string, bool) {
-	return nil, nil, false
-}
-
-func (a *AARImport) SrcJarArgs() ([]string, android.Paths) {
-	return nil, nil
-}
-
 var _ android.ApexModule = (*AARImport)(nil)
 
 // Implements android.ApexModule
diff --git a/java/android_manifest.go b/java/android_manifest.go
index c76bb2f..b30f3d2 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -91,7 +91,7 @@
 	if err != nil {
 		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
 	}
-	if UseApiFingerprint(ctx) {
+	if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
 		targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
 		deps = append(deps, ApiFingerprintPath(ctx))
 	}
@@ -100,7 +100,7 @@
 	if err != nil {
 		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 	}
-	if UseApiFingerprint(ctx) {
+	if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
 		minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
 		deps = append(deps, ApiFingerprintPath(ctx))
 	}
diff --git a/java/app.go b/java/app.go
index 39f06d0..e98fe31 100755
--- a/java/app.go
+++ b/java/app.go
@@ -122,8 +122,8 @@
 	// or an android_app_certificate module name in the form ":module".
 	Certificate *string
 
-	// Name of the signing certificate lineage file.
-	Lineage *string
+	// Name of the signing certificate lineage file or filegroup module.
+	Lineage *string `android:"path"`
 
 	// the package name of this app. The package name in the manifest file is used if one was not given.
 	Package_name *string
@@ -1217,6 +1217,15 @@
 	return optionalUsesLibs
 }
 
+// Helper function to replace string in a list.
+func replaceInList(list []string, oldstr, newstr string) {
+	for i, str := range list {
+		if str == oldstr {
+			list[i] = newstr
+		}
+	}
+}
+
 // Returns a map of module names of shared library dependencies to the paths
 // to their dex jars on host and on device.
 func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
@@ -1227,7 +1236,16 @@
 			if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
 				dep := ctx.OtherModuleName(m)
 				if lib, ok := m.(UsesLibraryDependency); ok {
-					clcMap.AddContext(ctx, tag.sdkVersion, dep,
+					libName := dep
+					if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
+						libName = *ulib.ProvidesUsesLib()
+						// Replace module name with library name in `uses_libs`/`optional_uses_libs`
+						// in order to pass verify_uses_libraries check (which compares these
+						// properties against library names written in the manifest).
+						replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
+						replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
+					}
+					clcMap.AddContext(ctx, tag.sdkVersion, libName,
 						lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{dep})
@@ -1262,6 +1280,14 @@
 	outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
 	statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
 
+	// Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the
+	// check is not necessary, and although it is good to have, it is difficult to maintain on
+	// non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
+	// various unrelated reasons, such as a failure to get manifest from an APK).
+	if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
+		return manifest
+	}
+
 	rule := android.NewRuleBuilder(pctx, ctx)
 	cmd := rule.Command().BuiltTool("manifest_check").
 		Flag("--enforce-uses-libraries").
@@ -1292,6 +1318,14 @@
 	outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
 	statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
 
+	// Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the
+	// check is not necessary, and although it is good to have, it is difficult to maintain on
+	// non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
+	// various unrelated reasons, such as a failure to get manifest from an APK).
+	if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
+		return apk
+	}
+
 	rule := android.NewRuleBuilder(pctx, ctx)
 	aapt := ctx.Config().HostToolPath(ctx, "aapt")
 	rule.Command().
diff --git a/java/app_import.go b/java/app_import.go
index 59eb10a..d4da64d 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -67,12 +67,15 @@
 	// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
 	Certificate *string
 
+	// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
+	Additional_certificates []string
+
 	// Set this flag to true if the prebuilt apk is already signed. The certificate property must not
 	// be set for presigned modules.
 	Presigned *bool
 
-	// Name of the signing certificate lineage file.
-	Lineage *string
+	// Name of the signing certificate lineage file or filegroup module.
+	Lineage *string `android:"path"`
 
 	// Sign with the default system dev certificate. Must be used judiciously. Most imported apps
 	// need to either specify a specific certificate or be presigned.
@@ -156,6 +159,16 @@
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
+	for _, cert := range a.properties.Additional_certificates {
+		cert = android.SrcIsModule(cert)
+		if cert != "" {
+			ctx.AddDependency(ctx.Module(), certificateTag, cert)
+		} else {
+			ctx.PropertyErrorf("additional_certificates",
+				`must be names of android_app_certificate modules in the form ":module"`)
+		}
+	}
+
 	a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
 }
 
@@ -244,10 +257,6 @@
 
 	srcApk := a.prebuilt.SingleSourcePath(ctx)
 
-	if a.usesLibrary.enforceUsesLibraries() {
-		srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
-	}
-
 	// TODO: Install or embed JNI libraries
 
 	// Uncompress JNI libraries in the apk
@@ -276,6 +285,10 @@
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 
+	if a.usesLibrary.enforceUsesLibraries() {
+		srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+	}
+
 	a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
 	if a.dexpreopter.uncompressedDex {
 		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
@@ -303,9 +316,6 @@
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
 		// Which makes processMainCert's behavior for the empty cert string WAI.
 		certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
-		if len(certificates) != 1 {
-			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
-		}
 		a.certificate = certificates[0]
 		signed := android.PathForModuleOut(ctx, "signed", apkFilename)
 		var lineageFile android.Path
diff --git a/java/app_import_test.go b/java/app_import_test.go
index dc31d07..cae41d0 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -109,14 +109,54 @@
 			name: "foo",
 			apk: "prebuilts/apk/app.apk",
 			certificate: "platform",
+			additional_certificates: [":additional_certificate"],
 			lineage: "lineage.bin",
 		}
+
+		android_app_certificate {
+			name: "additional_certificate",
+			certificate: "cert/additional_cert",
+		}
 	`)
 
 	variant := ctx.ModuleForTests("foo", "android_common")
 
-	// Check cert signing lineage flag.
 	signedApk := variant.Output("signed/foo.apk")
+	// Check certificates
+	certificatesFlag := signedApk.Args["certificates"]
+	expected := "build/make/target/product/security/platform.x509.pem " +
+		"build/make/target/product/security/platform.pk8 " +
+		"cert/additional_cert.x509.pem cert/additional_cert.pk8"
+	if expected != certificatesFlag {
+		t.Errorf("Incorrect certificates flags, expected: %q, got: %q", expected, certificatesFlag)
+	}
+	// Check cert signing lineage flag.
+	signingFlag := signedApk.Args["flags"]
+	expected = "--lineage lineage.bin"
+	if expected != signingFlag {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+	}
+}
+
+func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
+	ctx, _ := testJava(t, `
+	  android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			lineage: ":lineage_bin",
+		}
+
+		filegroup {
+			name: "lineage_bin",
+			srcs: ["lineage.bin"],
+		}
+	`)
+
+	variant := ctx.ModuleForTests("foo", "android_common")
+
+	signedApk := variant.Output("signed/foo.apk")
+	// Check cert signing lineage flag.
 	signingFlag := signedApk.Args["flags"]
 	expected := "--lineage lineage.bin"
 	if expected != signingFlag {
diff --git a/java/app_test.go b/java/app_test.go
index 349579e..78e1a57 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1576,6 +1576,31 @@
 			expectedLineage:     "--lineage lineage.bin",
 			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
+		{
+			name: "lineage from filegroup",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					certificate: ":new_certificate",
+					lineage: ":lineage_bin",
+					sdk_version: "current",
+				}
+
+				android_app_certificate {
+					name: "new_certificate",
+					certificate: "cert/new_cert",
+				}
+
+				filegroup {
+					name: "lineage_bin",
+					srcs: ["lineage.bin"],
+				}
+			`,
+			certificateOverride: "",
+			expectedLineage:     "--lineage lineage.bin",
+			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+		},
 	}
 
 	for _, test := range testCases {
@@ -2290,17 +2315,33 @@
 			sdk_version: "current",
 		}
 
+		// A library that has to use "provides_uses_lib", because:
+		//    - it is not an SDK library
+		//    - its library name is different from its module name
+		java_library {
+			name: "non-sdk-lib",
+			provides_uses_lib: "com.non.sdk.lib",
+			installable: true,
+			srcs: ["a.java"],
+		}
+
 		android_app {
 			name: "app",
 			srcs: ["a.java"],
-			libs: ["qux", "quuz.stubs"],
+			libs: [
+				"qux",
+				"quuz.stubs"
+			],
 			static_libs: [
 				"static-runtime-helper",
 				// statically linked component libraries should not pull their SDK libraries,
 				// so "fred" should not be added to class loader context
 				"fred.stubs",
 			],
-			uses_libs: ["foo"],
+			uses_libs: [
+				"foo",
+				"non-sdk-lib"
+			],
 			sdk_version: "current",
 			optional_uses_libs: [
 				"bar",
@@ -2312,7 +2353,11 @@
 			name: "prebuilt",
 			apk: "prebuilts/apk/app.apk",
 			certificate: "platform",
-			uses_libs: ["foo", "android.test.runner"],
+			uses_libs: [
+				"foo",
+				"non-sdk-lib",
+				"android.test.runner"
+			],
 			optional_uses_libs: [
 				"bar",
 				"baz",
@@ -2331,39 +2376,51 @@
 	prebuilt := ctx.ModuleForTests("prebuilt", "android_common")
 
 	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
-	manifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-	for _, w := range []string{"qux", "quuz", "runtime-library"} {
-		if !strings.Contains(manifestFixerArgs, "--uses-library "+w) {
-			t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
-		}
+	// This should not include explicit `uses_libs`/`optional_uses_libs` entries.
+	actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+	expectManifestFixerArgs := `--extract-native-libs=true ` +
+		`--uses-library qux ` +
+		`--uses-library quuz ` +
+		`--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer
+		`--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer
+		`--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer
+		`--uses-library runtime-library`
+	if actualManifestFixerArgs != expectManifestFixerArgs {
+		t.Errorf("unexpected manifest_fixer args:\n\texpect: %q\n\tactual: %q",
+			expectManifestFixerArgs, actualManifestFixerArgs)
 	}
 
-	// Test that all libraries are verified
-	cmd := app.Rule("verify_uses_libraries").RuleParams.Command
-	if w := "--uses-library foo"; !strings.Contains(cmd, w) {
-		t.Errorf("wanted %q in %q", w, cmd)
+	// Test that all libraries are verified (library order matters).
+	verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
+	verifyArgs := `--uses-library foo ` +
+		`--uses-library com.non.sdk.lib ` +
+		`--uses-library qux ` +
+		`--uses-library quuz ` +
+		`--uses-library runtime-library ` +
+		`--optional-uses-library bar ` +
+		`--optional-uses-library baz `
+	if !strings.Contains(verifyCmd, verifyArgs) {
+		t.Errorf("wanted %q in %q", verifyArgs, verifyCmd)
 	}
 
-	if w := "--optional-uses-library bar --optional-uses-library baz"; !strings.Contains(cmd, w) {
-		t.Errorf("wanted %q in %q", w, cmd)
+	// Test that all libraries are verified for an APK (library order matters).
+	verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command
+	verifyApkReqLibs := `uses_library_names="foo com.non.sdk.lib android.test.runner"`
+	verifyApkOptLibs := `optional_uses_library_names="bar baz"`
+	if !strings.Contains(verifyApkCmd, verifyApkReqLibs) {
+		t.Errorf("wanted %q in %q", verifyApkReqLibs, verifyApkCmd)
 	}
-
-	cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command
-
-	if w := `uses_library_names="foo android.test.runner"`; !strings.Contains(cmd, w) {
-		t.Errorf("wanted %q in %q", w, cmd)
-	}
-
-	if w := `optional_uses_library_names="bar baz"`; !strings.Contains(cmd, w) {
-		t.Errorf("wanted %q in %q", w, cmd)
+	if !strings.Contains(verifyApkCmd, verifyApkOptLibs) {
+		t.Errorf("wanted %q in %q", verifyApkOptLibs, verifyApkCmd)
 	}
 
 	// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs
-	cmd = app.Rule("dexpreopt").RuleParams.Command
+	cmd := app.Rule("dexpreopt").RuleParams.Command
 	w := `--target-context-for-sdk any ` +
 		`PCL[/system/framework/qux.jar]#` +
 		`PCL[/system/framework/quuz.jar]#` +
 		`PCL[/system/framework/foo.jar]#` +
+		`PCL[/system/framework/non-sdk-lib.jar]#` +
 		`PCL[/system/framework/bar.jar]#` +
 		`PCL[/system/framework/runtime-library.jar]`
 	if !strings.Contains(cmd, w) {
@@ -2393,6 +2450,7 @@
 	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
 	if w := `--target-context-for-sdk any` +
 		` PCL[/system/framework/foo.jar]` +
+		`#PCL[/system/framework/non-sdk-lib.jar]` +
 		`#PCL[/system/framework/android.test.runner.jar]` +
 		`#PCL[/system/framework/bar.jar] `; !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
diff --git a/java/boot_image.go b/java/boot_image.go
index 8a1e3c9..12e2874 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -25,10 +25,18 @@
 
 func init() {
 	RegisterBootImageBuildComponents(android.InitRegistrationContext)
+
+	android.RegisterSdkMemberType(&bootImageMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "boot_images",
+			SupportsSdk:  true,
+		},
+	})
 }
 
 func RegisterBootImageBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("boot_image", bootImageFactory)
+	ctx.RegisterModuleType("prebuilt_boot_image", prebuiltBootImageFactory)
 }
 
 type bootImageProperties struct {
@@ -41,7 +49,7 @@
 type BootImageModule struct {
 	android.ModuleBase
 	android.ApexModuleBase
-
+	android.SdkBase
 	properties bootImageProperties
 }
 
@@ -50,6 +58,7 @@
 	m.AddProperties(&m.properties)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitApexModule(m)
+	android.InitSdkAwareModule(m)
 	return m
 }
 
@@ -138,3 +147,74 @@
 	// Make it available for other modules.
 	ctx.SetProvider(BootImageInfoProvider, info)
 }
+
+type bootImageMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (b *bootImageMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (b *bootImageMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*BootImageModule)
+	return ok
+}
+
+func (b *bootImageMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_boot_image")
+}
+
+func (b *bootImageMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &bootImageSdkMemberProperties{}
+}
+
+type bootImageSdkMemberProperties struct {
+	android.SdkMemberPropertiesBase
+
+	Image_name string
+}
+
+func (b *bootImageSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+	module := variant.(*BootImageModule)
+
+	b.Image_name = module.properties.Image_name
+}
+
+func (b *bootImageSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+	if b.Image_name != "" {
+		propertySet.AddProperty("image_name", b.Image_name)
+	}
+}
+
+var _ android.SdkMemberType = (*bootImageMemberType)(nil)
+
+// A prebuilt version of the boot image module.
+//
+// At the moment this is basically just a boot image module that can be used as a prebuilt.
+// Eventually as more functionality is migrated into the boot image module from the singleton then
+// this will diverge.
+type prebuiltBootImageModule struct {
+	BootImageModule
+	prebuilt android.Prebuilt
+}
+
+func (module *prebuiltBootImageModule) Prebuilt() *android.Prebuilt {
+	return &module.prebuilt
+}
+
+func (module *prebuiltBootImageModule) Name() string {
+	return module.prebuilt.Name(module.ModuleBase.Name())
+}
+
+func prebuiltBootImageFactory() android.Module {
+	m := &prebuiltBootImageModule{}
+	m.AddProperties(&m.properties)
+	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+	// This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs
+	// array.
+	android.InitPrebuiltModule(m, &[]string{"placeholder"})
+	android.InitApexModule(m)
+	android.InitSdkAwareModule(m)
+	return m
+}
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
index a295782..65e590d 100644
--- a/java/boot_image_test.go
+++ b/java/boot_image_test.go
@@ -29,3 +29,12 @@
 		}
 `)
 }
+
+func TestUnknownPrebuiltBootImage(t *testing.T) {
+	testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", `
+		prebuilt_boot_image {
+			name: "unknown-boot-image",
+			image_name: "unknown",
+		}
+`)
+}
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index ee7d018..39fb04a 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -146,14 +146,6 @@
 	return d.headerJars
 }
 
-func (d *DeviceHostConverter) ImplementationJars() android.Paths {
-	return d.implementationJars
-}
-
-func (d *DeviceHostConverter) ResourceJars() android.Paths {
-	return d.resourceJars
-}
-
 func (d *DeviceHostConverter) ImplementationAndResourcesJars() android.Paths {
 	return d.implementationAndResourceJars
 }
@@ -174,14 +166,6 @@
 	return nil
 }
 
-func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string, bool) {
-	return nil, nil, false
-}
-
-func (d *DeviceHostConverter) SrcJarArgs() ([]string, android.Paths) {
-	return d.srcJarArgs, d.srcJarDeps
-}
-
 func (d *DeviceHostConverter) JacocoReportClassesFile() android.Path {
 	return nil
 }
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 86b1895..e94b20c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -210,6 +210,15 @@
 // apps instead of the Framework boot image extension (see DEXPREOPT_USE_ART_IMAGE and UseArtImage).
 //
 
+var artApexNames = []string{
+	"com.android.art",
+	"com.android.art.debug",
+	"com.android.art,testing",
+	"com.google.android.art",
+	"com.google.android.art.debug",
+	"com.google.android.art.testing",
+}
+
 func init() {
 	RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
 }
@@ -485,7 +494,14 @@
 
 	switch image.name {
 	case artBootImageName:
-		if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") {
+		inArtApex := false
+		for _, n := range artApexNames {
+			if apexInfo.InApexByBaseName(n) {
+				inArtApex = true
+				break
+			}
+		}
+		if inArtApex {
 			// ok: found the jar in the ART apex
 		} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
 			// exception (skip and continue): Jacoco platform variant for a coverage build
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 48bc244..a0d10ec 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"path/filepath"
-	"reflect"
 	"sort"
 	"testing"
 
@@ -44,17 +43,11 @@
 		}
 	`
 
-	config := testConfig(nil, bp, nil)
+	result := javaFixtureFactory.
+		Extend(dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar", "platform:baz")).
+		RunTestWithBp(t, bp)
 
-	pathCtx := android.PathContextForTesting(config)
-	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-	dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList([]string{"platform:foo", "platform:bar", "platform:baz"})
-	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
-	ctx := testContext(config)
-	run(t, ctx, config)
-
-	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
+	dexpreoptBootJars := result.SingletonForTests("dex_bootjars")
 	rule := dexpreoptBootJars.Output(ruleFile)
 
 	for i := range expectedInputs {
@@ -73,13 +66,9 @@
 	sort.Strings(outputs)
 	sort.Strings(expectedOutputs)
 
-	if !reflect.DeepEqual(inputs, expectedInputs) {
-		t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
-	}
+	result.AssertDeepEquals("inputs", expectedInputs, inputs)
 
-	if !reflect.DeepEqual(outputs, expectedOutputs) {
-		t.Errorf("want outputs %q\n got outputs %q", expectedOutputs, outputs)
-	}
+	result.AssertDeepEquals("outputs", expectedOutputs, outputs)
 }
 
 func TestDexpreoptBootJars(t *testing.T) {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 8f1644c..f0decec 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1203,8 +1203,14 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
-	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicitsRsp android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
+	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths,
+	implicitsRsp, homeDir android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
+	rule.Command().Text("rm -rf").Flag(homeDir.String())
+	rule.Command().Text("mkdir -p").Flag(homeDir.String())
+
 	cmd := rule.Command()
+	cmd.FlagWithArg("ANDROID_SDK_HOME=", homeDir.String())
+
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
 		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
 		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava")
@@ -1214,17 +1220,21 @@
 			execStrategy = remoteexec.LocalExecStrategy
 			labels["shallow"] = "true"
 		}
-		inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()}
+		inputs := []string{
+			ctx.Config().HostJavaToolPath(ctx, "metalava").String(),
+			homeDir.String(),
+		}
 		if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
 			inputs = append(inputs, strings.Split(v, ",")...)
 		}
 		cmd.Text((&remoteexec.REParams{
-			Labels:          labels,
-			ExecStrategy:    execStrategy,
-			Inputs:          inputs,
-			RSPFile:         implicitsRsp.String(),
-			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
-			Platform:        map[string]string{remoteexec.PoolKey: pool},
+			Labels:               labels,
+			ExecStrategy:         execStrategy,
+			Inputs:               inputs,
+			RSPFile:              implicitsRsp.String(),
+			ToolchainInputs:      []string{config.JavaCmd(ctx).String()},
+			Platform:             map[string]string{remoteexec.PoolKey: pool},
+			EnvironmentVariables: []string{"ANDROID_SDK_HOME"},
 		}).NoVarTemplate(ctx.Config()))
 	}
 
@@ -1302,9 +1312,9 @@
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
 
 	implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
-
+	homeDir := android.PathForModuleOut(ctx, "metalava-home")
 	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
-		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp,
+		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp, homeDir,
 		Bool(d.Javadoc.properties.Sandbox))
 	cmd.Implicits(d.Javadoc.implicits)
 
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index da2c48f..208ced7 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -148,7 +148,18 @@
 		primary = configurationName == ctx.ModuleName()
 
 		// A source module that has been replaced by a prebuilt can never be the primary module.
-		primary = primary && !module.IsReplacedByPrebuilt()
+		if module.IsReplacedByPrebuilt() {
+			ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(prebuilt android.Module) {
+				if h, ok := prebuilt.(hiddenAPIIntf); ok && h.bootDexJar() != nil {
+					primary = false
+				} else {
+					ctx.ModuleErrorf(
+						"hiddenapi has determined that the source module %q should be ignored as it has been"+
+							" replaced by the prebuilt module %q but unfortunately it does not provide a"+
+							" suitable boot dex jar", ctx.ModuleName(), ctx.OtherModuleName(prebuilt))
+				}
+			})
+		}
 	}
 	h.primary = primary
 }
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index c0f0e38..fb63820 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -126,6 +126,29 @@
 `, indexParams)
 }
 
+func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) {
+	config := testConfigWithBootJars(`
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			prefer: true,
+		}
+	`, []string{"platform:foo"}, nil)
+
+	ctx := testContextWithHiddenAPI(config)
+
+	runWithErrors(t, ctx, config,
+		"hiddenapi has determined that the source module \"foo\" should be ignored as it has been"+
+			" replaced by the prebuilt module \"prebuilt_foo\" but unfortunately it does not provide a"+
+			" suitable boot dex jar")
+}
+
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
 	ctx, _ := testHiddenAPIBootJars(t, `
 		java_import {
diff --git a/java/java.go b/java/java.go
index e471f0d..9e35835 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2046,13 +2046,6 @@
 	return j.installFile
 }
 
-func (j *Module) ResourceJars() android.Paths {
-	if j.resourceJar == nil {
-		return nil
-	}
-	return android.Paths{j.resourceJar}
-}
-
 func (j *Module) ImplementationAndResourcesJars() android.Paths {
 	if j.implementationAndResourcesJar == nil {
 		return nil
@@ -2069,17 +2062,6 @@
 	return j.classLoaderContexts
 }
 
-// ExportedPlugins returns the list of jars needed to run the exported plugins, the list of
-// classes for the plugins, and a boolean for whether turbine needs to be disabled due to plugins
-// that generate APIs.
-func (j *Module) ExportedPlugins() (android.Paths, []string, bool) {
-	return j.exportedPluginJars, j.exportedPluginClasses, j.exportedDisableTurbine
-}
-
-func (j *Module) SrcJarArgs() ([]string, android.Paths) {
-	return j.srcJarArgs, j.srcJarDeps
-}
-
 var _ logtagsProducer = (*Module)(nil)
 
 func (j *Module) logtags() android.Paths {
@@ -2515,6 +2497,11 @@
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if j.testProperties.Test_options.Unit_test == nil && ctx.Host() {
+		// TODO(b/): Clean temporary heuristic to avoid unexpected onboarding.
+		defaultUnitTest := !inList("tradefed", j.properties.Static_libs) && !inList("tradefed", j.properties.Libs) && !inList("cts", j.testProperties.Test_suites) && !inList("robolectric-host-android_all", j.properties.Static_libs) && !inList("robolectric-host-android_all", j.properties.Libs)
+		j.testProperties.Test_options.Unit_test = proptools.BoolPtr(defaultUnitTest)
+	}
 	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
 		j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
 
@@ -2675,6 +2662,7 @@
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
 	InitJavaModuleMultiTargets(module, android.HostSupported)
+
 	return module
 }
 
@@ -3046,17 +3034,6 @@
 	return android.Paths{j.combinedClasspathFile}
 }
 
-func (j *Import) ImplementationJars() android.Paths {
-	if j.combinedClasspathFile == nil {
-		return nil
-	}
-	return android.Paths{j.combinedClasspathFile}
-}
-
-func (j *Import) ResourceJars() android.Paths {
-	return nil
-}
-
 func (j *Import) ImplementationAndResourcesJars() android.Paths {
 	if j.combinedClasspathFile == nil {
 		return nil
@@ -3072,22 +3049,10 @@
 	return nil
 }
 
-func (j *Import) AidlIncludeDirs() android.Paths {
-	return j.exportAidlIncludeDirs
-}
-
 func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
 	return j.classLoaderContexts
 }
 
-func (j *Import) ExportedPlugins() (android.Paths, []string, bool) {
-	return nil, nil, false
-}
-
-func (j *Import) SrcJarArgs() ([]string, android.Paths) {
-	return nil, nil
-}
-
 var _ android.ApexModule = (*Import)(nil)
 
 // Implements android.ApexModule
diff --git a/java/java_test.go b/java/java_test.go
index 8407f24..670eefc 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -25,6 +25,7 @@
 	"strings"
 	"testing"
 
+	"android/soong/genrule"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -47,6 +48,26 @@
 	os.RemoveAll(buildDir)
 }
 
+// Factory to use to create fixtures for tests in this package.
+var javaFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	genrule.PrepareForTestWithGenRuleBuildComponents,
+	// Get the CC build components but not default modules.
+	cc.PrepareForTestWithCcBuildComponents,
+	// Include all the default java modules.
+	PrepareForTestWithJavaDefaultModules,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("java_plugin", PluginFactory)
+		ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
+
+		ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators)
+		ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+		ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
+	}),
+	javaMockFS().AddToFixture(),
+	dexpreopt.PrepareForTestWithDexpreopt,
+)
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
@@ -58,10 +79,20 @@
 	os.Exit(run())
 }
 
+// testConfig is a legacy way of creating a test Config for testing java modules.
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
 	return TestConfig(buildDir, env, bp, fs)
 }
 
+// testContext is a legacy way of creating a TestContext for testing java modules.
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func testContext(config android.Config) *android.TestContext {
 
 	ctx := android.NewTestArchContext(config)
@@ -79,6 +110,8 @@
 
 	android.RegisterPrebuiltMutators(ctx)
 
+	genrule.RegisterGenruleBuildComponents(ctx)
+
 	// Register module types and mutators from cc needed for JNI testing
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 
@@ -89,6 +122,11 @@
 	return ctx
 }
 
+// run is a legacy way of running tests of java modules.
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
 	t.Helper()
 
@@ -102,50 +140,92 @@
 	android.FailIfErrored(t, errs)
 }
 
+// testJavaError 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 testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
 	return testJavaErrorWithConfig(t, pattern, testConfig(nil, bp, nil))
 }
 
+// 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()
-	ctx := testContext(config)
-
+	// 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 := javaFixtureFactory.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+		RunTestWithConfig(t, config)
+	return result.TestContext, result.Config
+}
 
+// runWithErrors is a legacy way of running tests of java modules that expect errors.
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
+func runWithErrors(t *testing.T, ctx *android.TestContext, config android.Config, pattern string) {
 	ctx.Register()
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 	if len(errs) > 0 {
 		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return ctx, config
+		return
 	}
 	_, errs = ctx.PrepareBuildActions(config)
 	if len(errs) > 0 {
 		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return ctx, config
+		return
 	}
 
 	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
-	return ctx, config
+	return
 }
 
-func testJavaWithFS(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
+// testJavaWithFS runs tests using the javaFixtureFactory
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
+func testJavaWithFS(t *testing.T, bp string, fs android.MockFS) (*android.TestContext, android.Config) {
 	t.Helper()
-	return testJavaWithConfig(t, testConfig(nil, bp, fs))
+	result := javaFixtureFactory.Extend(fs.AddToFixture()).RunTestWithBp(t, bp)
+	return result.TestContext, result.Config
 }
 
+// testJava runs tests using the javaFixtureFactory
+//
+// Do not add any new usages of this, instead use the javaFixtureFactory directly as it makes it
+// much easier to customize the test behavior.
+//
+// If it is necessary to customize the behavior of an existing test that uses this then please first
+// convert the test to using javaFixtureFactory first and then in a following change add the
+// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
+// that it did not change the test behavior unexpectedly.
+//
+// deprecated
 func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	return testJavaWithFS(t, bp, nil)
+	result := javaFixtureFactory.RunTestWithBp(t, bp)
+	return result.TestContext, result.Config
 }
 
+// testJavaWithConfig runs tests using the javaFixtureFactory
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
 func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
 	t.Helper()
-	ctx := testContext(config)
-	run(t, ctx, config)
-
-	return ctx, config
+	result := javaFixtureFactory.RunTestWithConfig(t, config)
+	return result.TestContext, result.Config
 }
 
 func moduleToPath(name string) string {
@@ -159,6 +239,12 @@
 	}
 }
 
+// defaultModuleToPath constructs a path to the turbine generate jar for a default test module that
+// is defined in PrepareForIntegrationTestWithJava
+func defaultModuleToPath(name string) string {
+	return filepath.Join(buildDir, ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
+}
+
 func TestJavaLinkType(t *testing.T) {
 	testJava(t, `
 		java_library {
@@ -2343,75 +2429,11 @@
 		expected := "java.base=.:" + buildDir
 		checkPatchModuleFlag(t, ctx, "bar", expected)
 		expected = "java.base=" + strings.Join([]string{
-			".", buildDir, "dir", "dir2", "nested", moduleToPath("ext"), moduleToPath("framework")}, ":")
+			".", buildDir, "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":")
 		checkPatchModuleFlag(t, ctx, "baz", expected)
 	})
 }
 
-func TestJavaSystemModules(t *testing.T) {
-	ctx, _ := testJava(t, `
-		java_system_modules {
-			name: "system-modules",
-			libs: ["system-module1", "system-module2"],
-		}
-		java_library {
-			name: "system-module1",
-			srcs: ["a.java"],
-			sdk_version: "none",
-			system_modules: "none",
-		}
-		java_library {
-			name: "system-module2",
-			srcs: ["b.java"],
-			sdk_version: "none",
-			system_modules: "none",
-		}
-		`)
-
-	// check the existence of the module
-	systemModules := ctx.ModuleForTests("system-modules", "android_common")
-
-	cmd := systemModules.Rule("jarsTosystemModules")
-
-	// make sure the command compiles against the supplied modules.
-	for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
-		if !strings.Contains(cmd.Args["classpath"], module) {
-			t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
-				module)
-		}
-	}
-}
-
-func TestJavaSystemModulesImport(t *testing.T) {
-	ctx, _ := testJava(t, `
-		java_system_modules_import {
-			name: "system-modules",
-			libs: ["system-module1", "system-module2"],
-		}
-		java_import {
-			name: "system-module1",
-			jars: ["a.jar"],
-		}
-		java_import {
-			name: "system-module2",
-			jars: ["b.jar"],
-		}
-		`)
-
-	// check the existence of the module
-	systemModules := ctx.ModuleForTests("system-modules", "android_common")
-
-	cmd := systemModules.Rule("jarsTosystemModules")
-
-	// make sure the command compiles against the supplied modules.
-	for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
-		if !strings.Contains(cmd.Args["classpath"], module) {
-			t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
-				module)
-		}
-	}
-}
-
 func TestJavaLibraryWithSystemModules(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 021920a..874338d 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -19,10 +19,6 @@
 	"android/soong/java/config"
 )
 
-// This variable is effectively unused in pre-master branches, and is
-// included (with the same value as it has in AOSP) only to ease
-// merges between branches (see the comment in the
-// useLegacyCorePlatformApi() function):
 var legacyCorePlatformApiModules = []string{
 	"ahat-test-dump",
 	"android.car",
@@ -34,29 +30,44 @@
 	"art_cts_jvmti_test_library",
 	"art-gtest-jars-MyClassNatives",
 	"BackupFrameworksServicesRoboTests",
+	"backuplib",
 	"BandwidthEnforcementTest",
 	"BlockedNumberProvider",
 	"BluetoothInstrumentationTests",
 	"BluetoothMidiService",
+	"CarDeveloperOptions",
+	"CarService",
+	"CarServiceTest",
 	"car-apps-common",
+	"car-service-test-lib",
+	"car-service-test-static-lib",
 	"CertInstaller",
 	"ConnectivityManagerTest",
 	"ContactsProvider",
+	"CorePerfTests",
 	"core-tests-support",
+	"CtsAppExitTestCases",
 	"CtsContentTestCases",
 	"CtsIkeTestCases",
+	"CtsAppExitTestCases",
 	"CtsLibcoreWycheproofBCTestCases",
 	"CtsMediaTestCases",
 	"CtsNetTestCases",
 	"CtsNetTestCasesLatestSdk",
 	"CtsSecurityTestCases",
+	"CtsSuspendAppsTestCases",
 	"CtsUsageStatsTestCases",
+	"DeadpoolService",
+	"DeadpoolServiceBtServices",
+	"DeviceInfo",
+	"DiagnosticTools",
 	"DisplayCutoutEmulationEmu01Overlay",
 	"DocumentsUIPerfTests",
 	"DocumentsUITests",
 	"DownloadProvider",
 	"DownloadProviderTests",
 	"DownloadProviderUi",
+	"ds-car-docs", // for AAOS API documentation only
 	"DynamicSystemInstallationService",
 	"EmergencyInfo-lib",
 	"ethernet-service",
@@ -73,6 +84,7 @@
 	"FrameworksServicesRoboTests",
 	"FrameworksServicesTests",
 	"FrameworksUtilTests",
+	"FrameworksWifiTests",
 	"hid",
 	"hidl_test_java_java",
 	"hwbinder",
@@ -95,6 +107,9 @@
 	"platform_library-docs",
 	"PrintSpooler",
 	"RollbackTest",
+	"service-blobstore",
+	"service-connectivity",
+	"service-jobscheduler",
 	"services",
 	"services.accessibility",
 	"services.backup",
@@ -136,10 +151,6 @@
 	"wifi-service",
 }
 
-// This variable is effectively unused in pre-master branches, and is
-// included (with the same value as it has in AOSP) only to ease
-// merges between branches (see the comment in the
-// useLegacyCorePlatformApi() function):
 var legacyCorePlatformApiLookup = make(map[string]struct{})
 
 func init() {
@@ -149,12 +160,8 @@
 }
 
 func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool {
-	// In pre-master branches, we don't attempt to force usage of the stable
-	// version of the core/platform API. Instead, we always use the legacy
-	// version --- except in tests, where we always use stable, so that we
-	// can make the test assertions the same as other branches.
-	// This should be false in tests and true otherwise:
-	return ctx.Config().TestProductVariables == nil
+	_, found := legacyCorePlatformApiLookup[ctx.ModuleName()]
+	return found
 }
 
 func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
diff --git a/java/lint.go b/java/lint.go
index 8272595..9f677db 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -22,6 +22,8 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/java/config"
+	"android/soong/remoteexec"
 )
 
 type LintProperties struct {
@@ -172,8 +174,44 @@
 		extraLintCheckTag, extraCheckModules...)
 }
 
-func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
-	rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir, homeDir android.WritablePath, deps android.Paths) {
+// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
+// around.
+type lintPaths struct {
+	projectXML android.WritablePath
+	configXML  android.WritablePath
+	cacheDir   android.WritablePath
+	homeDir    android.WritablePath
+	srcjarDir  android.WritablePath
+
+	deps android.Paths
+
+	remoteInputs    android.Paths
+	remoteRSPInputs android.Paths
+}
+
+func lintRBEExecStrategy(ctx android.ModuleContext) string {
+	return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+}
+
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
+	var deps android.Paths
+	var remoteInputs android.Paths
+	var remoteRSPInputs android.Paths
+
+	// Paths passed to trackInputDependency will be added as dependencies of the rule that runs
+	// lint and passed as inputs to the remote execution proxy.
+	trackInputDependency := func(paths ...android.Path) {
+		deps = append(deps, paths...)
+		remoteInputs = append(remoteInputs, paths...)
+	}
+
+	// Paths passed to trackRSPDependency will be added as dependencies of the rule that runs
+	// lint, but the RSP file will be used by the remote execution proxy to find the files so that
+	// it doesn't overflow command line limits.
+	trackRSPDependency := func(paths android.Paths, rsp android.Path) {
+		deps = append(deps, paths...)
+		remoteRSPInputs = append(remoteRSPInputs, rsp)
+	}
 
 	var resourcesList android.WritablePath
 	if len(l.resources) > 0 {
@@ -184,17 +222,27 @@
 		resListRule := android.NewRuleBuilder(pctx, ctx)
 		resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
 		resListRule.Build("lint_resources_list", "lint resources list")
-		deps = append(deps, l.resources...)
+		trackRSPDependency(l.resources, resourcesList)
 	}
 
-	projectXMLPath = android.PathForModuleOut(ctx, "lint", "project.xml")
+	projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
 	// Lint looks for a lint.xml file next to the project.xml file, give it one.
-	configXMLPath = android.PathForModuleOut(ctx, "lint", "lint.xml")
-	cacheDir = android.PathForModuleOut(ctx, "lint", "cache")
-	homeDir = android.PathForModuleOut(ctx, "lint", "home")
+	configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
+	cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
+	homeDir := android.PathForModuleOut(ctx, "lint", "home")
 
 	srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
+	// TODO(ccross): this is a little fishy.  The files extracted from the srcjars are referenced
+	// by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
+	// not the extracted files.
+	trackRSPDependency(l.srcJars, srcJarList)
+
+	// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
+	// lint separately.
+	srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
+	rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList)
+	trackRSPDependency(l.srcs, srcsList)
 
 	cmd := rule.Command().
 		BuiltTool("lint-project-xml").
@@ -209,38 +257,40 @@
 		cmd.Flag("--test")
 	}
 	if l.manifest != nil {
-		deps = append(deps, l.manifest)
 		cmd.FlagWithArg("--manifest ", l.manifest.String())
+		trackInputDependency(l.manifest)
 	}
 	if l.mergedManifest != nil {
-		deps = append(deps, l.mergedManifest)
 		cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
+		trackInputDependency(l.mergedManifest)
 	}
 
-	// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
-	// lint separately.
-	cmd.FlagWithRspFileInputList("--srcs ", l.srcs)
-	deps = append(deps, l.srcs...)
+	cmd.FlagWithInput("--srcs ", srcsList)
 
 	cmd.FlagWithInput("--generated_srcs ", srcJarList)
-	deps = append(deps, l.srcJars...)
 
 	if resourcesList != nil {
 		cmd.FlagWithInput("--resources ", resourcesList)
 	}
 
 	if l.classes != nil {
-		deps = append(deps, l.classes)
 		cmd.FlagWithArg("--classes ", l.classes.String())
+		trackInputDependency(l.classes)
 	}
 
 	cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
-	deps = append(deps, l.classpath...)
+	trackInputDependency(l.classpath...)
 
 	cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
-	deps = append(deps, l.extraLintCheckJars...)
+	trackInputDependency(l.extraLintCheckJars...)
 
-	cmd.FlagWithArg("--root_dir ", "$PWD")
+	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") &&
+		lintRBEExecStrategy(ctx) != remoteexec.LocalExecStrategy {
+		// TODO(b/181912787): remove these and use "." instead.
+		cmd.FlagWithArg("--root_dir ", "/b/f/w")
+	} else {
+		cmd.FlagWithArg("--root_dir ", "$PWD")
+	}
 
 	// The cache tag in project.xml is relative to the root dir, or the project.xml file if
 	// the root dir is not set.
@@ -254,7 +304,18 @@
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	return projectXMLPath, configXMLPath, cacheDir, homeDir, deps
+	return lintPaths{
+		projectXML: projectXMLPath,
+		configXML:  configXMLPath,
+		cacheDir:   cacheDir,
+		homeDir:    homeDir,
+
+		deps: deps,
+
+		remoteInputs:    remoteInputs,
+		remoteRSPInputs: remoteRSPInputs,
+	}
+
 }
 
 // generateManifest adds a command to the rule to write a simple manifest that contains the
@@ -297,7 +358,7 @@
 		l.manifest = manifest
 	}
 
-	projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
+	lintPaths := l.writeLintProjectXML(ctx, rule)
 
 	html := android.PathForModuleOut(ctx, "lint-report.html")
 	text := android.PathForModuleOut(ctx, "lint-report.txt")
@@ -311,8 +372,9 @@
 		}
 	})
 
-	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
-	rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
+	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
+	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
+	rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
 
 	var annotationsZipPath, apiVersionsXMLPath android.Path
 	if ctx.Config().AlwaysUsePrebuiltSdks() {
@@ -323,17 +385,53 @@
 		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
 	}
 
-	cmd := rule.Command().
-		Text("(").
-		Flag("JAVA_OPTS=-Xmx3072m").
-		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
+	cmd := rule.Command()
+
+	cmd.Flag("JAVA_OPTS=-Xmx3072m").
+		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
 		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
-		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath).
-		Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
-		Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")).
+		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
+
+	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
+		pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
+		// TODO(b/181912787): this should be local fallback once the hack that passes /b/f/w in project.xml
+		// is removed.
+		execStrategy := lintRBEExecStrategy(ctx)
+		labels := map[string]string{"type": "tool", "name": "lint"}
+		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
+		remoteInputs := lintPaths.remoteInputs
+		remoteInputs = append(remoteInputs,
+			lintPaths.projectXML,
+			lintPaths.configXML,
+			lintPaths.homeDir,
+			lintPaths.cacheDir,
+			ctx.Config().HostJavaToolPath(ctx, "lint.jar"),
+			annotationsZipPath,
+			apiVersionsXMLPath,
+		)
+
+		cmd.Text((&remoteexec.REParams{
+			Labels:          labels,
+			ExecStrategy:    execStrategy,
+			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+			Inputs:          remoteInputs.Strings(),
+			OutputFiles:     android.Paths{html, text, xml}.Strings(),
+			RSPFile:         strings.Join(lintPaths.remoteRSPInputs.Strings(), ","),
+			EnvironmentVariables: []string{
+				"JAVA_OPTS",
+				"ANDROID_SDK_HOME",
+				"SDK_ANNOTATIONS",
+				"LINT_OPTS",
+				"LANG",
+			},
+			Platform: map[string]string{remoteexec.PoolKey: pool},
+		}).NoVarTemplate(ctx.Config()))
+	}
+
+	cmd.BuiltTool("lint").
 		Flag("--quiet").
-		FlagWithInput("--project ", projectXML).
-		FlagWithInput("--config ", lintXML).
+		FlagWithInput("--project ", lintPaths.projectXML).
+		FlagWithInput("--config ", lintPaths.configXML).
 		FlagWithOutput("--html ", html).
 		FlagWithOutput("--text ", text).
 		FlagWithOutput("--xml ", xml).
@@ -343,7 +441,9 @@
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
 		Flag("--exitcode").
 		Flags(l.properties.Lint.Flags).
-		Implicits(deps)
+		Implicit(annotationsZipPath).
+		Implicit(apiVersionsXMLPath).
+		Implicits(lintPaths.deps)
 
 	if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
 		cmd.FlagWithArg("--check ", checkOnly)
@@ -362,9 +462,9 @@
 		}
 	}
 
-	cmd.Text("|| (").Text("cat").Input(text).Text("; exit 7)").Text(")")
+	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
 
-	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
+	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
 
 	rule.Build("lint", "lint")
 
@@ -438,13 +538,13 @@
 	}
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
+		Rule:   android.CpIfChanged,
 		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
 		Output: copiedAnnotationsZipPath(ctx),
 	})
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
+		Rule:   android.CpIfChanged,
 		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
 		Output: copiedAPIVersionsXmlPath(ctx),
 	})
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c91b321..8a442b5 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -178,7 +178,7 @@
 	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver))
 	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver))
 
-	mctx.CreateModule(SystemModulesFactory, &props)
+	mctx.CreateModule(systemModulesImportFactory, &props)
 }
 
 func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) {
@@ -248,9 +248,9 @@
 	}
 
 	// Create incompatibilities tracking files for all modules, if we have a "next" api.
+	incompatibilities := make(map[string]bool)
 	if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" {
 		files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
-		incompatibilities := make(map[string]bool)
 		for _, f := range files {
 			localPath := strings.TrimPrefix(f, mydir)
 			module, _, scope := parseApiFilePath(mctx, localPath)
@@ -266,11 +266,11 @@
 
 			incompatibilities[referencedModule+"."+scope] = true
 		}
-		// Create empty incompatibilities files for remaining modules
-		for _, k := range android.SortedStringKeys(m) {
-			if _, ok := incompatibilities[k]; !ok {
-				createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
-			}
+	}
+	// Create empty incompatibilities files for remaining modules
+	for _, k := range android.SortedStringKeys(m) {
+		if _, ok := incompatibilities[k]; !ok {
+			createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
 		}
 	}
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index 98bb710..00f233e 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -31,13 +31,15 @@
 }
 
 var robolectricDefaultLibs = []string{
-	"Robolectric_all-target",
 	"mockito-robolectric-prebuilt",
 	"truth-prebuilt",
 	// TODO(ccross): this is not needed at link time
 	"junitxml",
 }
 
+const robolectricCurrentLib = "Robolectric_all-target"
+const robolectricPrebuiltLibPattern = "platform-robolectric-%s-prebuilt"
+
 var (
 	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
 	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
@@ -57,6 +59,10 @@
 		// Number of shards to use when running the tests.
 		Shards *int64
 	}
+
+	// The version number of a robolectric prebuilt to use from prebuilts/misc/common/robolectric
+	// instead of the one built from source in external/robolectric-shadows.
+	Robolectric_prebuilt_version *string
 }
 
 type robolectricTest struct {
@@ -94,6 +100,12 @@
 		ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module")
 	}
 
+	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
+		ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
+	} else {
+		ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib)
+	}
+
 	ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
 
 	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
@@ -298,7 +310,11 @@
 	if t := r.robolectricProperties.Test_options.Timeout; t != nil {
 		fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
 	}
-	fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
+	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
+		fmt.Fprintf(w, "-include prebuilts/misc/common/robolectric/%s/run_robotests.mk\n", v)
+	} else {
+		fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
+	}
 }
 
 // An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host
diff --git a/java/rro_test.go b/java/rro_test.go
index edbf170..061d9d3 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -20,6 +20,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/shared"
 )
 
 func TestRuntimeResourceOverlay(t *testing.T) {
@@ -105,7 +106,7 @@
 
 	// Check device location.
 	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{"/tmp/target/product/test_device/product/overlay"}
+	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay")}
 	if !reflect.DeepEqual(path, expectedPath) {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
 	}
@@ -114,7 +115,7 @@
 	m = ctx.ModuleForTests("foo_themed", "android_common")
 	androidMkEntries = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0]
 	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
+	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/faza")}
 	if !reflect.DeepEqual(path, expectedPath) {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
 	}
@@ -160,7 +161,7 @@
 
 	// Check device location.
 	path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
+	expectedPath := []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/default_theme")}
 	if !reflect.DeepEqual(path, expectedPath) {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
 	}
@@ -179,7 +180,7 @@
 
 	// Check device location.
 	path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
+	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/system/overlay")}
 	if !reflect.DeepEqual(path, expectedPath) {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 90823a0..b03f90c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -452,6 +452,7 @@
 	// that references the latest released API and remove API specification files.
 	// * API specification filegroup -> <dist-stem>.api.<scope>.latest
 	// * Removed API specification filegroup -> <dist-stem>-removed.api.<scope>.latest
+	// * API incompatibilities baseline filegroup -> <dist-stem>-incompatibilities.api.<scope>.latest
 	Dist_stem *string
 
 	// A compatibility mode that allows historical API-tracking files to not exist.
@@ -1059,6 +1060,9 @@
 		if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
 			missingApiModules = append(missingApiModules, m)
 		}
+		if m := android.SrcIsModule(module.latestIncompatibilitiesFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+			missingApiModules = append(missingApiModules, m)
+		}
 	}
 	if len(missingApiModules) != 0 && !module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api {
 		m := module.Name() + " is missing tracking files for previously released library versions.\n"
@@ -1165,6 +1169,10 @@
 	return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest"
 }
 
+func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string {
+	return ":" + module.distStem() + "-incompatibilities.api." + apiScope.name + ".latest"
+}
+
 func childModuleVisibility(childVisibility []string) []string {
 	if childVisibility == nil {
 		// No child visibility set. The child will use the visibility of the sdk_library.
@@ -1389,6 +1397,8 @@
 		props.Check_api.Last_released.Api_file = latestApiFilegroupName
 		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
 			module.latestRemovedApiFilegroupName(apiScope))
+		props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
+			module.latestIncompatibilitiesFilegroupName(apiScope))
 
 		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
 			// Enable api lint.
@@ -1772,6 +1782,8 @@
 	android.ApexModuleBase
 	android.SdkBase
 
+	hiddenAPI
+
 	properties sdkLibraryImportProperties
 
 	// Map from api scope to the scope specific property structure.
@@ -1786,6 +1798,9 @@
 	// The reference to the xml permissions module created by the source module.
 	// Is nil if the source module does not exist.
 	xmlPermissionsFileModule *sdkLibraryXml
+
+	// Path to the dex implementation jar obtained from the prebuilt_apex, if any.
+	dexJarFile android.Path
 }
 
 var _ SdkLibraryDependency = (*SdkLibraryImport)(nil)
@@ -1982,6 +1997,8 @@
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.generateCommonBuildActions(ctx)
 
+	var deapexerModule android.Module
+
 	// Record the paths to the prebuilt stubs library and stubs source.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
@@ -2007,6 +2024,11 @@
 				ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to)
 			}
 		}
+
+		// Save away the `deapexer` module on which this depends, if any.
+		if tag == android.DeapexerTag {
+			deapexerModule = to
+		}
 	})
 
 	// Populate the scope paths with information from the properties.
@@ -2019,6 +2041,32 @@
 		paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api)
 		paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api)
 	}
+
+	if ctx.Device() {
+		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
+		// obtained from the associated deapexer module.
+		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+		if ai.ForPrebuiltApex {
+			if deapexerModule == nil {
+				// This should never happen as a variant for a prebuilt_apex is only created if the
+				// deapxer module has been configured to export the dex implementation jar for this module.
+				ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
+					module.Name(), ai.ApexVariationName)
+			}
+
+			// Get the path of the dex implementation jar from the `deapexer` module.
+			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+			if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+				module.dexJarFile = dexOutputPath
+				module.initHiddenAPI(ctx, module.configurationName)
+				module.hiddenAPIExtractInformation(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0])
+			} else {
+				// This should never happen as a variant for a prebuilt_apex is only created if the
+				// prebuilt_apex has been configured to export the java library dex file.
+				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+			}
+		}
+	}
 }
 
 func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
@@ -2051,6 +2099,11 @@
 
 // to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarBuildPath() android.Path {
+	// The dex implementation jar extracted from the .apex file should be used in preference to the
+	// source.
+	if module.dexJarFile != nil {
+		return module.dexJarFile
+	}
 	if module.implLibraryModule == nil {
 		return nil
 	} else {
diff --git a/java/system_modules.go b/java/system_modules.go
index 95f71b8..8c69051 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -169,7 +169,13 @@
 	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars)
 }
 
-func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
+// ComponentDepsMutator is called before prebuilt modules without a corresponding source module are
+// renamed so unless the supplied libs specifically includes the prebuilt_ prefix this is guaranteed
+// to only add dependencies on source modules.
+//
+// The systemModuleLibsTag will prevent the prebuilt mutators from replacing this dependency so it
+// will never be changed to depend on a prebuilt either.
+func (system *SystemModules) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, systemModulesLibsTag, system.properties.Libs...)
 }
 
@@ -225,6 +231,15 @@
 	return &system.prebuilt
 }
 
+// ComponentDepsMutator is called before prebuilt modules without a corresponding source module are
+// renamed so as this adds a prebuilt_ prefix this is guaranteed to only add dependencies on source
+// modules.
+func (system *systemModulesImport) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, lib := range system.properties.Libs {
+		ctx.AddVariationDependencies(nil, systemModulesLibsTag, "prebuilt_"+lib)
+	}
+}
+
 type systemModulesSdkMemberType struct {
 	android.SdkMemberTypeBase
 }
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
new file mode 100644
index 0000000..abcd186
--- /dev/null
+++ b/java/system_modules_test.go
@@ -0,0 +1,112 @@
+// 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 (
+	"testing"
+
+	"android/soong/android"
+)
+
+func getModuleHeaderJarsAsNormalizedPaths(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)...)
+	}
+	return paths
+}
+
+var addSourceSystemModules = android.FixtureAddTextFile("source/Android.bp", `
+		java_system_modules {
+			name: "system-modules",
+			libs: ["system-module1", "system-module2"],
+		}
+		java_library {
+			name: "system-module1",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+		java_library {
+			name: "system-module2",
+			srcs: ["b.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+`)
+
+func TestJavaSystemModules(t *testing.T) {
+	result := javaFixtureFactory.RunTest(t, addSourceSystemModules)
+
+	// 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")
+	result.AssertArrayString("source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+}
+
+var addPrebuiltSystemModules = android.FixtureAddTextFile("prebuilts/Android.bp", `
+		java_system_modules_import {
+			name: "system-modules",
+			libs: ["system-module1", "system-module2"],
+		}
+		java_import {
+			name: "system-module1",
+			jars: ["a.jar"],
+		}
+		java_import {
+			name: "system-module2",
+			jars: ["b.jar"],
+		}
+`)
+
+func TestJavaSystemModulesImport(t *testing.T) {
+	result := javaFixtureFactory.RunTest(t, addPrebuiltSystemModules)
+
+	// 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")
+	result.AssertArrayString("renamed prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+}
+
+func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
+	result := javaFixtureFactory.RunTest(t,
+		addSourceSystemModules,
+		addPrebuiltSystemModules,
+	)
+
+	// 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")
+	result.AssertArrayString("source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+
+	// 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")
+	result.AssertArrayString("prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+}
diff --git a/java/testing.go b/java/testing.go
index 781106f..4e1997e 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -29,10 +29,40 @@
 	"github.com/google/blueprint"
 )
 
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
-	bp += GatherRequiredDepsForTest()
+const defaultJavaDir = "default/java"
 
-	mockFS := map[string][]byte{
+// Test fixture preparer that will register most java build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of java
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithJavaBuildComponents = android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest)
+
+// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules.
+var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers(
+	// Make sure that mutators and module types, e.g. prebuilt mutators available.
+	android.PrepareForTestWithAndroidBuildComponents,
+	// Make sure that all the module types used in the defaults are registered.
+	PrepareForTestWithJavaBuildComponents,
+	// The java default module definitions.
+	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", GatherRequiredDepsForTest()),
+)
+
+// Prepare a fixture to use all java module types, mutators and singletons fully.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithJava = android.GroupFixturePreparers(
+	cc.PrepareForIntegrationTestWithCc,
+	PrepareForTestWithJavaDefaultModules,
+)
+
+func javaMockFS() android.MockFS {
+	mockFS := android.MockFS{
 		"api/current.txt":        nil,
 		"api/removed.txt":        nil,
 		"api/system-current.txt": nil,
@@ -64,6 +94,14 @@
 		mockFS[k] = v
 	}
 
+	return mockFS
+}
+
+func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
+	bp += GatherRequiredDepsForTest()
+
+	mockFS := javaMockFS()
+
 	cc.GatherRequiredFilesForTest(mockFS)
 
 	for k, v := range fs {
@@ -240,6 +278,7 @@
 }
 
 func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) {
+	t.Helper()
 	actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n"))
 	expected = strings.TrimSpace(expected)
 	if actual != expected {
diff --git a/python/binary.go b/python/binary.go
index 416a7ee..372b8a8 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -20,10 +20,92 @@
 	"fmt"
 
 	"android/soong/android"
+	"android/soong/bazel"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
 	android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
+	android.RegisterBp2BuildMutator("python_binary_host", PythonBinaryBp2Build)
+}
+
+type bazelPythonBinaryAttributes struct {
+	Main           string
+	Srcs           bazel.LabelList
+	Data           bazel.LabelList
+	Python_version string
+}
+
+type bazelPythonBinary struct {
+	android.BazelTargetModuleBase
+	bazelPythonBinaryAttributes
+}
+
+func BazelPythonBinaryFactory() android.Module {
+	module := &bazelPythonBinary{}
+	module.AddProperties(&module.bazelPythonBinaryAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func (m *bazelPythonBinary) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelPythonBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
+	m, ok := ctx.Module().(*Module)
+	if !ok || !m.ConvertWithBp2build() {
+		return
+	}
+
+	// a Module can be something other than a python_binary_host
+	if ctx.ModuleType() != "python_binary_host" {
+		return
+	}
+
+	var main string
+	for _, propIntf := range m.GetProperties() {
+		if props, ok := propIntf.(*BinaryProperties); ok {
+			// main is optional.
+			if props.Main != nil {
+				main = *props.Main
+				break
+			}
+		}
+	}
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_binary_host modules
+	// under Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version string
+	if py3Enabled && py2Enabled {
+		panic(fmt.Errorf(
+			"error for '%s' module: bp2build's python_binary_host converter does not support "+
+				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
+	} else if py2Enabled {
+		python_version = "PY2"
+	} else {
+		// do nothing, since python_version defaults to PY3.
+	}
+
+	attrs := &bazelPythonBinaryAttributes{
+		Main:           main,
+		Srcs:           android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs),
+		Data:           android.BazelLabelForModuleSrc(ctx, m.properties.Data),
+		Python_version: python_version,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_binary rule.
+		Rule_class: "py_binary",
+	}
+
+	ctx.CreateBazelTargetModule(BazelPythonBinaryFactory, m.Name(), props, attrs)
 }
 
 type BinaryProperties struct {
@@ -81,6 +163,8 @@
 func PythonBinaryHostFactory() android.Module {
 	module, _ := NewBinary(android.HostSupported)
 
+	android.InitBazelModule(module)
+
 	return module.init()
 }
 
diff --git a/python/python.go b/python/python.go
index b3e3d13..a078c0b 100644
--- a/python/python.go
+++ b/python/python.go
@@ -125,6 +125,7 @@
 type Module struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.BazelModuleBase
 
 	properties      BaseProperties
 	protoProperties android.ProtoProperties
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index d6e2c0a..5f0426a 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -81,6 +81,9 @@
 	// ToolchainInputs is a list of paths or ninja variables pointing to the location of
 	// toolchain binaries used by the rule.
 	ToolchainInputs []string
+	// EnvironmentVariables is a list of environment variables whose values should be passed through
+	// to the remote execution.
+	EnvironmentVariables []string
 }
 
 func init() {
@@ -162,6 +165,10 @@
 		args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
 	}
 
+	if len(r.EnvironmentVariables) > 0 {
+		args += " --env_var_allowlist=" + strings.Join(r.EnvironmentVariables, ",")
+	}
+
 	return args + " -- "
 }
 
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index fc11d29..408d433 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -14,6 +14,7 @@
 		"external/rust",
 		"external/vm_tools/p9",
 		"frameworks/native/libs/binder/rust",
+		"frameworks/proto_logging/stats",
 		"packages/modules/DnsResolver",
 		"packages/modules/Virtualization",
 		"prebuilts/rust",
diff --git a/rust/testing.go b/rust/testing.go
index 1afe27e..5be71c9 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -17,6 +17,31 @@
 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),
+)
+
+// The directory in which rust test default modules will be defined.
+//
+// Placing them here ensures that their location does not conflict with default test modules
+// defined by other packages.
+const rustDefaultsDir = "defaults/rust/"
+
+// Preparer that will define default rust modules, e.g. standard prebuilt modules.
+var PrepareForTestWithRustDefaultModules = android.GroupFixturePreparers(
+	cc.PrepareForTestWithCcDefaultModules,
+	PrepareForTestWithRustBuildComponents,
+	android.FixtureAddTextFile(rustDefaultsDir+"Android.bp", GatherRequiredDepsForTest()),
+)
+
+// Preparer that will allow use of all rust modules fully.
+var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers(
+	PrepareForTestWithRustDefaultModules,
 )
 
 func GatherRequiredDepsForTest() string {
@@ -211,6 +236,7 @@
 	ctx := android.NewTestArchContext(config)
 	android.RegisterPrebuiltMutators(ctx)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	genrule.RegisterGenruleBuildComponents(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	RegisterRequiredBuildComponentsForTest(ctx)
 
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 0813f1a..18174a4 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -36,8 +36,9 @@
 # List of libraries installed on the platform that are needed for ART chroot
 # testing.
 PLATFORM_LIBRARIES=(
-  liblog
+  heapprofd_client_api
   libartpalette-system
+  liblog
 )
 
 # We want to create apex modules for all supported architectures.
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 8a3119c..6e49c6d 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -20,6 +20,7 @@
         "update.go",
     ],
     testSrcs: [
+        "boot_image_sdk_test.go",
         "bp_test.go",
         "cc_sdk_test.go",
         "exports_test.go",
diff --git a/sdk/boot_image_sdk_test.go b/sdk/boot_image_sdk_test.go
new file mode 100644
index 0000000..915253d
--- /dev/null
+++ b/sdk/boot_image_sdk_test.go
@@ -0,0 +1,63 @@
+// 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 sdk
+
+import "testing"
+
+func TestSnapshotWithBootImage(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			boot_images: ["mybootimage"],
+		}
+
+		boot_image {
+			name: "mybootimage",
+			image_name: "art",
+		}
+	`)
+
+	CheckSnapshot(result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_boot_image {
+    name: "mybootimage",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    image_name: "art",
+}
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_boot_image {
+    name: "mysdk_mybootimage@current",
+    sdk_member_name: "mybootimage",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    image_name: "art",
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    boot_images: ["mysdk_mybootimage@current"],
+}
+`),
+		checkAllCopyRules(""),
+	)
+}
diff --git a/sdk/bp_test.go b/sdk/bp_test.go
index e1edc51..a7164a5 100644
--- a/sdk/bp_test.go
+++ b/sdk/bp_test.go
@@ -54,7 +54,7 @@
 	return str
 }
 
-func checkPropertySetFixture(h *TestHelper, val interface{}, hasTags bool) {
+func checkPropertySetFixture(h android.TestHelper, val interface{}, hasTags bool) {
 	set := val.(*bpPropertySet)
 	h.AssertDeepEquals("wrong x value", "taxi", set.getValue("x"))
 	h.AssertDeepEquals("wrong y value", 1729, set.getValue("y"))
@@ -73,7 +73,7 @@
 }
 
 func TestAddPropertySimple(t *testing.T) {
-	h := &TestHelper{t}
+	h := android.TestHelper{t}
 	set := newPropertySet()
 	for name, val := range map[string]interface{}{
 		"x":   "taxi",
@@ -92,7 +92,7 @@
 }
 
 func TestAddPropertySubset(t *testing.T) {
-	h := &TestHelper{t}
+	h := android.TestHelper{t}
 	getFixtureMap := map[string]func() interface{}{
 		"property set":    propertySetFixture,
 		"property struct": propertyStructFixture,
@@ -139,7 +139,7 @@
 }
 
 func TestAddPropertySetNew(t *testing.T) {
-	h := &TestHelper{t}
+	h := android.TestHelper{t}
 	set := newPropertySet()
 	subset := set.AddPropertySet("sub")
 	subset.AddProperty("new", "d^^b")
@@ -147,7 +147,7 @@
 }
 
 func TestAddPropertySetExisting(t *testing.T) {
-	h := &TestHelper{t}
+	h := android.TestHelper{t}
 	set := propertySetFixture().(*bpPropertySet)
 	subset := set.AddPropertySet("sub")
 	subset.AddProperty("new", "d^^b")
@@ -181,7 +181,7 @@
 
 func TestTransformRemoveProperty(t *testing.T) {
 
-	helper := &TestHelper{t}
+	helper := android.TestHelper{t}
 
 	set := newPropertySet()
 	set.AddProperty("name", "name")
@@ -196,7 +196,7 @@
 
 func TestTransformRemovePropertySet(t *testing.T) {
 
-	helper := &TestHelper{t}
+	helper := android.TestHelper{t}
 
 	set := newPropertySet()
 	set.AddProperty("name", "name")
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 997d2f6..28c9e9e 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -21,7 +21,7 @@
 	"android/soong/cc"
 )
 
-var ccTestFs = map[string][]byte{
+var ccTestFs = android.MockFS{
 	"Test.cpp":                        nil,
 	"myinclude/Test.h":                nil,
 	"myinclude-android/AndroidTest.h": nil,
@@ -32,7 +32,7 @@
 	"some/where/stubslib.map.txt":     nil,
 }
 
-func testSdkWithCc(t *testing.T, bp string) *testSdkResult {
+func testSdkWithCc(t *testing.T, bp string) *android.TestResult {
 	t.Helper()
 	return testSdkWithFs(t, bp, ccTestFs)
 }
@@ -101,7 +101,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -251,6 +251,7 @@
 			uses_sdks: ["mysdk@1"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 
 		apex {
@@ -259,6 +260,7 @@
 			uses_sdks: ["mysdk@2"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 
 		apex {
@@ -266,6 +268,7 @@
 			native_shared_libs: ["sdkmember"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 	`)
 
@@ -350,7 +353,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -437,7 +440,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
@@ -483,7 +486,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -553,7 +556,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -612,7 +615,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mymodule_exports", "",
+	CheckSnapshot(result, "mymodule_exports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -697,7 +700,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -805,7 +808,15 @@
 }
 
 func TestSnapshotWithSingleHostOsType(t *testing.T) {
-	ctx, config := testSdkContext(`
+	result := sdkFixtureFactory.Extend(
+		ccTestFs.AddToFixture(),
+		cc.PrepareForTestOnLinuxBionic,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.Targets[android.LinuxBionic] = []android.Target{
+				{android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", false},
+			}
+		}),
+	).RunTestWithBp(t, `
 		cc_defaults {
 			name: "mydefaults",
 			device_supported: false,
@@ -846,11 +857,9 @@
 			],
 			stl: "none",
 		}
-	`, ccTestFs, []android.OsType{android.LinuxBionic})
+	`)
 
-	result := runTests(t, ctx, config)
-
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -988,7 +997,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mymodule_exports", "",
+	CheckSnapshot(result, "mymodule_exports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1096,7 +1105,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1197,7 +1206,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1294,7 +1303,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1421,7 +1430,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1549,7 +1558,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1612,7 +1621,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1726,7 +1735,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1840,7 +1849,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1937,7 +1946,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1975,7 +1984,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2077,7 +2086,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2190,7 +2199,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2263,7 +2272,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2374,7 +2383,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2427,7 +2436,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2540,7 +2549,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -2655,7 +2664,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
diff --git a/sdk/exports.go b/sdk/exports.go
index d313057..9a0ba4e 100644
--- a/sdk/exports.go
+++ b/sdk/exports.go
@@ -17,8 +17,12 @@
 import "android/soong/android"
 
 func init() {
-	android.RegisterModuleType("module_exports", ModuleExportsFactory)
-	android.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+	registerModuleExportsBuildComponents(android.InitRegistrationContext)
+}
+
+func registerModuleExportsBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
+	ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
 }
 
 // module_exports defines the exports of a mainline module. The exports are Soong modules
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 1c59244..54a40d2 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -42,7 +42,7 @@
 			"package/Android.bp": []byte(packageBp),
 		})
 
-	result.CheckSnapshot("myexports", "package",
+	CheckSnapshot(result, "myexports", "package",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 17a6ca9..40f2769 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -17,10 +17,11 @@
 import (
 	"testing"
 
+	"android/soong/android"
 	"android/soong/java"
 )
 
-func testSdkWithJava(t *testing.T, bp string) *testSdkResult {
+func testSdkWithJava(t *testing.T, bp string) *android.TestResult {
 	t.Helper()
 
 	fs := map[string][]byte{
@@ -125,9 +126,9 @@
 	`)
 
 	// Make sure that the mysdk module depends on "sdkmember" and not "prebuilt_sdkmember".
-	java.CheckModuleDependencies(t, result.ctx, "mysdk", "android_common", []string{"sdkmember"})
+	java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -211,6 +212,7 @@
 			uses_sdks: ["mysdk@1"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 
 		apex {
@@ -219,14 +221,15 @@
 			uses_sdks: ["mysdk@2"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 	`)
 
-	sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
-	sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
+	sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
+	sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
 
-	javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
-	javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
+	javalibForMyApex := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
+	javalibForMyApex2 := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
 
 	// Depending on the uses_sdks value, different libs are linked
 	ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
@@ -253,7 +256,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -310,7 +313,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -367,7 +370,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -438,7 +441,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -494,7 +497,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -549,7 +552,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -605,7 +608,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -660,7 +663,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -729,7 +732,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -825,7 +828,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -916,7 +919,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("myexports", "",
+	CheckSnapshot(result, "myexports", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1030,7 +1033,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1131,7 +1134,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1200,7 +1203,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1272,7 +1275,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1365,7 +1368,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1473,7 +1476,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1561,7 +1564,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -1637,7 +1640,7 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
diff --git a/sdk/sdk.go b/sdk/sdk.go
index f3d0750..2c84a2e 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -33,10 +33,14 @@
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/java/config")
 
-	android.RegisterModuleType("sdk", SdkModuleFactory)
-	android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
-	android.PreDepsMutators(RegisterPreDepsMutators)
-	android.PostDepsMutators(RegisterPostDepsMutators)
+	registerSdkBuildComponents(android.InitRegistrationContext)
+}
+
+func registerSdkBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("sdk", SdkModuleFactory)
+	ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+	ctx.PreDepsMutators(RegisterPreDepsMutators)
+	ctx.PostDepsMutators(RegisterPostDepsMutators)
 }
 
 type sdk struct {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index c4dc41b..65a9001 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -169,7 +169,7 @@
 			"package/Android.bp": []byte(packageBp),
 		})
 
-	result.CheckSnapshot("mysdk", "package",
+	CheckSnapshot(result, "mysdk", "package",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -309,18 +309,15 @@
 `)
 }
 
-func TestSDkInstall(t *testing.T) {
+func TestSdkInstall(t *testing.T) {
 	sdk := `
 		sdk {
 			name: "mysdk",
 		}
 	`
-	result := testSdkWithFs(t, ``,
-		map[string][]byte{
-			"Android.bp": []byte(sdk),
-		})
+	result := testSdkWithFs(t, sdk, nil)
 
-	result.CheckSnapshot("mysdk", "",
+	CheckSnapshot(result, "mysdk", "",
 		checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`),
 	)
 }
@@ -390,7 +387,7 @@
 
 	extractor := newCommonValueExtractor(common)
 
-	h := TestHelper{t}
+	h := android.TestHelper{t}
 
 	err := extractor.extractCommonProperties(common, structs)
 	h.AssertDeepEquals("unexpected error", nil, err)
@@ -465,7 +462,7 @@
 
 	extractor := newCommonValueExtractor(common)
 
-	h := TestHelper{t}
+	h := android.TestHelper{t}
 
 	err := extractor.extractCommonProperties(common, structs)
 	h.AssertErrorMessageEquals("unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties:
diff --git a/sdk/testing.go b/sdk/testing.go
index 0bbf7f4..41280fc 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -19,20 +19,25 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"reflect"
 	"strings"
 	"testing"
 
 	"android/soong/android"
 	"android/soong/apex"
 	"android/soong/cc"
+	"android/soong/genrule"
 	"android/soong/java"
 )
 
-func testSdkContext(bp string, fs map[string][]byte, extraOsTypes []android.OsType) (*android.TestContext, android.Config) {
-	extraOsTypes = append(extraOsTypes, android.Android, android.Windows)
+var sdkFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	apex.PrepareForTestWithApexBuildComponents,
+	cc.PrepareForTestWithCcDefaultModules,
+	genrule.PrepareForTestWithGenRuleBuildComponents,
+	java.PrepareForTestWithJavaBuildComponents,
+	PrepareForTestWithSdkBuildComponents,
 
-	bp = bp + `
+	android.FixtureAddTextFile("sdk/tests/Android.bp", `
 		apex_key {
 			name: "myapex.key",
 			public_key: "myapex.avbpubkey",
@@ -43,9 +48,9 @@
 			name: "myapex.cert",
 			certificate: "myapex",
 		}
-	` + cc.GatherRequiredDepsForTest(extraOsTypes...)
+	`),
 
-	mockFS := map[string][]byte{
+	android.FixtureMergeMockFs(map[string][]byte{
 		"build/make/target/product/security":           nil,
 		"apex_manifest.json":                           nil,
 		"system/sepolicy/apex/myapex-file_contexts":    nil,
@@ -55,115 +60,33 @@
 		"myapex.pem":                                   nil,
 		"myapex.x509.pem":                              nil,
 		"myapex.pk8":                                   nil,
-	}
+	}),
 
-	cc.GatherRequiredFilesForTest(mockFS)
-
-	for k, v := range fs {
-		mockFS[k] = v
-	}
-
-	config := android.TestArchConfig(buildDir, nil, bp, mockFS)
-
-	// Add windows as a default disable OS to test behavior when some OS variants
-	// are disabled.
-	config.Targets[android.Windows] = []android.Target{
-		{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
-	}
-
-	for _, extraOsType := range extraOsTypes {
-		switch extraOsType {
-		case android.LinuxBionic:
-			config.Targets[android.LinuxBionic] = []android.Target{
-				{android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", false},
-			}
+	cc.PrepareForTestOnWindows,
+	android.FixtureModifyConfig(func(config android.Config) {
+		// Add windows as a default disable OS to test behavior when some OS variants
+		// are disabled.
+		config.Targets[android.Windows] = []android.Target{
+			{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
 		}
-	}
+	}),
+)
 
-	ctx := android.NewTestArchContext(config)
+var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerModuleExportsBuildComponents),
+	android.FixtureRegisterWithContext(registerSdkBuildComponents),
+)
 
-	// Enable androidmk support.
-	// * Register the singleton
-	// * Configure that we are inside make
-	// * Add CommonOS to ensure that androidmk processing works.
-	android.RegisterAndroidMkBuildComponents(ctx)
-	android.SetKatiEnabledForTests(config)
-	config.Targets[android.CommonOS] = []android.Target{
-		{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", "", true},
-	}
-
-	// from android package
-	android.RegisterPackageBuildComponents(ctx)
-	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(android.RegisterComponentsMutator)
-
-	android.RegisterPrebuiltMutators(ctx)
-
-	// Register these after the prebuilt mutators have been registered to match what
-	// happens at runtime.
-	ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
-	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
-
-	// from java package
-	java.RegisterRequiredBuildComponentsForTest(ctx)
-
-	// from cc package
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-
-	// from apex package
-	ctx.RegisterModuleType("apex", apex.BundleFactory)
-	ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
-	ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
-
-	// from this package
-	ctx.RegisterModuleType("sdk", SdkModuleFactory)
-	ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
-	ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
-	ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
-	ctx.PreDepsMutators(RegisterPreDepsMutators)
-	ctx.PostDepsMutators(RegisterPostDepsMutators)
-
-	ctx.Register()
-
-	return ctx, config
-}
-
-func runTests(t *testing.T, ctx *android.TestContext, config android.Config) *testSdkResult {
+func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult {
 	t.Helper()
-	_, errs := ctx.ParseBlueprintsFiles(".")
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-	return &testSdkResult{
-		TestHelper: TestHelper{t: t},
-		ctx:        ctx,
-		config:     config,
-	}
-}
-
-func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
-	t.Helper()
-	ctx, config := testSdkContext(bp, fs, nil)
-	return runTests(t, ctx, config)
+	return sdkFixtureFactory.RunTest(t, fs.AddToFixture(), android.FixtureWithRootAndroidBp(bp))
 }
 
 func testSdkError(t *testing.T, pattern, bp string) {
 	t.Helper()
-	ctx, config := testSdkContext(bp, nil, nil)
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-	_, errs = ctx.PrepareBuildActions(config)
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+	sdkFixtureFactory.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+		RunTestWithBp(t, bp)
 }
 
 func ensureListContains(t *testing.T, result []string, expected string) {
@@ -181,74 +104,13 @@
 	return ret
 }
 
-// Provides general test support.
-type TestHelper struct {
-	t *testing.T
-}
-
-func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
-	h.t.Helper()
-	if actual != expected {
-		h.t.Errorf("%s: expected %s, actual %s", message, expected, actual)
-	}
-}
-
-func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) {
-	h.t.Helper()
-	if actual == nil {
-		h.t.Errorf("Expected error but was nil")
-	} else if actual.Error() != expected {
-		h.t.Errorf("%s: expected %s, actual %s", message, expected, actual.Error())
-	}
-}
-
-func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
-	h.t.Helper()
-	expected = strings.TrimSpace(expected)
-	actual = strings.TrimSpace(actual)
-	if actual != expected {
-		h.t.Errorf("%s: expected:\n%s\nactual:\n%s\n", message, expected, actual)
-	}
-}
-
-func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
-	h.t.Helper()
-	if !reflect.DeepEqual(actual, expected) {
-		h.t.Errorf("%s: expected %#v, actual %#v", message, expected, actual)
-	}
-}
-
-func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) {
-	h.t.Helper()
-	panicked := false
-	func() {
-		defer func() {
-			if x := recover(); x != nil {
-				panicked = true
-			}
-		}()
-		funcThatShouldPanic()
-	}()
-	if !panicked {
-		h.t.Error(message)
-	}
-}
-
-// Encapsulates result of processing an SDK definition. Provides support for
-// checking the state of the build structures.
-type testSdkResult struct {
-	TestHelper
-	ctx    *android.TestContext
-	config android.Config
-}
-
 // Analyse the sdk build rules to extract information about what it is doing.
-
+//
 // e.g. find the src/dest pairs from each cp command, the various zip files
 // generated, etc.
-func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
+func getSdkSnapshotBuildInfo(result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
 	info := &snapshotBuildInfo{
-		r:                            r,
+		r:                            result,
 		androidBpContents:            sdk.GetAndroidBpContentsForTests(),
 		androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
 		androidVersionedBpContents:   sdk.GetVersionedAndroidBpContentsForTests(),
@@ -291,7 +153,7 @@
 			info.intermediateZip = info.outputZip
 			mergeInput := android.NormalizePathForTesting(bp.Input)
 			if info.intermediateZip != mergeInput {
-				r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
+				result.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
 					info.intermediateZip, mergeInput)
 			}
 
@@ -310,28 +172,20 @@
 	return info
 }
 
-func (r *testSdkResult) Module(name string, variant string) android.Module {
-	return r.ctx.ModuleForTests(name, variant).Module()
-}
-
-func (r *testSdkResult) ModuleForTests(name string, variant string) android.TestingModule {
-	return r.ctx.ModuleForTests(name, variant)
-}
-
 // Check the snapshot build rules.
 //
 // Takes a list of functions which check different facets of the snapshot build rules.
 // Allows each test to customize what is checked without duplicating lots of code
 // or proliferating check methods of different flavors.
-func (r *testSdkResult) CheckSnapshot(name string, dir string, checkers ...snapshotBuildInfoChecker) {
-	r.t.Helper()
+func CheckSnapshot(result *android.TestResult, name string, dir string, checkers ...snapshotBuildInfoChecker) {
+	result.Helper()
 
 	// The sdk CommonOS variant is always responsible for generating the snapshot.
 	variant := android.CommonOS.Name
 
-	sdk := r.Module(name, variant).(*sdk)
+	sdk := result.Module(name, variant).(*sdk)
 
-	snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
+	snapshotBuildInfo := getSdkSnapshotBuildInfo(result, sdk)
 
 	// Check state of the snapshot build.
 	for _, checker := range checkers {
@@ -343,7 +197,7 @@
 	if dir != "" {
 		dir = filepath.Clean(dir) + "/"
 	}
-	r.AssertStringEquals("Snapshot zip file in wrong place",
+	result.AssertStringEquals("Snapshot zip file in wrong place",
 		fmt.Sprintf(".intermediates/%s%s/%s/%s-current.zip", dir, name, variant, name), actual)
 
 	// Populate a mock filesystem with the files that would have been copied by
@@ -354,7 +208,7 @@
 	}
 
 	// Process the generated bp file to make sure it is valid.
-	testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs)
+	testSdkWithFs(result.T, snapshotBuildInfo.androidBpContents, fs)
 }
 
 type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
@@ -364,7 +218,7 @@
 // Both the expected and actual string are both trimmed before comparing.
 func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
-		info.r.t.Helper()
+		info.r.Helper()
 		info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents)
 	}
 }
@@ -376,7 +230,7 @@
 // Both the expected and actual string are both trimmed before comparing.
 func checkUnversionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
-		info.r.t.Helper()
+		info.r.Helper()
 		info.r.AssertTrimmedStringEquals("unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents)
 	}
 }
@@ -391,7 +245,7 @@
 // Both the expected and actual string are both trimmed before comparing.
 func checkVersionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
-		info.r.t.Helper()
+		info.r.Helper()
 		info.r.AssertTrimmedStringEquals("versioned Android.bp contents do not match", expected, info.androidVersionedBpContents)
 	}
 }
@@ -403,14 +257,14 @@
 // before comparing.
 func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
-		info.r.t.Helper()
+		info.r.Helper()
 		info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules)
 	}
 }
 
 func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
-		info.r.t.Helper()
+		info.r.Helper()
 		info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.otherCopyRules)
 	}
 }
@@ -418,9 +272,9 @@
 // Check that the specified paths match the list of zips to merge with the intermediate zip.
 func checkMergeZips(expected ...string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
-		info.r.t.Helper()
+		info.r.Helper()
 		if info.intermediateZip == "" {
-			info.r.t.Errorf("No intermediate zip file was created")
+			info.r.Errorf("No intermediate zip file was created")
 		}
 
 		info.r.AssertDeepEquals("mismatching merge zip files", expected, info.mergeZips)
@@ -433,7 +287,7 @@
 // All source/input paths are relative either the build directory. All dest/output paths are
 // relative to the snapshot root directory.
 type snapshotBuildInfo struct {
-	r *testSdkResult
+	r *android.TestResult
 
 	// The contents of the generated Android.bp file
 	androidBpContents string
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 031cd47..49f4961 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -40,14 +40,32 @@
 func init() {
 	pctx.Import("android/soong/android")
 
-	android.RegisterModuleType("sh_binary", ShBinaryFactory)
-	android.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
-	android.RegisterModuleType("sh_test", ShTestFactory)
-	android.RegisterModuleType("sh_test_host", ShTestHostFactory)
+	registerShBuildComponents(android.InitRegistrationContext)
 
 	android.RegisterBp2BuildMutator("sh_binary", ShBinaryBp2Build)
 }
 
+func registerShBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("sh_binary", ShBinaryFactory)
+	ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
+	ctx.RegisterModuleType("sh_test", ShTestFactory)
+	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
+}
+
+// Test fixture preparer that will register most sh build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of sh
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithShBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerShBuildComponents),
+)
+
 type shBinaryProperties struct {
 	// Source file of this prebuilt.
 	Src *string `android:"path,arch_variant"`
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index fb7ab13..7a24168 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -3,6 +3,7 @@
 import (
 	"io/ioutil"
 	"os"
+	"path"
 	"path/filepath"
 	"reflect"
 	"testing"
@@ -36,28 +37,32 @@
 	os.Exit(run())
 }
 
-func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
-	fs := map[string][]byte{
+var shFixtureFactory = android.NewFixtureFactory(
+	&buildDir,
+	cc.PrepareForTestWithCcBuildComponents,
+	PrepareForTestWithShBuildComponents,
+	android.FixtureMergeMockFs(android.MockFS{
 		"test.sh":            nil,
 		"testdata/data1":     nil,
 		"testdata/sub/data2": nil,
-	}
+	}),
+)
 
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
+// testShBinary runs tests using the shFixtureFactory
+//
+// Do not add any new usages of this, instead use the shFixtureFactory directly as it makes it much
+// easier to customize the test behavior.
+//
+// If it is necessary to customize the behavior of an existing test that uses this then please first
+// convert the test to using shFixtureFactory first and then in a following change add the
+// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
+// that it did not change the test behavior unexpectedly.
+//
+// deprecated
+func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	result := shFixtureFactory.RunTestWithBp(t, bp)
 
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("sh_test", ShTestFactory)
-	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
-
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-
-	ctx.Register()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	return ctx, config
+	return result.TestContext, result.Config
 }
 
 func TestShTestSubDir(t *testing.T) {
@@ -73,7 +78,8 @@
 
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
-	expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo_test"
+	expectedPath := path.Join(buildDir,
+		"../target/product/test_device/data/nativetest64/foo_test")
 	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
 	if expectedPath != actualPath {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
@@ -97,7 +103,8 @@
 
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
-	expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo"
+	expectedPath := path.Join(buildDir,
+		"../target/product/test_device/data/nativetest64/foo")
 	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
 	if expectedPath != actualPath {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
diff --git a/shared/Android.bp b/shared/Android.bp
index 5aa9d54..deb17f8 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -6,7 +6,12 @@
     name: "soong-shared",
     pkgPath: "android/soong/shared",
     srcs: [
+        "env.go",
         "paths.go",
+        "debug.go",
+    ],
+    testSrcs: [
+        "paths_test.go",
     ],
     deps: [
         "soong-bazel",
diff --git a/shared/debug.go b/shared/debug.go
new file mode 100644
index 0000000..5392f2b
--- /dev/null
+++ b/shared/debug.go
@@ -0,0 +1,69 @@
+package shared
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"strings"
+	"syscall"
+)
+
+var (
+	isDebugging bool
+)
+
+// Finds the Delve binary to use. Either uses the SOONG_DELVE_PATH environment
+// variable or if that is unset, looks at $PATH.
+func ResolveDelveBinary() string {
+	result := os.Getenv("SOONG_DELVE_PATH")
+	if result == "" {
+		result, _ = exec.LookPath("dlv")
+	}
+
+	return result
+}
+
+// Returns whether the current process is running under Delve due to
+// ReexecWithDelveMaybe().
+func IsDebugging() bool {
+	return isDebugging
+}
+
+// Re-executes the binary in question under the control of Delve when
+// delveListen is not the empty string. delvePath gives the path to the Delve.
+func ReexecWithDelveMaybe(delveListen, delvePath string) {
+	isDebugging = os.Getenv("SOONG_DELVE_REEXECUTED") == "true"
+	if isDebugging || delveListen == "" {
+		return
+	}
+
+	if delvePath == "" {
+		fmt.Fprintln(os.Stderr, "Delve debugging requested but failed to find dlv")
+		os.Exit(1)
+	}
+
+	soongDelveEnv := []string{}
+	for _, env := range os.Environ() {
+		idx := strings.IndexRune(env, '=')
+		if idx != -1 {
+			soongDelveEnv = append(soongDelveEnv, env)
+		}
+	}
+
+	soongDelveEnv = append(soongDelveEnv, "SOONG_DELVE_REEXECUTED=true")
+
+	dlvArgv := []string{
+		delvePath,
+		"--listen=:" + delveListen,
+		"--headless=true",
+		"--api-version=2",
+		"exec",
+		os.Args[0],
+		"--",
+	}
+
+	dlvArgv = append(dlvArgv, os.Args[1:]...)
+	syscall.Exec(delvePath, dlvArgv, soongDelveEnv)
+	fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
+	os.Exit(1)
+}
diff --git a/env/env.go b/shared/env.go
similarity index 76%
rename from env/env.go
rename to shared/env.go
index 735a38a..152729b 100644
--- a/env/env.go
+++ b/shared/env.go
@@ -12,15 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// env implements the environment JSON file handling for the soong_env command line tool run before
-// the builder and for the env writer in the builder.
-package env
+// Implements the environment JSON file handling for serializing the
+// environment variables that were used in soong_build so that soong_ui can
+// check whether they have changed
+package shared
 
 import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"os"
 	"sort"
 )
 
@@ -57,7 +57,7 @@
 // Reads and deserializes a Soong environment file located at the given file path to determine its
 // staleness. If any environment variable values have changed, it prints them out and returns true.
 // Failing to read or parse the file also causes it to return true.
-func StaleEnvFile(filepath string) (bool, error) {
+func StaleEnvFile(filepath string, getenv func(string) string) (bool, error) {
 	data, err := ioutil.ReadFile(filepath)
 	if err != nil {
 		return true, err
@@ -74,7 +74,7 @@
 	for _, entry := range contents {
 		key := entry.Key
 		old := entry.Value
-		cur := os.Getenv(key)
+		cur := getenv(key)
 		if old != cur {
 			changed = append(changed, fmt.Sprintf("%s (%q -> %q)", key, old, cur))
 		}
@@ -91,6 +91,28 @@
 	return false, nil
 }
 
+// Deserializes and environment serialized by EnvFileContents() and returns it
+// as a map[string]string.
+func EnvFromFile(envFile string) (map[string]string, error) {
+	result := make(map[string]string)
+	data, err := ioutil.ReadFile(envFile)
+	if err != nil {
+		return result, err
+	}
+
+	var contents envFileData
+	err = json.Unmarshal(data, &contents)
+	if err != nil {
+		return result, err
+	}
+
+	for _, entry := range contents {
+		result[entry.Key] = entry.Value
+	}
+
+	return result, nil
+}
+
 // Implements sort.Interface so that we can use sort.Sort on envFileData arrays.
 func (e envFileData) Len() int {
 	return len(e)
diff --git a/shared/paths.go b/shared/paths.go
index 1b9ff60..fca8b4c 100644
--- a/shared/paths.go
+++ b/shared/paths.go
@@ -30,6 +30,21 @@
 	BazelMetricsDir() string
 }
 
+// Joins the path strings in the argument list, taking absolute paths into
+// account. That is, if one of the strings is an absolute path, the ones before
+// are ignored.
+func JoinPath(base string, rest ...string) string {
+	result := base
+	for _, next := range rest {
+		if filepath.IsAbs(next) {
+			result = next
+		} else {
+			result = filepath.Join(result, next)
+		}
+	}
+	return result
+}
+
 // Given the out directory, returns the root of the temp directory (to be cleared at the start of each execution of Soong)
 func TempDirForOutDir(outDir string) (tempPath string) {
 	return filepath.Join(outDir, ".temp")
diff --git a/shared/paths_test.go b/shared/paths_test.go
new file mode 100644
index 0000000..018d55f
--- /dev/null
+++ b/shared/paths_test.go
@@ -0,0 +1,32 @@
+// 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 shared
+
+import (
+	"testing"
+)
+
+func assertEqual(t *testing.T, expected, actual string) {
+	t.Helper()
+	if expected != actual {
+		t.Errorf("expected %q != got %q", expected, actual)
+	}
+}
+
+func TestJoinPath(t *testing.T) {
+	assertEqual(t, "/a/b", JoinPath("c/d", "/a/b"))
+	assertEqual(t, "a/b", JoinPath("a", "b"))
+	assertEqual(t, "/a/b", JoinPath("x", "/a", "b"))
+}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 4ad68ea..892a16c 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -165,6 +165,12 @@
 		// Forwarded to cc_library.min_sdk_version
 		Min_sdk_version *string
 	}
+
+	Java struct {
+		// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+		// Forwarded to java_library.min_sdk_version
+		Min_sdk_version *string
+	}
 }
 
 var (
@@ -403,6 +409,8 @@
 	Libs              []string
 	Stem              *string
 	SyspropPublicStub string
+	Apex_available    []string
+	Min_sdk_version   *string
 }
 
 func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
@@ -508,6 +516,8 @@
 		Sdk_version:       proptools.StringPtr("core_current"),
 		Libs:              []string{javaSyspropStub},
 		SyspropPublicStub: publicStub,
+		Apex_available:    m.ApexProperties.Apex_available,
+		Min_sdk_version:   m.properties.Java.Min_sdk_version,
 	})
 
 	if publicStub != "" {
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 9d914e3..fde41d6 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -81,6 +81,51 @@
 }
 
 func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
+	bp += `
+		cc_library {
+			name: "libbase",
+			host_supported: true,
+		}
+
+		cc_library_headers {
+			name: "libbase_headers",
+			vendor_available: true,
+			recovery_available: true,
+		}
+
+		cc_library {
+			name: "liblog",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			recovery_available: true,
+			host_supported: true,
+			llndk_stubs: "liblog.llndk",
+		}
+
+		llndk_library {
+			name: "liblog.llndk",
+			symbol_file: "",
+		}
+
+		java_library {
+			name: "sysprop-library-stub-platform",
+			sdk_version: "core_current",
+		}
+
+		java_library {
+			name: "sysprop-library-stub-vendor",
+			soc_specific: true,
+			sdk_version: "core_current",
+		}
+
+		java_library {
+			name: "sysprop-library-stub-product",
+			product_specific: true,
+			sdk_version: "core_current",
+		}
+	`
+
 	bp += cc.GatherRequiredDepsForTest(android.Android)
 
 	mockFS := map[string][]byte{
@@ -250,54 +295,11 @@
 			static_libs: ["sysprop-platform", "sysprop-vendor"],
 		}
 
-		cc_library {
-			name: "libbase",
-			host_supported: true,
-		}
-
-		cc_library_headers {
-			name: "libbase_headers",
-			vendor_available: true,
-			recovery_available: true,
-		}
-
-		cc_library {
-			name: "liblog",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			recovery_available: true,
-			host_supported: true,
-			llndk_stubs: "liblog.llndk",
-		}
-
 		cc_binary_host {
 			name: "hostbin",
 			static_libs: ["sysprop-platform"],
 		}
-
-		llndk_library {
-			name: "liblog.llndk",
-			symbol_file: "",
-		}
-
-		java_library {
-			name: "sysprop-library-stub-platform",
-			sdk_version: "core_current",
-		}
-
-		java_library {
-			name: "sysprop-library-stub-vendor",
-			soc_specific: true,
-			sdk_version: "core_current",
-		}
-
-		java_library {
-			name: "sysprop-library-stub-product",
-			product_specific: true,
-			sdk_version: "core_current",
-		}
-		`)
+	`)
 
 	// Check for generated cc_library
 	for _, variant := range []string{
@@ -391,3 +393,62 @@
 		t.Errorf("system api client should use public stub %q, got %q", w, g)
 	}
 }
+
+func TestApexAvailabilityIsForwarded(t *testing.T) {
+	ctx := test(t, `
+		sysprop_library {
+			name: "sysprop-platform",
+			apex_available: ["//apex_available:platform"],
+			srcs: ["android/sysprop/PlatformProperties.sysprop"],
+			api_packages: ["android.sysprop"],
+			property_owner: "Platform",
+		}
+	`)
+
+	expected := []string{"//apex_available:platform"}
+
+	ccModule := ctx.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	propFromCc := ccModule.ApexProperties.Apex_available
+	if !reflect.DeepEqual(propFromCc, expected) {
+		t.Errorf("apex_available not forwarded to cc module. expected %#v, got %#v",
+			expected, propFromCc)
+	}
+
+	javaModule := ctx.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
+	propFromJava := javaModule.ApexProperties.Apex_available
+	if !reflect.DeepEqual(propFromJava, expected) {
+		t.Errorf("apex_available not forwarded to java module. expected %#v, got %#v",
+			expected, propFromJava)
+	}
+}
+
+func TestMinSdkVersionIsForwarded(t *testing.T) {
+	ctx := test(t, `
+		sysprop_library {
+			name: "sysprop-platform",
+			srcs: ["android/sysprop/PlatformProperties.sysprop"],
+			api_packages: ["android.sysprop"],
+			property_owner: "Platform",
+			cpp: {
+				min_sdk_version: "29",
+			},
+			java: {
+				min_sdk_version: "30",
+			},
+		}
+	`)
+
+	ccModule := ctx.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	propFromCc := proptools.String(ccModule.Properties.Min_sdk_version)
+	if propFromCc != "29" {
+		t.Errorf("min_sdk_version not forwarded to cc module. expected %#v, got %#v",
+			"29", propFromCc)
+	}
+
+	javaModule := ctx.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
+	propFromJava := javaModule.MinSdkVersion()
+	if propFromJava != "30" {
+		t.Errorf("min_sdk_version not forwarded to java module. expected %#v, got %#v",
+			"30", propFromJava)
+	}
+}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index b1f8551..6ba497c 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -124,7 +124,7 @@
 		productOut("obj/PACKAGING"),
 		productOut("ramdisk"),
 		productOut("debug_ramdisk"),
-		productOut("vendor-ramdisk"),
+		productOut("vendor_ramdisk"),
 		productOut("vendor_debug_ramdisk"),
 		productOut("test_harness_ramdisk"),
 		productOut("recovery"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 15da1bc..1152cd7 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -56,7 +56,6 @@
 	katiSuffix      string
 	targetDevice    string
 	targetDeviceDir string
-	fullBuild       bool
 
 	// Autodetected
 	totalRAM uint64
@@ -792,14 +791,6 @@
 	c.targetDevice = device
 }
 
-func (c *configImpl) FullBuild() bool {
-	return c.fullBuild
-}
-
-func (c *configImpl) SetFullBuild(fullBuild bool) {
-	c.fullBuild = fullBuild
-}
-
 func (c *configImpl) TargetBuildVariant() string {
 	if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok {
 		return v
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index be6f00a..fe0aca9 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -214,9 +214,6 @@
 		// So that later Kati runs can find BoardConfig.mk faster
 		"TARGET_DEVICE_DIR",
 
-		// To decide whether to skip the old installed cleanup step.
-		"FULL_BUILD",
-
 		// Whether --werror_overriding_commands will work
 		"BUILD_BROKEN_DUP_RULES",
 
@@ -281,7 +278,6 @@
 	config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"]))
 	config.SetTargetDevice(makeVars["TARGET_DEVICE"])
 	config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"])
-	config.SetFullBuild(makeVars["FULL_BUILD"] == "true")
 
 	config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
diff --git a/ui/build/environment.go b/ui/build/environment.go
index 6d8a28f..50d059f 100644
--- a/ui/build/environment.go
+++ b/ui/build/environment.go
@@ -33,6 +33,19 @@
 	return &env
 }
 
+// Returns a copy of the environment as a map[string]string.
+func (e *Environment) AsMap() map[string]string {
+	result := make(map[string]string)
+
+	for _, envVar := range *e {
+		if k, v, ok := decodeKeyValue(envVar); ok {
+			result[k] = v
+		}
+	}
+
+	return result
+}
+
 // Get returns the value associated with the key, and whether it exists.
 // It's equivalent to the os.LookupEnv function, but with this copy of the
 // Environment.
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 668b20e..06ec646 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -229,11 +229,7 @@
 
 	// Cleanup steps.
 	cleanCopyHeaders(ctx, config)
-	// Skip the old installed file cleanup step for few non-full build goals as we don't create
-	// an installed file list for them.
-	if config.FullBuild() {
-		cleanOldInstalledFiles(ctx, config)
-	}
+	cleanOldInstalledFiles(ctx, config)
 }
 
 // Clean out obsolete header files on the disk that were *not copied* during the
diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go
index 3b1005f..067f3f3 100644
--- a/ui/build/paths/logs_test.go
+++ b/ui/build/paths/logs_test.go
@@ -26,6 +26,9 @@
 )
 
 func TestSendLog(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping in short mode, sometimes hangs")
+	}
 	t.Run("Short name", func(t *testing.T) {
 		d, err := ioutil.TempDir("", "s")
 		if err != nil {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 899ab5d..884e957 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -19,7 +19,8 @@
 	"os"
 	"path/filepath"
 	"strconv"
-	"strings"
+
+	"android/soong/shared"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 
@@ -30,6 +31,15 @@
 	"android/soong/ui/status"
 )
 
+func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
+	data, err := shared.EnvFileContents(envDeps)
+	if err != nil {
+		return err
+	}
+
+	return ioutil.WriteFile(envFile, data, 0644)
+}
+
 // This uses Android.bp files and various tools to generate <builddir>/build.ninja.
 //
 // However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build()
@@ -47,6 +57,12 @@
 	ctx.BeginTrace(metrics.RunSoong, "soong")
 	defer ctx.EndTrace()
 
+	// We have two environment files: .available is the one with every variable,
+	// .used with the ones that were actually used. The latter is used to
+	// determine whether Soong needs to be re-run since why re-run it if only
+	// unused variables were changed?
+	envFile := filepath.Join(config.SoongOutDir(), "soong.environment.available")
+
 	// Use an anonymous inline function for tracing purposes (this pattern is used several times below).
 	func() {
 		ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
@@ -61,6 +77,7 @@
 		}
 
 		cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", args...)
+
 		cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint")
 		cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash")
 		cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
@@ -74,34 +91,36 @@
 		cmd.RunAndPrintOrFatal()
 	}()
 
+	soongBuildEnv := config.Environment().Copy()
+	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
+	// These two dependencies are read from bootstrap.go, but also need to be here
+	// so that soong_build can declare a dependency on them
+	soongBuildEnv.Set("SOONG_DELVE", os.Getenv("SOONG_DELVE"))
+	soongBuildEnv.Set("SOONG_DELVE_PATH", os.Getenv("SOONG_DELVE_PATH"))
+	soongBuildEnv.Set("SOONG_OUTDIR", config.SoongOutDir())
+	// For Bazel mixed builds.
+	soongBuildEnv.Set("BAZEL_PATH", "./tools/bazel")
+	soongBuildEnv.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
+	soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
+	soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
+	soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
+
+	err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap())
+	if err != nil {
+		ctx.Fatalf("failed to write environment file %s: %s", envFile, err)
+	}
+
 	func() {
 		ctx.BeginTrace(metrics.RunSoong, "environment check")
 		defer ctx.EndTrace()
 
-		envFile := filepath.Join(config.SoongOutDir(), ".soong.environment")
-		envTool := filepath.Join(config.SoongOutDir(), ".bootstrap/bin/soong_env")
-		if _, err := os.Stat(envFile); err == nil {
-			if _, err := os.Stat(envTool); err == nil {
-				cmd := Command(ctx, config, "soong_env", envTool, envFile)
-				cmd.Sandbox = soongSandbox
-
-				var buf strings.Builder
-				cmd.Stdout = &buf
-				cmd.Stderr = &buf
-				if err := cmd.Run(); err != nil {
-					ctx.Verboseln("soong_env failed, forcing manifest regeneration")
-					os.Remove(envFile)
-				}
-
-				if buf.Len() > 0 {
-					ctx.Verboseln(buf.String())
-				}
-			} else {
-				ctx.Verboseln("Missing soong_env tool, forcing manifest regeneration")
-				os.Remove(envFile)
-			}
-		} else if !os.IsNotExist(err) {
-			ctx.Fatalf("Failed to stat %f: %v", envFile, err)
+		envFile := filepath.Join(config.SoongOutDir(), "soong.environment.used")
+		getenv := func(k string) string {
+			v, _ := soongBuildEnv.Get(k)
+			return v
+		}
+		if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
+			os.Remove(envFile)
 		}
 	}()
 
@@ -151,14 +170,20 @@
 			"--frontend_file", fifo,
 			"-f", filepath.Join(config.SoongOutDir(), file))
 
-		// For Bazel mixed builds.
-		cmd.Environment.Set("BAZEL_PATH", "./tools/bazel")
-		cmd.Environment.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
-		cmd.Environment.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
-		cmd.Environment.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
-		cmd.Environment.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
+		var ninjaEnv Environment
 
-		cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
+		// This is currently how the command line to invoke soong_build finds the
+		// root of the source tree and the output root
+		ninjaEnv.Set("TOP", os.Getenv("TOP"))
+		ninjaEnv.Set("SOONG_OUTDIR", config.SoongOutDir())
+
+		// For debugging
+		if os.Getenv("SOONG_DELVE") != "" {
+			ninjaEnv.Set("SOONG_DELVE", os.Getenv("SOONG_DELVE"))
+			ninjaEnv.Set("SOONG_DELVE_PATH", shared.ResolveDelveBinary())
+		}
+
+		cmd.Environment = &ninjaEnv
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}
diff --git a/vnames.go.json b/vnames.go.json
deleted file mode 100644
index 7ce2d4b..0000000
--- a/vnames.go.json
+++ /dev/null
@@ -1,9 +0,0 @@
-[
-    {
-        "pattern": "(.*)",
-        "vname": {
-            "corpus": "android.googlesource.com/platform/superproject",
-            "path": "@1@"
-        }
-    }
-]
diff --git a/vnames.json b/vnames.json
index f9d3adc..096260f 100644
--- a/vnames.json
+++ b/vnames.json
@@ -2,7 +2,6 @@
   {
     "pattern": "out/(.*)",
     "vname": {
-      "corpus": "android.googlesource.com/platform/superproject",
       "root": "out",
       "path": "@1@"
     }
@@ -10,9 +9,7 @@
   {
     "pattern": "(.*)",
     "vname": {
-      "corpus": "android.googlesource.com/platform/superproject",
       "path": "@1@"
     }
   }
 ]
-