Merge "Stop using '&' for class loader context."
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ff85661..317f5c4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,6 @@
 [Builtin Hooks]
 gofmt = true
 bpfmt = true
+
+[Hook Scripts]
+do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT}
diff --git a/android/Android.bp b/android/Android.bp
index 00139d8..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",
     ],
@@ -22,6 +21,7 @@
         "api_levels.go",
         "arch.go",
         "arch_list.go",
+        "bazel.go",
         "bazel_handler.go",
         "config.go",
         "csuite_config.go",
@@ -33,6 +33,7 @@
         "deptag.go",
         "expand.go",
         "filegroup.go",
+        "fixture.go",
         "hooks.go",
         "image.go",
         "license.go",
@@ -86,6 +87,7 @@
         "depset_test.go",
         "deptag_test.go",
         "expand_test.go",
+        "fixture_test.go",
         "license_kind_test.go",
         "license_test.go",
         "licenses_test.go",
diff --git a/android/android_test.go b/android/android_test.go
index 46b7054..68cb705 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -44,3 +44,5 @@
 
 	os.Exit(run())
 }
+
+var emptyTestFixtureFactory = NewFixtureFactory(&buildDir)
diff --git a/android/androidmk.go b/android/androidmk.go
index 5856851..32d7712 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -141,7 +141,20 @@
 	entryOrder []string
 }
 
-type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
+type AndroidMkExtraEntriesContext interface {
+	Provider(provider blueprint.ProviderKey) interface{}
+}
+
+type androidMkExtraEntriesContext struct {
+	ctx fillInEntriesContext
+	mod blueprint.Module
+}
+
+func (a *androidMkExtraEntriesContext) Provider(provider blueprint.ProviderKey) interface{} {
+	return a.ctx.ModuleProvider(a.mod, provider)
+}
+
+type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
 type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string)
 
 // Utility funcs to manipulate Android.mk variable entries.
@@ -448,7 +461,13 @@
 
 // fillInEntries goes through the common variable processing and calls the extra data funcs to
 // generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
-func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
+type fillInEntriesContext interface {
+	ModuleDir(module blueprint.Module) string
+	Config() Config
+	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
+}
+
+func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
 	a.EntryMap = make(map[string][]string)
 	amod := mod.(Module).base()
 	name := amod.BaseModuleName()
@@ -470,7 +489,7 @@
 	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
 
 	// Collect make variable assignment entries.
-	a.SetString("LOCAL_PATH", filepath.Dir(bpPath))
+	a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
 	a.SetString("LOCAL_MODULE", name+a.SubName)
 	a.AddStrings("LOCAL_LICENSE_KINDS", amod.commonProperties.Effective_license_kinds...)
 	a.AddStrings("LOCAL_LICENSE_CONDITIONS", amod.commonProperties.Effective_license_conditions...)
@@ -561,17 +580,23 @@
 
 		}
 
-		if amod.Arch().ArchType != config.Targets[amod.Os()][0].Arch.ArchType {
+		if amod.Arch().ArchType != ctx.Config().Targets[amod.Os()][0].Arch.ArchType {
 			prefix = "2ND_" + prefix
 		}
 	}
+
+	extraCtx := &androidMkExtraEntriesContext{
+		ctx: ctx,
+		mod: mod,
+	}
+
 	for _, extra := range a.ExtraEntries {
-		extra(a)
+		extra(extraCtx, a)
 	}
 
 	// Write to footer.
 	fmt.Fprintln(&a.footer, "include "+a.Include)
-	blueprintDir := filepath.Dir(bpPath)
+	blueprintDir := ctx.ModuleDir(mod)
 	for _, footerFunc := range a.ExtraFooters {
 		footerFunc(&a.footer, name, prefix, blueprintDir)
 	}
@@ -732,7 +757,7 @@
 	return nil
 }
 
-func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
+func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
 	// Get the preamble content through AndroidMkEntries logic.
 	data.Entries = AndroidMkEntries{
 		Class:           data.Class,
@@ -745,7 +770,7 @@
 		Host_required:   data.Host_required,
 		Target_required: data.Target_required,
 	}
-	data.Entries.fillInEntries(config, bpPath, mod)
+	data.Entries.fillInEntries(ctx, mod)
 
 	// copy entries back to data since it is used in Custom
 	data.Required = data.Entries.Required
@@ -768,7 +793,7 @@
 		data.Include = "$(BUILD_PREBUILT)"
 	}
 
-	data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
+	data.fillInData(ctx, mod)
 
 	prefix := ""
 	if amod.ArchSpecific() {
@@ -795,7 +820,7 @@
 	if data.Custom != nil {
 		// List of module types allowed to use .Custom(...)
 		// Additions to the list require careful review for proper license handling.
-		switch reflect.TypeOf(mod).String() {  // ctx.ModuleType(mod) doesn't work: aidl_interface creates phony without type
+		switch reflect.TypeOf(mod).String() { // ctx.ModuleType(mod) doesn't work: aidl_interface creates phony without type
 		case "*aidl.aidlApi": // writes non-custom before adding .phony
 		case "*aidl.aidlMapping": // writes non-custom before adding .phony
 		case "*android.customModule": // appears in tests only
@@ -850,7 +875,7 @@
 
 	// Any new or special cases here need review to verify correct propagation of license information.
 	for _, entries := range provider.AndroidMkEntries() {
-		entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
+		entries.fillInEntries(ctx, mod)
 		entries.write(w)
 	}
 
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 347b92e..2f568fb 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -137,9 +137,9 @@
 	return module
 }
 
-// buildConfigAndCustomModuleFoo creates a config object, processes the supplied
+// buildContextAndCustomModuleFoo creates a config object, processes the supplied
 // bp module and then returns the config and the custom module called "foo".
-func buildConfigAndCustomModuleFoo(t *testing.T, bp string) (Config, *customModule) {
+func buildContextAndCustomModuleFoo(t *testing.T, bp string) (*TestContext, *customModule) {
 	t.Helper()
 	config := TestConfig(buildDir, nil, bp, nil)
 	config.katiEnabled = true // Enable androidmk Singleton
@@ -155,7 +155,7 @@
 	FailIfErrored(t, errs)
 
 	module := ctx.ModuleForTests("foo", "").Module().(*customModule)
-	return config, module
+	return ctx, module
 }
 
 func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
@@ -168,7 +168,7 @@
 	}
 	`
 
-	_, m := buildConfigAndCustomModuleFoo(t, bp)
+	_, m := buildContextAndCustomModuleFoo(t, bp)
 
 	assertEqual := func(expected interface{}, actual interface{}) {
 		if !reflect.DeepEqual(expected, actual) {
@@ -253,8 +253,8 @@
 		"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
 	}
 
-	config, module := buildConfigAndCustomModuleFoo(t, bp)
-	entries := AndroidMkEntriesForTest(t, config, "", module)
+	ctx, module := buildContextAndCustomModuleFoo(t, bp)
+	entries := AndroidMkEntriesForTest(t, ctx, module)
 	if len(entries) != 1 {
 		t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
 	}
@@ -343,8 +343,8 @@
 		t.Run(name, func(t *testing.T) {
 			t.Helper()
 
-			config, module := buildConfigAndCustomModuleFoo(t, bp)
-			entries := AndroidMkEntriesForTest(t, config, "", module)
+			ctx, module := buildContextAndCustomModuleFoo(t, bp)
+			entries := AndroidMkEntriesForTest(t, ctx, module)
 			if len(entries) != 1 {
 				t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
 			}
diff --git a/android/apex.go b/android/apex.go
index 6bb0751..a014592 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -739,6 +739,8 @@
 
 	d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath
 	WriteFileRule(ctx, d.flatListPath, flatContent.String())
+
+	ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath)
 }
 
 // TODO(b/158059172): remove minSdkVersion allowlist
@@ -757,7 +759,6 @@
 	"captiveportal-lib":              28,
 	"flatbuffer_headers":             30,
 	"framework-permission":           30,
-	"framework-statsd":               30,
 	"gemmlowp_headers":               30,
 	"ike-internals":                  30,
 	"kotlinx-coroutines-android":     28,
@@ -790,11 +791,6 @@
 	"libprotobuf-java-lite":          30,
 	"libprotoutil":                   30,
 	"libqemu_pipe":                   30,
-	"libstats_jni":                   30,
-	"libstatslog_statsd":             30,
-	"libstatsmetadata":               30,
-	"libstatspull":                   30,
-	"libstatssocket":                 30,
 	"libsync":                        30,
 	"libtextclassifier_hash_headers": 30,
 	"libtextclassifier_hash_static":  30,
@@ -807,9 +803,6 @@
 	"philox_random_headers":          30,
 	"philox_random":                  30,
 	"service-permission":             30,
-	"service-statsd":                 30,
-	"statsd-aidl-ndk_platform":       30,
-	"statsd":                         30,
 	"tensorflow_headers":             30,
 	"xz-java":                        29,
 })
@@ -856,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/arch.go b/android/arch.go
index b277381..20b4ab0 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -616,16 +616,8 @@
 		osTargets = []Target{osTargets[0]}
 	}
 
-	// Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit.
-	// This is used for HOST_PREFER_32_BIT=true support for Art modules.
-	prefer32 := false
-	if base.prefer32 != nil {
-		prefer32 = base.prefer32(mctx, base, os)
-	}
-	if os == Windows {
-		// Windows builds always prefer 32-bit
-		prefer32 = true
-	}
+	// Windows builds always prefer 32-bit
+	prefer32 := os == Windows
 
 	// Determine the multilib selection for this module.
 	multilib, extraMultilib := decodeMultilib(base, os.Class)
@@ -1442,7 +1434,7 @@
 func getNdkAbisConfig() []archConfig {
 	return []archConfig{
 		{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
-		{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
+		{"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
 		{"x86", "", "", []string{"x86"}},
 		{"x86_64", "", "", []string{"x86_64"}},
 	}
@@ -1609,15 +1601,111 @@
 		} else {
 			buildTargets = firstTarget(targets, "lib64", "lib32")
 		}
+	case "first_prefer32":
+		buildTargets = firstTarget(targets, "lib32", "lib64")
 	case "prefer32":
 		buildTargets = filterMultilibTargets(targets, "lib32")
 		if len(buildTargets) == 0 {
 			buildTargets = filterMultilibTargets(targets, "lib64")
 		}
 	default:
-		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", or "prefer32" found %q`,
+		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
 			multilib)
 	}
 
 	return buildTargets, nil
 }
+
+// GetArchProperties returns a map of architectures to the values of the
+// properties of the 'dst' struct that are specific to that architecture.
+//
+// For example, passing a struct { Foo bool, Bar string } will return an
+// interface{} that can be type asserted back into the same struct, containing
+// the arch specific property value specified by the module if defined.
+func (m *ModuleBase) GetArchProperties(dst interface{}) map[ArchType]interface{} {
+	// Return value of the arch types to the prop values for that arch.
+	archToProp := map[ArchType]interface{}{}
+
+	// Nothing to do for non-arch-specific modules.
+	if !m.ArchSpecific() {
+		return archToProp
+	}
+
+	// archProperties has the type of [][]interface{}. Looks complicated, so let's
+	// explain this step by step.
+	//
+	// Loop over the outer index, which determines the property struct that
+	// contains a matching set of properties in dst that we're interested in.
+	// For example, BaseCompilerProperties or BaseLinkerProperties.
+	for i := range m.archProperties {
+		if m.archProperties[i] == nil {
+			// Skip over nil arch props
+			continue
+		}
+
+		// Non-nil arch prop, let's see if the props match up.
+		for _, arch := range ArchTypeList() {
+			// e.g X86, Arm
+			field := arch.Field
+
+			// If it's not nil, loop over the inner index, which determines the arch variant
+			// of the prop type. In an Android.bp file, this is like looping over:
+			//
+			// arch: { arm: { key: value, ... }, x86: { key: value, ... } }
+			for _, archProperties := range m.archProperties[i] {
+				archPropValues := reflect.ValueOf(archProperties).Elem()
+
+				// This is the archPropRoot struct. Traverse into the Arch nested struct.
+				src := archPropValues.FieldByName("Arch").Elem()
+
+				// Step into non-nil pointers to structs in the src value.
+				if src.Kind() == reflect.Ptr {
+					if src.IsNil() {
+						// Ignore nil pointers.
+						continue
+					}
+					src = src.Elem()
+				}
+
+				// Find the requested field (e.g. x86, x86_64) in the src struct.
+				src = src.FieldByName(field)
+				if !src.IsValid() {
+					continue
+				}
+
+				// We only care about structs. These are not the droids you are looking for.
+				if src.Kind() != reflect.Struct {
+					continue
+				}
+
+				// If the value of the field is a struct  then step into the
+				// BlueprintEmbed field. The special "BlueprintEmbed" name is
+				// used by createArchPropTypeDesc to embed the arch properties
+				// in the parent struct, so the src arch prop should be in this
+				// field.
+				//
+				// See createArchPropTypeDesc for more details on how Arch-specific
+				// module properties are processed from the nested props and written
+				// into the module's archProperties.
+				src = src.FieldByName("BlueprintEmbed")
+
+				// Clone the destination prop, since we want a unique prop struct per arch.
+				dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface()
+
+				// Copy the located property struct into the cloned destination property struct.
+				err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace)
+				if err != nil {
+					// This is fine, it just means the src struct doesn't match.
+					continue
+				}
+
+				// Found the prop for the arch, you have.
+				archToProp[arch] = dstClone
+
+				// Go to the next prop.
+				break
+			}
+		}
+	}
+	return archToProp
+}
diff --git a/android/arch_list.go b/android/arch_list.go
index 0c33b9d..d68a0d1 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -41,6 +41,7 @@
 	},
 	Arm64: {
 		"armv8_a",
+		"armv8_a_branchprot",
 		"armv8_2a",
 		"armv8-2a-dotprod",
 		"cortex-a53",
diff --git a/android/bazel.go b/android/bazel.go
new file mode 100644
index 0000000..9939bd5
--- /dev/null
+++ b/android/bazel.go
@@ -0,0 +1,57 @@
+// 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 android
+
+import "android/soong/bazel"
+
+// BazelModuleBase contains the property structs with metadata for modules which can be converted to
+// Bazel.
+type BazelModuleBase struct {
+	bazelProperties bazel.Properties
+}
+
+// Bazelable is specifies the interface for modules that can be converted to Bazel.
+type Bazelable interface {
+	bazelProps() *bazel.Properties
+	GetBazelLabel() string
+	ConvertWithBp2build() bool
+}
+
+// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
+type BazelModule interface {
+	Module
+	Bazelable
+}
+
+// InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion
+// properties.
+func InitBazelModule(module BazelModule) {
+	module.AddProperties(module.bazelProps())
+}
+
+// bazelProps returns the Bazel properties for the given BazelModuleBase.
+func (b *BazelModuleBase) bazelProps() *bazel.Properties {
+	return &b.bazelProperties
+}
+
+// GetBazelLabel returns the Bazel label for the given BazelModuleBase.
+func (b *BazelModuleBase) GetBazelLabel() string {
+	return b.bazelProperties.Bazel_module.Label
+}
+
+// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
+func (b *BazelModuleBase) ConvertWithBp2build() bool {
+	return b.bazelProperties.Bazel_module.Bp2build_available
+}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index a5c4bed..9cd9fad 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -36,12 +36,14 @@
 
 const (
 	getAllFiles CqueryRequestType = iota
+	getCcObjectFiles
 )
 
 // Map key to describe bazel cquery requests.
 type cqueryKey struct {
 	label       string
 	requestType CqueryRequestType
+	archType    ArchType
 }
 
 type BazelContext interface {
@@ -50,7 +52,11 @@
 	// 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)
+
+	// Returns object files produced by compiling the given cc-related target.
+	// Retrieves these files from Bazel's CcInfo provider.
+	GetCcObjectFiles(label string, archType ArchType) ([]string, bool)
 
 	// TODO(cparsons): Other cquery-related methods should be added here.
 	// ** End cquery methods
@@ -100,7 +106,12 @@
 	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
 }
@@ -123,8 +134,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 +144,21 @@
 	}
 }
 
-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 (n noopBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
+	panic("unimplemented")
+}
+
+func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
 
@@ -207,8 +232,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 {
@@ -241,17 +267,21 @@
 		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 "", fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
 	} else {
 		return string(output), nil
 	}
@@ -273,20 +303,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,25 +408,48 @@
 }
 
 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            ")))
 }
 
 func (context *bazelContext) cqueryStarlarkFileContents() []byte {
@@ -345,23 +459,64 @@
   %s
 }
 
+getCcObjectFilesLabels = {
+  %s
+}
+
+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([f.path for f in target.files.to_list()])
+  elif id_string in getCcObjectFilesLabels:
+    return id_string + ">>" + ', '.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
 
-	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)
+		}
+	}
+	getAllFilesDepsString := strings.Join(getAllFilesDeps, ",\n  ")
+	getCcObjectFilesDepsString := strings.Join(getCcObjectFilesDeps, ",\n  ")
+
+	return []byte(fmt.Sprintf(formatString, getAllFilesDepsString, getCcObjectFilesDepsString))
 }
 
 // Returns a workspace-relative path containing build-related metadata required
@@ -414,9 +569,15 @@
 	}
 	buildrootLabel := "//:buildroot"
 	cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
-		[]string{fmt.Sprintf("deps(%s)", buildrootLabel)},
+		[]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 +592,10 @@
 	}
 
 	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]", getCqueryId(val), cqueryOutput)
 		}
 	}
 
@@ -510,6 +671,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 +695,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 50e39d7..c10ad09 100644
--- a/android/config.go
+++ b/android/config.go
@@ -24,6 +24,7 @@
 	"os"
 	"path/filepath"
 	"runtime"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -127,7 +128,7 @@
 
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
 	// in tests when a path doesn't exist.
-	testAllowNonExistentPaths bool
+	TestAllowNonExistentPaths bool
 
 	// The list of files that when changed, must invalidate soong_build to
 	// regenerate build.ninja.
@@ -231,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{
@@ -246,6 +247,7 @@
 			AAPTCharacteristics:               stringPtr("nosdcard"),
 			AAPTPrebuiltDPI:                   []string{"xhdpi", "xxhdpi"},
 			UncompressPrivAppDex:              boolPtr(true),
+			ShippingApiLevel:                  stringPtr("30"),
 		},
 
 		buildDir:     buildDir,
@@ -254,7 +256,7 @@
 
 		// Set testAllowNonExistentPaths so that test contexts don't need to specify every path
 		// passed to PathForSource or PathForModuleSrc.
-		testAllowNonExistentPaths: true,
+		TestAllowNonExistentPaths: true,
 
 		BazelContext: noopBazelContext{},
 	}
@@ -303,10 +305,7 @@
 	return testConfig
 }
 
-// TestArchConfig returns a Config object suitable for using for tests that
-// need to run the arch mutator.
-func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
-	testConfig := TestConfig(buildDir, env, bp, fs)
+func modifyTestConfigToSupportArchMutator(testConfig Config) {
 	config := testConfig.config
 
 	config.Targets = map[OsType][]Target{
@@ -332,7 +331,13 @@
 	config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
 	config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
 	config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
+}
 
+// TestArchConfig returns a Config object suitable for using for tests that
+// need to run the arch mutator.
+func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	testConfig := TestConfig(buildDir, env, bp, fs)
+	modifyTestConfigToSupportArchMutator(testConfig)
 	return testConfig
 }
 
@@ -911,6 +916,25 @@
 	return "json"
 }
 
+// XrefCuJavaSourceMax returns the maximum number of the Java source files
+// in a single compilation unit
+const xrefJavaSourceFileMaxDefault = "1000"
+
+func (c Config) XrefCuJavaSourceMax() string {
+	v := c.Getenv("KYTHE_JAVA_SOURCE_BATCH_SIZE")
+	if v == "" {
+		return xrefJavaSourceFileMaxDefault
+	}
+	if _, err := strconv.ParseUint(v, 0, 0); err != nil {
+		fmt.Fprintf(os.Stderr,
+			"bad KYTHE_JAVA_SOURCE_BATCH_SIZE value: %s, will use %s",
+			err, xrefJavaSourceFileMaxDefault)
+		return xrefJavaSourceFileMaxDefault
+	}
+	return v
+
+}
+
 func (c *config) EmitXrefRules() bool {
 	return c.XrefCorpusName() != ""
 }
@@ -944,13 +968,7 @@
 // More info: https://source.android.com/devices/architecture/rros
 func (c *config) EnforceRROForModule(name string) bool {
 	enforceList := c.productVariables.EnforceRROTargets
-	// TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
-	exemptedList := c.productVariables.EnforceRROExemptedTargets
-	if len(exemptedList) > 0 {
-		if InList(name, exemptedList) {
-			return false
-		}
-	}
+
 	if len(enforceList) > 0 {
 		if InList("*", enforceList) {
 			return true
@@ -959,11 +977,6 @@
 	}
 	return false
 }
-
-func (c *config) EnforceRROExemptedForModule(name string) bool {
-	return InList(name, c.productVariables.EnforceRROExemptedTargets)
-}
-
 func (c *config) EnforceRROExcludedOverlay(path string) bool {
 	excluded := c.productVariables.EnforceRROExcludedOverlays
 	if len(excluded) > 0 {
@@ -1421,6 +1434,18 @@
 	return c.config.productVariables.RecoverySnapshotModules
 }
 
+func (c *deviceConfig) ShippingApiLevel() ApiLevel {
+	if c.config.productVariables.ShippingApiLevel == nil {
+		return NoneApiLevel
+	}
+	apiLevel, _ := strconv.Atoi(*c.config.productVariables.ShippingApiLevel)
+	return uncheckedFinalApiLevel(apiLevel)
+}
+
+func (c *deviceConfig) BuildBrokenVendorPropertyNamespace() bool {
+	return c.config.productVariables.BuildBrokenVendorPropertyNamespace
+}
+
 // The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
 // Such lists are used in the build system for things like bootclasspath jars or system server jars.
 // The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
diff --git a/android/csuite_config.go b/android/csuite_config.go
index bf24d98..56d2408 100644
--- a/android/csuite_config.go
+++ b/android/csuite_config.go
@@ -40,7 +40,7 @@
 		OutputFile: OptionalPathForPath(me.OutputFilePath),
 	}
 	androidMkEntries.ExtraEntries = []AndroidMkExtraEntriesFunc{
-		func(entries *AndroidMkEntries) {
+		func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
 			if me.properties.Test_config != nil {
 				entries.SetString("LOCAL_TEST_CONFIG", *me.properties.Test_config)
 			}
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/filegroup.go b/android/filegroup.go
index 674a196..2eb4741 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -24,6 +24,10 @@
 	RegisterBp2BuildMutator("filegroup", FilegroupBp2Build)
 }
 
+var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.RegisterModuleType("filegroup", FileGroupFactory)
+})
+
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
 	Srcs bazel.LabelList
@@ -49,7 +53,7 @@
 
 func FilegroupBp2Build(ctx TopDownMutatorContext) {
 	fg, ok := ctx.Module().(*fileGroup)
-	if !ok || !fg.properties.Bazel_module.Bp2build_available {
+	if !ok || !fg.ConvertWithBp2build() {
 		return
 	}
 
@@ -57,9 +61,9 @@
 		Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs),
 	}
 
-	props := bazel.NewBazelTargetModuleProperties(fg.Name(), "filegroup", "")
+	props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
 
-	ctx.CreateBazelTargetModule(BazelFileGroupFactory, props, attrs)
+	ctx.CreateBazelTargetModule(BazelFileGroupFactory, fg.Name(), props, attrs)
 }
 
 type fileGroupProperties struct {
@@ -77,13 +81,11 @@
 	// Create a make variable with the specified name that contains the list of files in the
 	// filegroup, relative to the root of the source tree.
 	Export_to_make_var *string
-
-	// Properties for Bazel migration purposes.
-	bazel.Properties
 }
 
 type fileGroup struct {
 	ModuleBase
+	BazelModuleBase
 	properties fileGroupProperties
 	srcs       Paths
 }
@@ -97,6 +99,7 @@
 	module := &fileGroup{}
 	module.AddProperties(&module.properties)
 	InitAndroidModule(module)
+	InitBazelModule(module)
 	return module
 }
 
diff --git a/android/fixture.go b/android/fixture.go
new file mode 100644
index 0000000..2c8997b
--- /dev/null
+++ b/android/fixture.go
@@ -0,0 +1,737 @@
+// 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 android
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+)
+
+// Provides support for creating test fixtures on which tests can be run. Reduces duplication
+// of test setup by allow tests to easily reuse setup code.
+//
+// Fixture
+// =======
+// These determine the environment within which a test can be run. Fixtures are mutable and are
+// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
+// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
+// instances to it to modify the environment.
+//
+// FixtureFactory
+// ==============
+// These are responsible for creating fixtures. Factories are immutable and are intended to be
+// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
+// preparers that prepare a fixture for running a test. Factories can also be used to create other
+// factories by extending them with additional fixture preparers.
+//
+// FixturePreparer
+// ===============
+// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are
+// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
+// them sharing any data.
+//
+// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
+// FixturePreparers are flattened and deduped while preserving the order they first appear in the
+// list. This makes it easy to reuse, group and combine FixturePreparers together.
+//
+// Each small self contained piece of test setup should be their own FixturePreparer. e.g.
+// * A group of related modules.
+// * A group of related mutators.
+// * A combination of both.
+// * Configuration.
+//
+// They should not overlap, e.g. the same module type should not be registered by different
+// FixturePreparers as using them both would cause a build error. In that case the preparer should
+// be split into separate parts and combined together using FixturePreparers(...).
+//
+// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to
+// register module bar twice:
+//   var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
+//   var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
+//   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 = 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:
+// 1. PreparerFoo
+// 2. PreparerBar
+// 3. PreparerBaz
+//
+// Preparers can be used for both integration and unit tests.
+//
+// Integration tests typically use all the module types, mutators and singletons that are available
+// for that package to try and replicate the behavior of the runtime build as closely as possible.
+// However, that realism comes at a cost of increased fragility (as they can be broken by changes in
+// many different parts of the build) and also increased runtime, especially if they use lots of
+// singletons and mutators.
+//
+// Unit tests on the other hand try and minimize the amount of code being tested which makes them
+// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially
+// not testing realistic scenarios.
+//
+// Supporting unit tests effectively require that preparers are available at the lowest granularity
+// possible. Supporting integration tests effectively require that the preparers are organized into
+// groups that provide all the functionality available.
+//
+// At least in terms of tests that check the behavior of build components via processing
+// `Android.bp` there is no clear separation between a unit test and an integration test. Instead
+// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a
+// whole system of modules, mutators and singletons (e.g. apex + hiddenapi).
+//
+// TestResult
+// ==========
+// These are created by running tests in a Fixture and provide access to the Config and TestContext
+// in which the tests were run.
+//
+// Example
+// =======
+//
+// An exported preparer for use by other packages that need to use java modules.
+//
+// package java
+// var PrepareForIntegrationTestWithJava = GroupFixturePreparers(
+//    android.PrepareForIntegrationTestWithAndroid,
+//    FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
+//    FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
+//    ...
+// )
+//
+// Some files to use in tests in the java package.
+//
+// var javaMockFS = android.MockFS{
+//		"api/current.txt":        nil,
+//		"api/removed.txt":        nil,
+//    ...
+// }
+//
+// A package private factory for use for testing java within the java package.
+//
+// var javaFixtureFactory = NewFixtureFactory(
+//    PrepareForIntegrationTestWithJava,
+//    FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+//      ctx.RegisterModuleType("test_module", testModule)
+//    }),
+//    javaMockFS.AddToFixture(),
+//    ...
+// }
+//
+// func TestJavaStuff(t *testing.T) {
+//   result := javaFixtureFactory.RunTest(t,
+//       android.FixtureWithRootAndroidBp(`java_library {....}`),
+//       android.MockFS{...}.AddToFixture(),
+//   )
+//   ... test result ...
+// }
+//
+// package cc
+// var PrepareForTestWithCC = GroupFixturePreparers(
+//    android.PrepareForArchMutator,
+//	  android.prepareForPrebuilts,
+//    FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+//    ...
+// )
+//
+// package apex
+//
+// var PrepareForApex = GroupFixturePreparers(
+//    ...
+// )
+//
+// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
+// android.PrepareForArchMutator) will be automatically deduped.
+//
+// var apexFixtureFactory = android.NewFixtureFactory(
+//    PrepareForJava,
+//    PrepareForCC,
+//    PrepareForApex,
+// )
+
+// Factory for Fixture objects.
+//
+// This is configured with a set of FixturePreparer objects that are used to
+// initialize each Fixture instance this creates.
+type FixtureFactory interface {
+
+	// Creates a copy of this instance and adds some additional preparers.
+	//
+	// Before the preparers are used they are combined with the preparers provided when the factory
+	// was created, any groups of preparers are flattened, and the list is deduped so that each
+	// preparer is only used once. See the file documentation in android/fixture.go for more details.
+	Extend(preparers ...FixturePreparer) FixtureFactory
+
+	// Create a Fixture.
+	Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
+
+	// SetErrorHandler 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.
+	SetErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory
+
+	// Run the test, checking any errors reported and returning a TestResult instance.
+	//
+	// Shorthand for Fixture(t, preparers...).RunTest()
+	RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
+
+	// Run the test with the supplied Android.bp file.
+	//
+	// Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
+	RunTestWithBp(t *testing.T, bp string) *TestResult
+}
+
+// Create a new FixtureFactory that will apply the supplied preparers.
+//
+// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
+// the package level setUp method. It has to be a pointer to the variable as the variable will not
+// have been initialized at the time the factory is created.
+func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
+	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
+
+func (fs MockFS) Merge(extra map[string][]byte) {
+	for p, c := range extra {
+		fs[p] = c
+	}
+}
+
+func (fs MockFS) AddToFixture() FixturePreparer {
+	return FixtureMergeMockFs(fs)
+}
+
+// Modify the config
+func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
+	return newSimpleFixturePreparer(func(f *fixture) {
+		mutator(f.config)
+	})
+}
+
+// Modify the config and context
+func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer {
+	return newSimpleFixturePreparer(func(f *fixture) {
+		mutator(f.config, f.ctx)
+	})
+}
+
+// Modify the context
+func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer {
+	return newSimpleFixturePreparer(func(f *fixture) {
+		mutator(f.ctx)
+	})
+}
+
+func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
+	return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
+}
+
+// Modify the mock filesystem
+func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
+	return newSimpleFixturePreparer(func(f *fixture) {
+		mutator(f.mockFS)
+	})
+}
+
+// Merge the supplied file system into the mock filesystem.
+//
+// Paths that already exist in the mock file system are overridden.
+func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
+	return FixtureModifyMockFS(func(fs MockFS) {
+		fs.Merge(mockFS)
+	})
+}
+
+// Add a file to the mock filesystem
+func FixtureAddFile(path string, contents []byte) FixturePreparer {
+	return FixtureModifyMockFS(func(fs MockFS) {
+		fs[path] = contents
+	})
+}
+
+// Add a text file to the mock filesystem
+func FixtureAddTextFile(path string, contents string) FixturePreparer {
+	return FixtureAddFile(path, []byte(contents))
+}
+
+// Add the root Android.bp file with the supplied contents.
+func FixtureWithRootAndroidBp(contents string) FixturePreparer {
+	return FixtureAddTextFile("Android.bp", contents)
+}
+
+// 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)}
+}
+
+type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
+
+// FixturePreparer is an opaque interface that can change a fixture.
+type FixturePreparer interface {
+	// visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
+	visit(simpleFixturePreparerVisitor)
+}
+
+type fixturePreparers []FixturePreparer
+
+func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
+	for _, p := range f {
+		p.visit(visitor)
+	}
+}
+
+// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
+// instances.
+//
+// base      - a list of already flattened and deduped preparers that will be applied first before
+//             the list of additional preparers. Any duplicates of these in the additional preparers
+//             will be ignored.
+//
+// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
+//             base preparers.
+//
+// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
+func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
+	var list []*simpleFixturePreparer
+	visited := make(map[*simpleFixturePreparer]struct{})
+
+	// Mark the already flattened and deduped preparers, if any, as having been seen so that
+	// duplicates of these in the additional preparers will be discarded.
+	for _, s := range base {
+		visited[s] = struct{}{}
+	}
+
+	preparers.visit(func(preparer *simpleFixturePreparer) {
+		if _, seen := visited[preparer]; !seen {
+			visited[preparer] = struct{}{}
+			list = append(list, preparer)
+		}
+	})
+	return list
+}
+
+// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
+type compositeFixturePreparer struct {
+	preparers []*simpleFixturePreparer
+}
+
+func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
+	for _, p := range c.preparers {
+		p.visit(visitor)
+	}
+}
+
+// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
+type simpleFixturePreparer struct {
+	function func(fixture *fixture)
+}
+
+func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
+	visitor(s)
+}
+
+func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
+	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, checking any errors reported and returning a TestResult instance.
+	RunTest() *TestResult
+}
+
+// Provides general test support.
+type TestHelper struct {
+	*testing.T
+}
+
+// AssertBoolEquals 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) AssertBoolEquals(message string, expected bool, actual bool) {
+	h.Helper()
+	if actual != expected {
+		h.Errorf("%s: expected %t, actual %t", message, expected, actual)
+	}
+}
+
+// AssertStringEquals 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) AssertStringEquals(message string, expected string, actual string) {
+	h.Helper()
+	if actual != expected {
+		h.Errorf("%s: expected %s, actual %s", message, expected, actual)
+	}
+}
+
+// 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.
+func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
+	h.Helper()
+	h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
+}
+
+// AssertStringDoesContain checks if the string contains the expected substring. 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) AssertStringDoesContain(message string, s string, expectedSubstring string) {
+	h.Helper()
+	if !strings.Contains(s, expectedSubstring) {
+		h.Errorf("%s: could not find %q within %q", message, expectedSubstring, s)
+	}
+}
+
+// AssertStringDoesNotContain checks if the string contains the expected substring. If it does then
+// it reports an error prefixed with the supplied message and including a reason for why it failed.
+func (h *TestHelper) AssertStringDoesNotContain(message string, s string, unexpectedSubstring string) {
+	h.Helper()
+	if strings.Contains(s, unexpectedSubstring) {
+		h.Errorf("%s: unexpectedly found %q within %q", message, unexpectedSubstring, s)
+	}
+}
+
+// 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) {
+	h.Helper()
+	if len(actual) != len(expected) {
+		h.Errorf("%s: expected %d (%q), actual (%d) %q", message, len(expected), expected, len(actual), actual)
+		return
+	}
+	for i := range actual {
+		if actual[i] != expected[i] {
+			h.Errorf("%s: expected %d-th, %q (%q), actual %q (%q)",
+				message, i, expected[i], expected, actual[i], actual)
+			return
+		}
+	}
+}
+
+// AssertArrayString 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{}) {
+	h.Helper()
+	if !reflect.DeepEqual(actual, expected) {
+		h.Errorf("%s: expected:\n  %#v\n got:\n  %#v", message, expected, actual)
+	}
+}
+
+// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
+type testContext struct {
+	*TestContext
+}
+
+// The result of running a test.
+type TestResult struct {
+	TestHelper
+	testContext
+
+	fixture *fixture
+	Config  Config
+
+	// The errors that were reported during the test.
+	Errs []error
+}
+
+var _ FixtureFactory = (*fixtureFactory)(nil)
+
+type fixtureFactory struct {
+	buildDirSupplier *string
+	preparers        []*simpleFixturePreparer
+	errorHandler     FixtureErrorHandler
+}
+
+func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
+	all := append(f.preparers, 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),
+		errorHandler: f.errorHandler,
+	}
+
+	for _, preparer := range f.preparers {
+		preparer.function(fixture)
+	}
+
+	for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
+		preparer.function(fixture)
+	}
+
+	return fixture
+}
+
+func (f *fixtureFactory) SetErrorHandler(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...)
+	return fixture.RunTest()
+}
+
+func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
+	t.Helper()
+	return f.RunTest(t, FixtureWithRootAndroidBp(bp))
+}
+
+type fixture struct {
+	// The factory used to create this fixture.
+	factory *fixtureFactory
+
+	// 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 {
+	f.t.Helper()
+
+	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)
+	}
+
+	ctx.Register()
+	_, errs := ctx.ParseBlueprintsFiles("ignored")
+	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
+}
+
+// NormalizePathForTesting removes the test invocation specific build directory from the supplied
+// path.
+//
+// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
+// path to avoid tests having to deal with the dynamically generated build directory.
+//
+// Otherwise, this returns the supplied path as it is almost certainly a source path that is
+// relative to the root of the source tree.
+//
+// Even though some information is removed from some paths and not others it should be possible to
+// differentiate between them by the paths themselves, e.g. output paths will likely include
+// ".intermediates" but source paths won't.
+func (r *TestResult) NormalizePathForTesting(path Path) string {
+	pathContext := PathContextForTesting(r.Config)
+	pathAsString := path.String()
+	if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
+		return rel
+	}
+	return pathAsString
+}
+
+// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
+// forms.
+func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
+	var result []string
+	for _, path := range paths {
+		result = append(result, r.NormalizePathForTesting(path))
+	}
+	return result
+}
+
+// NewFixture creates a new test fixture that is based on the one that created this result. It is
+// intended to test the output of module types that generate content to be processed by the build,
+// e.g. sdk snapshots.
+func (r *TestResult) NewFixture(preparers ...FixturePreparer) Fixture {
+	return r.fixture.factory.Fixture(r.T, preparers...)
+}
+
+// RunTest is shorthand for NewFixture(preparers...).RunTest().
+func (r *TestResult) RunTest(preparers ...FixturePreparer) *TestResult {
+	r.Helper()
+	return r.fixture.factory.Fixture(r.T, preparers...).RunTest()
+}
+
+// Module returns the module with the specific name and of the specified variant.
+func (r *TestResult) Module(name string, variant string) Module {
+	return r.ModuleForTests(name, variant).Module()
+}
+
+// Create a *TestResult object suitable for use within a subtest.
+//
+// This ensures that any errors reported by the TestResult, e.g. from within one of its
+// Assert... methods, will be associated with the sub test and not the main test.
+//
+// result := ....RunTest()
+// t.Run("subtest", func(t *testing.T) {
+//    subResult := result.ResultForSubTest(t)
+//    subResult.AssertStringEquals("something", ....)
+// })
+func (r *TestResult) ResultForSubTest(t *testing.T) *TestResult {
+	subTestResult := *r
+	r.T = t
+	return &subTestResult
+}
diff --git a/android/fixture_test.go b/android/fixture_test.go
new file mode 100644
index 0000000..5a7bf3b
--- /dev/null
+++ b/android/fixture_test.go
@@ -0,0 +1,49 @@
+// 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 android
+
+import "testing"
+
+// Make sure that FixturePreparer instances are only called once per fixture and in the order in
+// which they were added.
+func TestFixtureDedup(t *testing.T) {
+	list := []string{}
+
+	appendToList := func(s string) FixturePreparer {
+		return FixtureModifyConfig(func(_ Config) {
+			list = append(list, s)
+		})
+	}
+
+	preparer1 := appendToList("preparer1")
+	preparer2 := appendToList("preparer2")
+	preparer3 := appendToList("preparer3")
+	preparer4 := appendToList("preparer4")
+
+	preparer1Then2 := GroupFixturePreparers(preparer1, preparer2)
+
+	preparer2Then1 := GroupFixturePreparers(preparer2, preparer1)
+
+	buildDir := "build"
+	factory := NewFixtureFactory(&buildDir, preparer1, preparer2, preparer1, preparer1Then2)
+
+	extension := factory.Extend(preparer4, preparer2)
+
+	extension.Fixture(t, preparer1, preparer2, preparer2Then1, preparer3)
+
+	h := TestHelper{t}
+	h.AssertDeepEquals("preparers called in wrong order",
+		[]string{"preparer1", "preparer2", "preparer4", "preparer3"}, list)
+}
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_test.go b/android/license_test.go
index 552bbae..9f68713 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -49,9 +49,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 +70,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`,
 		},
 	},
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..c043791 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -7,15 +7,15 @@
 )
 
 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                         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: "invalid module type without licenses property",
@@ -71,28 +71,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 +147,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 +218,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 +285,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 +327,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 +370,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 +449,7 @@
 		},
 		effectiveInheritedLicenses: map[string][]string{
 			"module": []string{"prebuilt", "top_sources"},
-			"other": []string{"prebuilt", "top_sources"},
+			"other":  []string{"prebuilt", "top_sources"},
 		},
 	},
 }
diff --git a/android/module.go b/android/module.go
index bf74cad..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
@@ -507,13 +508,17 @@
 type BazelTargetModule interface {
 	Module
 
-	BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties
+	bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties
+	SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties)
+
+	RuleClass() string
+	BzlLoadLocation() string
 }
 
 // InitBazelTargetModule is a wrapper function that decorates BazelTargetModule
 // with property structs containing metadata for bp2build conversion.
 func InitBazelTargetModule(module BazelTargetModule) {
-	module.AddProperties(module.BazelTargetModuleProperties())
+	module.AddProperties(module.bazelTargetModuleProperties())
 	InitAndroidModule(module)
 }
 
@@ -524,11 +529,26 @@
 	Properties bazel.BazelTargetModuleProperties
 }
 
-// BazelTargetModuleProperties getter.
-func (btmb *BazelTargetModuleBase) BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties {
+// bazelTargetModuleProperties getter.
+func (btmb *BazelTargetModuleBase) bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties {
 	return &btmb.Properties
 }
 
+// SetBazelTargetModuleProperties setter for BazelTargetModuleProperties
+func (btmb *BazelTargetModuleBase) SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties) {
+	btmb.Properties = props
+}
+
+// RuleClass returns the rule class for this Bazel target
+func (b *BazelTargetModuleBase) RuleClass() string {
+	return b.bazelTargetModuleProperties().Rule_class
+}
+
+// BzlLoadLocation returns the rule class for this Bazel target
+func (b *BazelTargetModuleBase) BzlLoadLocation() string {
+	return b.bazelTargetModuleProperties().Bzl_load_location
+}
+
 // Qualified id for a module
 type qualifiedModuleName struct {
 	// The package (i.e. directory) in which the module is defined, without trailing /
@@ -733,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
@@ -1104,8 +1124,15 @@
 	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
 	generalProperties       []interface{}
-	archProperties          [][]interface{}
-	customizableProperties  []interface{}
+
+	// Arch specific versions of structs in generalProperties. The outer index
+	// has the same order as generalProperties as initialized in
+	// InitAndroidArchModule, and the inner index chooses the props specific to
+	// the architecture. The interface{} value is an archPropRoot that is
+	// filled with arch specific values by the arch mutator.
+	archProperties [][]interface{}
+
+	customizableProperties []interface{}
 
 	// Properties specific to the Blueprint to BUILD migration.
 	bazelTargetModuleProperties bazel.BazelTargetModuleProperties
@@ -1149,8 +1176,6 @@
 
 	initRcPaths         Paths
 	vintfFragmentsPaths Paths
-
-	prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool
 }
 
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
@@ -1177,10 +1202,6 @@
 	return m.variables
 }
 
-func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool) {
-	m.prefer32 = prefer32
-}
-
 // Name returns the name of the module.  It may be overridden by individual module types, for
 // example prebuilts will prepend prebuilt_ to the name.
 func (m *ModuleBase) Name() string {
@@ -1811,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
@@ -1823,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 c387193..b023001 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -283,7 +283,7 @@
 	// factory method, just like in CreateModule, but also requires
 	// BazelTargetModuleProperties containing additional metadata for the
 	// bp2build codegenerator.
-	CreateBazelTargetModule(ModuleFactory, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule
+	CreateBazelTargetModule(ModuleFactory, string, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule
 }
 
 type topDownMutatorContext struct {
@@ -513,17 +513,25 @@
 
 func (t *topDownMutatorContext) CreateBazelTargetModule(
 	factory ModuleFactory,
+	name string,
 	bazelProps bazel.BazelTargetModuleProperties,
 	attrs interface{}) BazelTargetModule {
-	if !strings.HasPrefix(*bazelProps.Name, bazel.BazelTargetModuleNamePrefix) {
+	if strings.HasPrefix(name, bazel.BazelTargetModuleNamePrefix) {
 		panic(fmt.Errorf(
-			"bp2build error: the bazel target module name must start with '%s': %s",
+			"The %s name prefix is added automatically, do not set it manually: %s",
 			bazel.BazelTargetModuleNamePrefix,
-			*bazelProps.Name,
-		))
+			name))
+	}
+	name = bazel.BazelTargetModuleNamePrefix + name
+	nameProp := struct {
+		Name *string
+	}{
+		Name: &name,
 	}
 
-	return t.CreateModule(factory, &bazelProps, attrs).(BazelTargetModule)
+	b := t.CreateModule(factory, &nameProp, attrs).(BazelTargetModule)
+	b.SetBazelTargetModuleProperties(bazelProps)
+	return b
 }
 
 func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
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 4bb706a..853e5a9 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -76,52 +76,73 @@
 	var ret []string
 
 	for _, i := range pathPropertyIndexes {
-		// Turn an index into a field.
-		sv := fieldByIndex(v, i)
-		if !sv.IsValid() {
-			// Skip properties inside a nil pointer.
-			continue
-		}
-
-		// If the field is a non-nil pointer step into it.
-		if sv.Kind() == reflect.Ptr {
-			if sv.IsNil() {
+		var values []reflect.Value
+		fieldsByIndex(v, i, &values)
+		for _, sv := range values {
+			if !sv.IsValid() {
+				// Skip properties inside a nil pointer.
 				continue
 			}
-			sv = sv.Elem()
-		}
 
-		// Collect paths from all strings and slices of strings.
-		switch sv.Kind() {
-		case reflect.String:
-			ret = append(ret, sv.String())
-		case reflect.Slice:
-			ret = append(ret, sv.Interface().([]string)...)
-		default:
-			panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
-				v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+			// If the field is a non-nil pointer step into it.
+			if sv.Kind() == reflect.Ptr {
+				if sv.IsNil() {
+					continue
+				}
+				sv = sv.Elem()
+			}
+
+			// Collect paths from all strings and slices of strings.
+			switch sv.Kind() {
+			case reflect.String:
+				ret = append(ret, sv.String())
+			case reflect.Slice:
+				ret = append(ret, sv.Interface().([]string)...)
+			default:
+				panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
+					v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+			}
 		}
 	}
 
 	return ret
 }
 
-// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when
-// traversing a nil pointer to a struct.
-func fieldByIndex(v reflect.Value, index []int) reflect.Value {
+// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track
+// nil pointers and it returns multiple values when there's slice of struct.
+func fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) {
+	// leaf case
 	if len(index) == 1 {
-		return v.Field(index[0])
-	}
-	for _, x := range index {
-		if v.Kind() == reflect.Ptr {
-			if v.IsNil() {
-				return reflect.Value{}
+		if isSliceOfStruct(v) {
+			for i := 0; i < v.Len(); i++ {
+				*values = append(*values, v.Index(i).Field(index[0]))
 			}
-			v = v.Elem()
+		} else {
+			*values = append(*values, v.Field(index[0]))
 		}
-		v = v.Field(x)
+		return
 	}
-	return v
+
+	// recursion
+	if v.Kind() == reflect.Ptr {
+		// don't track nil pointer
+		if v.IsNil() {
+			return
+		}
+		v = v.Elem()
+	} else if isSliceOfStruct(v) {
+		// do the recursion for all elements
+		for i := 0; i < v.Len(); i++ {
+			fieldsByIndex(v.Index(i).Field(index[0]), index[1:], values)
+		}
+		return
+	}
+	fieldsByIndex(v.Field(index[0]), index[1:], values)
+	return
+}
+
+func isSliceOfStruct(v reflect.Value) bool {
+	return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct
 }
 
 var pathPropertyIndexesCache OncePer
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index f964d9f..2aab748 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -33,12 +33,21 @@
 		Foo string `android:"path"`
 	}
 
+	// nested slices of struct
+	props3 struct {
+		X []struct {
+			Y []struct {
+				Z []string `android:"path"`
+			}
+		}
+	}
+
 	sourceDeps []string
 }
 
 func pathDepsMutatorTestModuleFactory() Module {
 	module := &pathDepsMutatorTestModule{}
-	module.AddProperties(&module.props, &module.props2)
+	module.AddProperties(&module.props, &module.props2, &module.props3)
 	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
 	return module
 }
@@ -73,8 +82,20 @@
 				bar: [":b"],
 				baz: ":c{.bar}",
 				qux: ":d",
+				x: [
+					{
+						y: [
+							{
+								z: [":x", ":y"],
+							},
+							{
+								z: [":z"],
+							},
+						],
+					},
+				],
 			}`,
-			deps: []string{"a", "b", "c"},
+			deps: []string{"a", "b", "c", "x", "y", "z"},
 		},
 		{
 			name: "arch variant",
@@ -113,6 +134,18 @@
 				filegroup {
 					name: "d",
 				}
+
+				filegroup {
+					name: "x",
+				}
+
+				filegroup {
+					name: "y",
+				}
+
+				filegroup {
+					name: "z",
+				}
 			`
 
 			config := TestArchConfig(buildDir, nil, bp, nil)
diff --git a/android/paths.go b/android/paths.go
index c3fa61a..3f4d3f2 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -280,7 +280,8 @@
 	return false
 }
 
-// PathsForSource returns Paths rooted from SrcDir
+// PathsForSource returns Paths rooted from SrcDir, *not* rooted from the module's local source
+// directory
 func PathsForSource(ctx PathContext, paths []string) Paths {
 	ret := make(Paths, len(paths))
 	for i, path := range paths {
@@ -289,9 +290,9 @@
 	return ret
 }
 
-// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
-// found in the tree. If any are not found, they are omitted from the list,
-// and dependencies are added so that we're re-run when they are added.
+// ExistentPathsForSources returns a list of Paths rooted from SrcDir, *not* rooted from the
+// module's local source directory, that are found in the tree. If any are not found, they are
+// omitted from the list, and dependencies are added so that we're re-run when they are added.
 func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
 	ret := make(Paths, 0, len(paths))
 	for _, path := range paths {
@@ -395,6 +396,9 @@
 // `android:"path"` so that dependencies on other modules will have already been handled by the
 // path_properties mutator.
 func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
+	if paths == nil {
+		return bazel.LabelList{}
+	}
 	labels := bazel.LabelList{
 		Includes: []bazel.Label{},
 	}
@@ -582,7 +586,7 @@
 		p := pathForModuleSrc(ctx, sPath)
 		if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
 			ReportPathErrorf(ctx, "%s: %s", p, err.Error())
-		} else if !exists && !ctx.Config().testAllowNonExistentPaths {
+		} else if !exists && !ctx.Config().TestAllowNonExistentPaths {
 			ReportPathErrorf(ctx, "module source path %q does not exist", p)
 		}
 
@@ -1018,15 +1022,16 @@
 		}
 	} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
 		ReportPathErrorf(ctx, "%s: %s", path, err.Error())
-	} else if !exists && !ctx.Config().testAllowNonExistentPaths {
+	} else if !exists && !ctx.Config().TestAllowNonExistentPaths {
 		ReportPathErrorf(ctx, "source path %q does not exist", path)
 	}
 	return path
 }
 
-// ExistentPathForSource returns an OptionalPath with the SourcePath if the
-// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
-// so that the ninja file will be regenerated if the state of the path changes.
+// ExistentPathForSource returns an OptionalPath with the SourcePath, rooted from SrcDir, *not*
+// rooted from the module's local source directory, if the path exists, or an empty OptionalPath if
+// it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state
+// of the path changes.
 func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
 	path, err := pathForSource(ctx, pathComponents...)
 	if err != nil {
@@ -1657,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 9ac3875..32af5df 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -262,7 +262,7 @@
 }
 
 func TestPrebuilts(t *testing.T) {
-	fs := map[string][]byte{
+	fs := MockFS{
 		"prebuilt_file": nil,
 		"source_file":   nil,
 	}
@@ -277,32 +277,33 @@
 						deps: [":bar"],
 					}`
 			}
-			config := TestArchConfig(buildDir, nil, bp, fs)
 
 			// Add windows to the target list to test the logic when a variant is
 			// disabled by default.
 			if !Windows.DefaultDisabled {
 				t.Errorf("windows is assumed to be disabled by default")
 			}
-			config.config.Targets[Windows] = []Target{
-				{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
-			}
 
-			ctx := NewTestArchContext(config)
-			registerTestPrebuiltBuildComponents(ctx)
-			ctx.RegisterModuleType("filegroup", FileGroupFactory)
-			ctx.Register()
+			result := emptyTestFixtureFactory.Extend(
+				PrepareForTestWithArchMutator,
+				PrepareForTestWithPrebuilts,
+				PrepareForTestWithOverrides,
+				PrepareForTestWithFilegroup,
+				// Add a Windows target to the configuration.
+				FixtureModifyConfig(func(config Config) {
+					config.Targets[Windows] = []Target{
+						{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
+					}
+				}),
+				fs.AddToFixture(),
+				FixtureRegisterWithContext(registerTestPrebuiltModules),
+			).RunTestWithBp(t, bp)
 
-			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
-			FailIfErrored(t, errs)
-			_, errs = ctx.PrepareBuildActions(config)
-			FailIfErrored(t, errs)
-
-			for _, variant := range ctx.ModuleVariantsForTests("foo") {
-				foo := ctx.ModuleForTests("foo", variant)
+			for _, variant := range result.ModuleVariantsForTests("foo") {
+				foo := result.ModuleForTests("foo", variant)
 				t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
 					var dependsOnSourceModule, dependsOnPrebuiltModule bool
-					ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
+					result.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
 						if _, ok := m.(*sourceModule); ok {
 							dependsOnSourceModule = true
 						}
@@ -381,14 +382,20 @@
 }
 
 func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
-	ctx.RegisterModuleType("source", newSourceModule)
-	ctx.RegisterModuleType("override_source", newOverrideSourceModule)
+	registerTestPrebuiltModules(ctx)
 
 	RegisterPrebuiltMutators(ctx)
 	ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
 }
 
+var prepareForTestWithFakePrebuiltModules = FixtureRegisterWithContext(registerTestPrebuiltModules)
+
+func registerTestPrebuiltModules(ctx RegistrationContext) {
+	ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
+	ctx.RegisterModuleType("source", newSourceModule)
+	ctx.RegisterModuleType("override_source", newOverrideSourceModule)
+}
+
 type prebuiltModule struct {
 	ModuleBase
 	prebuilt   Prebuilt
diff --git a/android/register.go b/android/register.go
index 18c743f..47df972 100644
--- a/android/register.go
+++ b/android/register.go
@@ -168,6 +168,7 @@
 type RegistrationContext interface {
 	RegisterModuleType(name string, factory ModuleFactory)
 	RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
+	RegisterPreSingletonType(name string, factory SingletonFactory)
 	RegisterSingletonType(name string, factory SingletonFactory)
 	PreArchMutators(f RegisterMutatorFunc)
 
@@ -208,6 +209,7 @@
 type initRegistrationContext struct {
 	moduleTypes        map[string]ModuleFactory
 	singletonTypes     map[string]SingletonFactory
+	preSingletonTypes  map[string]SingletonFactory
 	moduleTypesForDocs map[string]reflect.Value
 }
 
@@ -238,6 +240,14 @@
 	RegisterSingletonType(name, factory)
 }
 
+func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
+	if _, present := ctx.preSingletonTypes[name]; present {
+		panic(fmt.Sprintf("pre singleton type %q is already registered", name))
+	}
+	ctx.preSingletonTypes[name] = factory
+	RegisterPreSingletonType(name, factory)
+}
+
 func (ctx *initRegistrationContext) PreArchMutators(f RegisterMutatorFunc) {
 	PreArchMutators(f)
 }
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/testing.go b/android/testing.go
index de338bf..d8c123b 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -48,6 +48,43 @@
 	return ctx
 }
 
+var PrepareForTestWithArchMutator = GroupFixturePreparers(
+	// Configure architecture targets in the fixture config.
+	FixtureModifyConfig(modifyTestConfigToSupportArchMutator),
+
+	// Add the arch mutator to the context.
+	FixtureRegisterWithContext(func(ctx RegistrationContext) {
+		ctx.PreDepsMutators(registerArchMutator)
+	}),
+)
+
+var PrepareForTestWithDefaults = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+})
+
+var PrepareForTestWithComponentsMutator = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterComponentsMutator)
+})
+
+var PrepareForTestWithPrebuilts = FixtureRegisterWithContext(RegisterPrebuiltMutators)
+
+var PrepareForTestWithOverrides = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
+})
+
+// Prepares an integration test with build components from the android package.
+var PrepareForIntegrationTestWithAndroid = GroupFixturePreparers(
+	// Mutators. Must match order in mutator.go.
+	PrepareForTestWithArchMutator,
+	PrepareForTestWithDefaults,
+	PrepareForTestWithComponentsMutator,
+	PrepareForTestWithPrebuilts,
+	PrepareForTestWithOverrides,
+
+	// Modules
+	PrepareForTestWithFilegroup,
+)
+
 func NewTestArchContext(config Config) *TestContext {
 	ctx := NewTestContext(config)
 	ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@@ -140,6 +177,10 @@
 	ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
 }
 
+func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
+	ctx.Context.RegisterPreSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
+}
+
 func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
 	var module Module
 	ctx.VisitAllModules(func(m blueprint.Module) {
@@ -201,6 +242,10 @@
 		"\nall singletons: %v", name, allSingletonNames))
 }
 
+func (ctx *TestContext) Config() Config {
+	return ctx.config
+}
+
 type testBuildProvider interface {
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -411,12 +456,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
@@ -432,6 +480,8 @@
 			t.Errorf("errs[%d] = %q", i, err)
 		}
 	}
+
+	return found
 }
 
 func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
@@ -452,16 +502,16 @@
 			for i, err := range errs {
 				t.Errorf("errs[%d] = %s", i, err)
 			}
+			t.FailNow()
 		}
 	}
-
 }
 
 func SetKatiEnabledForTests(config Config) {
 	config.katiEnabled = true
 }
 
-func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
+func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
 	var p AndroidMkEntriesProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkEntriesProvider); !ok {
@@ -470,19 +520,19 @@
 
 	entriesList := p.AndroidMkEntries()
 	for i, _ := range entriesList {
-		entriesList[i].fillInEntries(config, bpPath, mod)
+		entriesList[i].fillInEntries(ctx, mod)
 	}
 	return entriesList
 }
 
-func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkData {
+func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData {
 	var p AndroidMkDataProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkDataProvider); !ok {
 		t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
 	}
 	data := p.AndroidMk()
-	data.fillInData(config, bpPath, mod)
+	data.fillInData(ctx, mod)
 	return data
 }
 
diff --git a/android/util.go b/android/util.go
index 0f940fa..506f8f7 100644
--- a/android/util.go
+++ b/android/util.go
@@ -137,6 +137,16 @@
 	return false
 }
 
+// Returns true if any string in the given list has the given substring.
+func SubstringInList(list []string, substr string) bool {
+	for _, s := range list {
+		if strings.Contains(s, substr) {
+			return true
+		}
+	}
+	return false
+}
+
 // Returns true if any string in the given list has the given prefix.
 func PrefixInList(list []string, prefix string) bool {
 	for _, s := range list {
diff --git a/android/variable.go b/android/variable.go
index e76d683..dd000ad 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -199,11 +199,9 @@
 	CrossHostArch          *string `json:",omitempty"`
 	CrossHostSecondaryArch *string `json:",omitempty"`
 
-	DeviceResourceOverlays  []string `json:",omitempty"`
-	ProductResourceOverlays []string `json:",omitempty"`
-	EnforceRROTargets       []string `json:",omitempty"`
-	// TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
-	EnforceRROExemptedTargets  []string `json:",omitempty"`
+	DeviceResourceOverlays     []string `json:",omitempty"`
+	ProductResourceOverlays    []string `json:",omitempty"`
+	EnforceRROTargets          []string `json:",omitempty"`
 	EnforceRROExcludedOverlays []string `json:",omitempty"`
 
 	AAPTCharacteristics *string  `json:",omitempty"`
@@ -367,6 +365,10 @@
 	BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
 
 	PrebuiltHiddenApiDir *string `json:",omitempty"`
+
+	ShippingApiLevel *string `json:",omitempty"`
+
+	BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/android/visibility.go b/android/visibility.go
index 7eac471..631e88f 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -202,6 +202,18 @@
 	ExcludeFromVisibilityEnforcement()
 }
 
+var PrepareForTestWithVisibilityRuleChecker = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+})
+
+var PrepareForTestWithVisibilityRuleGatherer = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+})
+
+var PrepareForTestWithVisibilityRuleEnforcer = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	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..30cdcbf 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,43 @@
 func TestVisibility(t *testing.T) {
 	for _, test := range visibilityTests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx, errs := testVisibility(buildDir, test.fs)
-
-			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+			result := emptyTestFixtureFactory.Extend(
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("mock_library", newMockLibraryModule)
+					ctx.RegisterModuleType("mock_parent", newMockParentFactory)
+					ctx.RegisterModuleType("mock_defaults", defaultsFactory)
+				}),
+				prepareForTestWithFakePrebuiltModules,
+				PrepareForTestWithPackageModule,
+				// Order of the following method calls is significant as they register mutators.
+				PrepareForTestWithArchMutator,
+				PrepareForTestWithPrebuilts,
+				PrepareForTestWithOverrides,
+				PrepareForTestWithVisibilityRuleChecker,
+				PrepareForTestWithDefaults,
+				PrepareForTestWithVisibilityRuleGatherer,
+				PrepareForTestWithVisibilityRuleEnforcer,
+				// Add additional files to the mock filesystem
+				test.fs.AddToFixture(),
+			).
+				SetErrorHandler(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..6cb2f10 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -34,7 +34,8 @@
 type docsSingleton struct{}
 
 func primaryBuilderPath(ctx SingletonContext) Path {
-	primaryBuilder, err := filepath.Rel(ctx.Config().BuildDir(), os.Args[0])
+	buildDir := absolutePath(ctx.Config().BuildDir())
+	primaryBuilder, err := filepath.Rel(buildDir, os.Args[0])
 	if err != nil {
 		ctx.Errorf("path to primary builder %q is not in build dir %q",
 			os.Args[0], ctx.Config().BuildDir())
@@ -65,7 +66,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/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 2d1bbb4..b8316a3 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -48,6 +48,22 @@
 	"-": "_dash_",
 }
 
+// Fix steps that should only run in the androidmk tool, i.e. should only be applied to
+// newly-converted Android.bp files.
+var fixSteps = bpfix.FixStepsExtension{
+	Name: "androidmk",
+	Steps: []bpfix.FixStep{
+		{
+			Name: "RewriteRuntimeResourceOverlay",
+			Fix:  bpfix.RewriteRuntimeResourceOverlay,
+		},
+	},
+}
+
+func init() {
+	bpfix.RegisterFixStepExtension(&fixSteps)
+}
+
 func (f *bpFile) insertComment(s string) {
 	f.comments = append(f.comments, &bpparser.CommentGroup{
 		Comments: []*bpparser.Comment{
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 aee3fc4..476ac4a 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -176,12 +176,15 @@
 ExtServices-core(minSdkVersion:current)
 flatbuffer_headers(minSdkVersion:(no version))
 fmtlib(minSdkVersion:29)
+fmtlib_ndk(minSdkVersion:29)
+framework-mediaprovider(minSdkVersion:30)
 framework-permission(minSdkVersion:30)
 framework-permission(minSdkVersion:current)
 framework-permission-s(minSdkVersion:30)
 framework-permission-s-shared(minSdkVersion:30)
 framework-sdkextensions(minSdkVersion:30)
 framework-sdkextensions(minSdkVersion:current)
+framework-statsd(minSdkVersion:30)
 framework-statsd(minSdkVersion:current)
 framework-tethering(minSdkVersion:30)
 framework-tethering(minSdkVersion:current)
@@ -199,6 +202,7 @@
 InProcessTethering(minSdkVersion:current)
 ipmemorystore-aidl-interfaces-java(minSdkVersion:29)
 ipmemorystore-aidl-interfaces-unstable-java(minSdkVersion:29)
+ipmemorystore-aidl-interfaces-V10-java(minSdkVersion:29)
 ipmemorystore-aidl-interfaces-V11-java(minSdkVersion:29)
 jni_headers(minSdkVersion:29)
 jsr305(minSdkVersion:14)
@@ -239,6 +243,7 @@
 libbacktrace_sys.rust_sysroot(minSdkVersion:29)
 libbase(minSdkVersion:29)
 libbase_headers(minSdkVersion:29)
+libbase_ndk(minSdkVersion:29)
 libbinder_headers(minSdkVersion:29)
 libbinder_headers_platform_shared(minSdkVersion:29)
 libbinderthreadstateutils(minSdkVersion:29)
@@ -314,6 +319,8 @@
 libfmq(minSdkVersion:29)
 libfmq-base(minSdkVersion:29)
 libFraunhoferAAC(minSdkVersion:29)
+libfuse(minSdkVersion:30)
+libfuse_jni(minSdkVersion:30)
 libgav1(minSdkVersion:29)
 libgcc(minSdkVersion:(no version))
 libgcc_stripped(minSdkVersion:(no version))
@@ -354,6 +361,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))
@@ -425,11 +433,15 @@
 libstagefright_mpeg2extractor(minSdkVersion:29)
 libstagefright_mpeg2support_nocrypto(minSdkVersion:29)
 libstats_jni(minSdkVersion:(no version))
+libstats_jni(minSdkVersion:30)
 libstatslog_resolv(minSdkVersion:29)
 libstatslog_statsd(minSdkVersion:(no version))
+libstatslog_statsd(minSdkVersion:30)
 libstatspull(minSdkVersion:(no version))
+libstatspull(minSdkVersion:30)
 libstatspush_compat(minSdkVersion:29)
 libstatssocket(minSdkVersion:(no version))
+libstatssocket(minSdkVersion:30)
 libstatssocket_headers(minSdkVersion:29)
 libstd(minSdkVersion:29)
 libsystem_headers(minSdkVersion:apex_inherit)
@@ -463,9 +475,12 @@
 libzstd(minSdkVersion:(no version))
 media_ndk_headers(minSdkVersion:29)
 media_plugin_headers(minSdkVersion:29)
+MediaProvider(minSdkVersion:30)
 mediaswcodec(minSdkVersion:29)
 metrics-constants-protos(minSdkVersion:29)
+modules-annotation-minsdk(minSdkVersion:29)
 modules-utils-build(minSdkVersion:29)
+modules-utils-build_system(minSdkVersion:29)
 modules-utils-os(minSdkVersion:30)
 ndk_crtbegin_so.19(minSdkVersion:(no version))
 ndk_crtbegin_so.21(minSdkVersion:(no version))
@@ -482,7 +497,9 @@
 net-utils-framework-common(minSdkVersion:current)
 netd-client(minSdkVersion:29)
 netd_aidl_interface-java(minSdkVersion:29)
+netd_aidl_interface-lateststable-java(minSdkVersion:29)
 netd_aidl_interface-unstable-java(minSdkVersion:29)
+netd_aidl_interface-V5-java(minSdkVersion:29)
 netd_aidl_interface-V6-java(minSdkVersion:29)
 netd_event_listener_interface-java(minSdkVersion:29)
 netd_event_listener_interface-ndk_platform(minSdkVersion:29)
@@ -492,9 +509,13 @@
 networkstack-aidl-interfaces-unstable-java(minSdkVersion:29)
 networkstack-aidl-interfaces-V10-java(minSdkVersion:29)
 networkstack-client(minSdkVersion:29)
+NetworkStackApi29Shims(minSdkVersion:29)
+NetworkStackApi30Shims(minSdkVersion:29)
 NetworkStackApiStableDependencies(minSdkVersion:29)
 NetworkStackApiStableLib(minSdkVersion:29)
+NetworkStackApiStableShims(minSdkVersion:29)
 networkstackprotos(minSdkVersion:29)
+NetworkStackShimsCommon(minSdkVersion:29)
 neuralnetworks_types(minSdkVersion:30)
 neuralnetworks_utils_hal_1_0(minSdkVersion:30)
 neuralnetworks_utils_hal_1_1(minSdkVersion:30)
@@ -592,6 +613,7 @@
 service-permission(minSdkVersion:30)
 service-permission(minSdkVersion:current)
 service-permission-shared(minSdkVersion:30)
+service-statsd(minSdkVersion:30)
 service-statsd(minSdkVersion:current)
 SettingsLibActionBarShadow(minSdkVersion:21)
 SettingsLibAppPreference(minSdkVersion:21)
@@ -605,13 +627,16 @@
 SettingsLibUtils(minSdkVersion:21)
 stats_proto(minSdkVersion:29)
 statsd(minSdkVersion:(no version))
+statsd(minSdkVersion:30)
 statsd-aidl-ndk_platform(minSdkVersion:(no version))
+statsd-aidl-ndk_platform(minSdkVersion:30)
 statsprotos(minSdkVersion:29)
 tensorflow_headers(minSdkVersion:(no version))
 Tethering(minSdkVersion:30)
 Tethering(minSdkVersion:current)
 TetheringApiCurrentLib(minSdkVersion:30)
 TetheringApiCurrentLib(minSdkVersion:current)
+TetheringGoogle(minSdkVersion:30)
 TetheringGoogle(minSdkVersion:current)
 textclassifier-statsd(minSdkVersion:current)
 TextClassifierNotificationLibNoManifest(minSdkVersion:29)
diff --git a/apex/apex.go b/apex/apex.go
index cfeac72..efd1736 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -54,8 +54,6 @@
 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 +121,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.
@@ -208,6 +206,9 @@
 
 	// List of native tests that are embedded inside this APEX.
 	Tests []string
+
+	// List of filesystem images that are embedded inside this APEX bundle.
+	Filesystems []string
 }
 
 type apexMultilibProperties struct {
@@ -580,6 +581,7 @@
 	ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
 	ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
 	ctx.AddFarVariationDependencies(rustLibVariations, sharedLibTag, nativeModules.Rust_dyn_libs...)
+	ctx.AddFarVariationDependencies(target.Variations(), fsTag, nativeModules.Filesystems...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -1230,7 +1232,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
@@ -2242,8 +2244,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
 	})
@@ -2843,14 +2847,6 @@
 	//
 	// Module separator
 	//
-	m["com.android.sdkext"] = []string{
-		"fmtlib_ndk",
-		"libbase_ndk",
-		"libprotobuf-cpp-lite-ndk",
-	}
-	//
-	// Module separator
-	//
 	m["com.android.os.statsd"] = []string{
 		"libstatssocket",
 	}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 181946b..e3b7f95 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -19,6 +19,7 @@
 	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 	"reflect"
 	"regexp"
 	"sort"
@@ -143,17 +144,18 @@
 	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,
+		"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 +193,7 @@
 		"AppSet.apks":                                nil,
 		"foo.rs":                                     nil,
 		"libfoo.jar":                                 nil,
+		"libbar.jar":                                 nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -237,7 +240,6 @@
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterComponentsMutator)
-	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
 
 	android.RegisterPrebuiltMutators(ctx)
 
@@ -246,6 +248,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)
@@ -354,7 +360,7 @@
 
 // Minimal test
 func TestBasicApex(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
 			manifest: ":myapex.manifest",
@@ -375,6 +381,7 @@
 				"myjar",
 				"myjar_dex",
 			],
+			updatable: false,
 		}
 
 		apex {
@@ -475,6 +482,7 @@
 			binaries: ["foo"],
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		cc_library_shared {
@@ -562,7 +570,7 @@
 
 	// Make sure that Android.mk is created
 	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", ab)
+	data := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
 
@@ -668,6 +676,7 @@
 			apps: ["AppFoo"],
 			rros: ["rro"],
 			bpfs: ["bpf"],
+			updatable: false,
 		}
 
 		prebuilt_etc {
@@ -736,6 +745,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 
 		apex_key {
@@ -759,6 +769,7 @@
 			key: "myapex.key",
 			payload_type: "zip",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -808,6 +819,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib3"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -970,8 +982,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")
 
@@ -1055,11 +1067,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"]
@@ -1074,6 +1086,7 @@
 			name: "myapex2",
 			key: "myapex2.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1169,6 +1182,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1231,6 +1245,7 @@
 			name: "com.android.runtime",
 			key: "com.android.runtime.key",
 			native_shared_libs: ["libc"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1294,6 +1309,7 @@
 			name: "com.android.runtime",
 			key: "com.android.runtime.key",
 			native_shared_libs: ["libc"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1357,18 +1373,18 @@
 		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 {
@@ -1379,6 +1395,7 @@
 				key: "myapex.key",
 				use_vendor: true,
 				native_shared_libs: ["mylib"],
+				updatable: false,
 				`+tc.minSdkVersion+`
 			}
 
@@ -1444,6 +1461,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1530,8 +1548,8 @@
 }
 
 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, `
@@ -1605,9 +1623,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")
@@ -1670,6 +1688,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["libx"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1715,6 +1734,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["libx"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -1825,41 +1845,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 +1961,7 @@
 					name: "myapex",
 					java_libs: ["myjar"],
 					key: "myapex.key",
+					updatable: false,
 				}
 				apex_key {
 					name: "myapex.key",
@@ -2039,7 +2025,7 @@
 		},
 		{
 			name:          "Updatable apex with non-stable transitive dep",
-			expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against non-public Android API.",
+			expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against private API.",
 			bp: `
 				apex {
 					name: "myapex",
@@ -2171,7 +2157,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 +2191,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")
 }
 
@@ -2274,7 +2260,7 @@
 	// 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) {
@@ -2286,6 +2272,7 @@
 			binaries: ["mybin"],
 			prebuilts: ["myetc"],
 			compile_multilib: "both",
+			updatable: false,
 		}
 
 		apex_key {
@@ -2352,6 +2339,7 @@
 			},
 			compile_multilib: "both",
 			native_bridge_supported: true,
+			updatable: false,
 		}
 
 		apex_key {
@@ -2404,6 +2392,7 @@
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
 			use_vendor: true,
+			updatable: false,
 		}
 
 		apex_key {
@@ -2472,6 +2461,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			use_vendor: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2490,6 +2480,7 @@
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
 			use_vendor: true,
+			updatable: false,
 		}
 
 		apex_key {
@@ -2508,12 +2499,13 @@
 }
 
 func TestVendorApex(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			binaries: ["mybin"],
 			vendor: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2539,13 +2531,14 @@
 	})
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	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"])
@@ -2560,6 +2553,7 @@
 			binaries: ["mybin"],
 			vendor: true,
 			use_vndk_as_stable: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -2621,6 +2615,7 @@
 					name: "myapex",
 					key: "myapex.key",
 					prebuilts: ["myfirmware"],
+					updatable: false,
 					`+tc.additionalProp+`
 				}
 				apex_key {
@@ -2643,12 +2638,13 @@
 }
 
 func TestAndroidMk_UseVendorRequired(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			use_vendor: true,
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -2667,7 +2663,7 @@
 	})
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
@@ -2677,12 +2673,13 @@
 }
 
 func TestAndroidMk_VendorApexRequired(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			vendor: true,
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -2698,7 +2695,7 @@
 	`)
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
@@ -2708,12 +2705,13 @@
 }
 
 func TestAndroidMkWritesCommonProperties(t *testing.T) {
-	ctx, config := 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",
@@ -2726,7 +2724,7 @@
 	`)
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
@@ -2742,6 +2740,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -2788,6 +2787,7 @@
 			certificate: ":myapex.certificate",
 			native_shared_libs: ["mylib"],
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		cc_library {
@@ -2842,6 +2842,7 @@
 			apex {
 				name: "myapex",
 				key: "myapex.key",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2860,6 +2861,7 @@
 				name: "myapex_keytest",
 				key: "myapex.key",
 				file_contexts: ":myapex-file_contexts",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2882,6 +2884,7 @@
 				name: "myapex",
 				key: "myapex.key",
 				certificate: ":myapex.certificate",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2905,6 +2908,7 @@
 				key: "myapex.key",
 				file_contexts: ":myapex-file_contexts",
 				certificate: ":myapex.certificate",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2927,6 +2931,7 @@
 				name: "myapex",
 				key: "myapex.key",
 				certificate: "testkey",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2946,6 +2951,7 @@
 				key: "myapex.key",
 				file_contexts: ":myapex-file_contexts",
 				certificate: "testkey",
+				updatable: false,
 			}
 			apex_key {
 				name: "myapex.key",
@@ -2970,6 +2976,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib2"],
+			updatable: false,
 		}
 
 		apex {
@@ -3097,6 +3104,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3237,12 +3245,13 @@
 func TestVndkApexCurrent(t *testing.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 +3266,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 
 		cc_library {
@@ -3271,11 +3280,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",
@@ -3293,12 +3302,13 @@
 func TestVndkApexWithPrebuilt(t *testing.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 +3323,7 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex" ],
+			apex_available: [ "com.android.vndk.current" ],
 		}
 
 		cc_prebuilt_library_shared {
@@ -3332,15 +3342,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",
@@ -3377,10 +3386,11 @@
 func TestVndkApexVersion(t *testing.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 +3416,7 @@
 					srcs: ["libvndk27_arm64.so"],
 				},
 			},
-			apex_available: [ "myapex_v27" ],
+			apex_available: [ "com.android.vndk.v27" ],
 		}
 
 		vndk_prebuilt_shared {
@@ -3435,73 +3445,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, `
 		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 +3481,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, `
 		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 +3512,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 +3527,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",
 		}
@@ -3594,10 +3560,11 @@
 func TestVndkApexWithBinder32(t *testing.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 +3604,7 @@
 					srcs: ["libvndk27binder32.so"],
 				}
 			},
-			apex_available: [ "myapex_v27" ],
+			apex_available: [ "com.android.vndk.v27" ],
 		}
 		`+vndkLibrariesTxtFiles("27"),
 		withFiles(map[string][]byte{
@@ -3653,7 +3620,7 @@
 		}),
 	)
 
-	ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
+	ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{
 		"lib/libvndk27binder32.so",
 		"etc/*",
 	})
@@ -3662,13 +3629,14 @@
 func TestVndkApexShouldNotProvideNativeLibs(t *testing.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,7 +3657,7 @@
 		"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)
 }
@@ -3702,6 +3670,7 @@
 			native_shared_libs: ["lib_nodep"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex {
@@ -3710,6 +3679,7 @@
 			native_shared_libs: ["lib_dep"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex {
@@ -3718,6 +3688,7 @@
 			native_shared_libs: ["libfoo"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex {
@@ -3726,6 +3697,7 @@
 			native_shared_libs: ["lib_dep", "libfoo"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -3799,12 +3771,13 @@
 }
 
 func TestApexName(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apex_name: "com.android.myapex",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3832,7 +3805,7 @@
 	ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
@@ -3848,6 +3821,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib_common"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3900,6 +3874,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib_common_test"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -3947,6 +3922,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 			multilib: {
 				first: {
 					native_shared_libs: ["mylib_common"],
@@ -4037,6 +4013,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 			arch: {
 				arm64: {
 					native_shared_libs: ["mylib.arm64"],
@@ -4096,6 +4073,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			binaries: ["myscript"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4135,6 +4113,7 @@
 				apex {
 					name: "myapex",
 					key: "myapex.key",
+					updatable: false,
 					`+tc.propName+`
 				}
 
@@ -4167,6 +4146,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4186,6 +4166,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			file_contexts: "my_own_file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4205,6 +4186,7 @@
 			key: "myapex.key",
 			product_specific: true,
 			file_contexts: "product_specific_file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4220,6 +4202,7 @@
 			key: "myapex.key",
 			product_specific: true,
 			file_contexts: "product_specific_file_contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4242,6 +4225,7 @@
 			key: "myapex.key",
 			product_specific: true,
 			file_contexts: ":my-file-contexts",
+			updatable: false,
 		}
 
 		apex_key {
@@ -4336,7 +4320,7 @@
 }
 
 func TestPrebuiltOverrides(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		prebuilt_apex {
 			name: "myapex.prebuilt",
 			src: "myapex-arm.apex",
@@ -4349,7 +4333,7 @@
 	p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
 
 	expected := []string{"myapex"}
-	actual := android.AndroidMkEntriesForTest(t, config, "", p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected)
 	}
@@ -4366,14 +4350,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 +4375,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 +4426,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4429,13 +4437,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 +4474,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4462,26 +4486,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 +4539,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 +4557,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4521,13 +4565,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 +4598,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4558,6 +4612,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 +4649,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4595,13 +4664,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 +4698,8 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			java_libs: ["libfoo"],
+			java_libs: ["libfoo", "libbar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4630,7 +4718,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4644,13 +4732,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 +4785,7 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo"],
+			exported_java_libs: ["libfoo", "libbar"],
 		}
 
 		java_import {
@@ -4695,23 +4800,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, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex_test {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 			tests: [
 				"mytest",
 				"mytests",
@@ -4796,7 +4920,7 @@
 
 	// Ensure the module is correctly translated.
 	bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", bundle)
+	data := android.AndroidMkDataForTest(t, ctx, bundle)
 	name := bundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
@@ -4811,7 +4935,7 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
 
 	flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-	data = android.AndroidMkDataForTest(t, config, "", flatBundle)
+	data = android.AndroidMkDataForTest(t, ctx, flatBundle)
 	data.Custom(&builder, name, prefix, "", data)
 	flatAndroidMk := builder.String()
 	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n")
@@ -4819,10 +4943,11 @@
 }
 
 func TestInstallExtraFlattenedApexes(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -4834,7 +4959,7 @@
 	})
 	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
 	ensureListContains(t, ab.requiredDeps, "myapex.flattened")
-	mk := android.AndroidMkDataForTest(t, config, "", ab)
+	mk := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
 	androidMk := builder.String()
@@ -4893,6 +5018,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["myjavaimport"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -4924,6 +5050,7 @@
 				"AppFoo",
 				"AppFooPriv",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5002,6 +5129,7 @@
 				"AppFooPrebuilt",
 				"AppFooPrivPrebuilt",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5049,6 +5177,7 @@
 			apps: [
 				"AppFoo",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5078,7 +5207,8 @@
 	}))
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
-		"app/AppFoo/AppFooPrebuilt.apk",
+		// TODO(b/181974714) - this is wrong it should be "app/AppFoo/AppFooPrebuilt.apk"
+		"app/AppFoo/AppFoo.apk",
 	})
 }
 
@@ -5090,6 +5220,7 @@
 			apps: [
 				"TesterHelpAppFoo",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5120,6 +5251,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5132,6 +5264,7 @@
 		name: "otherapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	cc_defaults {
@@ -5154,6 +5287,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5166,6 +5300,7 @@
 		name: "otherapex",
 		key: "otherapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5184,7 +5319,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 +5330,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5232,6 +5368,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5252,6 +5389,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo", "libbar"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5291,6 +5429,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libbar", "libbaz"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5353,6 +5492,7 @@
 		name: "myapex",
 		key: "myapex.key",
 		native_shared_libs: ["libfoo"],
+		updatable: false,
 	}
 
 	apex_key {
@@ -5382,12 +5522,13 @@
 }
 
 func TestOverrideApex(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
 			overrides: ["oldapex"],
+			updatable: false,
 		}
 
 		override_apex {
@@ -5450,7 +5591,7 @@
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
 
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	var builder strings.Builder
 	data.Custom(&builder, name, "TARGET_", "", data)
 	androidMk := builder.String()
@@ -5530,6 +5671,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5567,6 +5709,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo", "bar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5619,6 +5762,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5681,6 +5825,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo", "bar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5758,6 +5903,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["foo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5785,6 +5931,7 @@
 			key: "myapex.key",
 			prebuilts: ["myjar-platform-compat-config"],
 			java_libs: ["myjar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5818,6 +5965,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			java_libs: ["myjar"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5838,11 +5986,12 @@
 }
 
 func TestCarryRequiredModuleNames(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -5864,7 +6013,7 @@
 	`)
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
@@ -5882,6 +6031,7 @@
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
 			java_libs: ["myjar"],
+			updatable: false,
 		}
 
 		apex {
@@ -6005,11 +6155,12 @@
 }
 
 func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6043,7 +6194,7 @@
 	`)
 
 	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
@@ -6061,6 +6212,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			jni_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6101,6 +6253,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -6123,6 +6276,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["AppFoo"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6153,6 +6307,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["AppSet"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6259,6 +6414,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 +6475,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 +6486,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 +6538,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 +6767,7 @@
 					name: "myapex",
 					key: "myapex.key",
 					java_libs: ["bcp_lib1", "nonbcp_lib2"],
+					updatable: false,
 				}`,
 			bootJars: []string{"bcp_lib1"},
 			modulesPackages: map[string][]string{
@@ -6624,6 +6800,7 @@
 					name: "myapex",
 					key: "myapex.key",
 					java_libs: ["bcp_lib1", "bcp_lib2"],
+					updatable: false,
 				}
 			`,
 			bootJars: []string{"bcp_lib1", "bcp_lib2"},
@@ -6648,6 +6825,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "myprivlib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6728,7 +6906,7 @@
 }
 
 func TestApexSet(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex_set {
 			name: "myapex",
 			set: "myapex.apks",
@@ -6760,7 +6938,7 @@
 
 	a := m.Module().(*ApexSet)
 	expectedOverrides := []string{"foo"}
-	actualOverrides := android.AndroidMkEntriesForTest(t, config, "", a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
 	if !reflect.DeepEqual(actualOverrides, expectedOverrides) {
 		t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES - expected %q vs actual %q", expectedOverrides, actualOverrides)
 	}
@@ -6772,6 +6950,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6807,6 +6986,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
+			updatable: false,
 		}
 
 		apex_key {
@@ -6849,6 +7029,7 @@
 			key: "myapex.key",
 			apps: ["app"],
 			allowed_files: "allowed.txt",
+			updatable: false,
 		}
 
 		apex_key {
@@ -6903,6 +7084,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -6933,11 +7115,12 @@
 }
 
 func TestCompressedApex(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			compressible: true,
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -6959,7 +7142,7 @@
 	ensureContains(t, ab.outputFile.String(), "myapex.capex")
 
 	// Verify android.mk rules
-	data := android.AndroidMkDataForTest(t, config, "", ab)
+	data := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
@@ -6967,11 +7150,12 @@
 }
 
 func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
-	ctx, config := testApex(t, `
+	ctx, _ := testApex(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -7007,7 +7191,7 @@
 	`)
 
 	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", ab)
+	data := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
@@ -7023,6 +7207,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 
 		apex_key {
@@ -7072,6 +7257,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib"],
+			updatable: false,
 		}
 		apex_key {
 			name: "myapex.key",
@@ -7090,6 +7276,7 @@
 			enabled: %s,
 			key: "myapex.key",
 			native_shared_libs: ["stublib"],
+			updatable: false,
 		}
 	`
 
@@ -7159,7 +7346,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			for _, otherApexEnabled := range test.otherApexEnabled {
 				t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
-					ctx, config := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
+					ctx, _ := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
 
 					type modAndMkEntries struct {
 						mod       *cc.Module
@@ -7177,7 +7364,7 @@
 							if !mod.Enabled() || mod.IsHideFromMake() {
 								continue
 							}
-							for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
+							for _, ent := range android.AndroidMkEntriesForTest(t, ctx, mod) {
 								if ent.Disabled {
 									continue
 								}
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 27a1562..ff779e1 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -48,6 +48,7 @@
 				"baz",
 				"quuz",
 			],
+			updatable: false,
 		}
 
 		apex_key {
@@ -187,6 +188,7 @@
 			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/prebuilt.go b/apex/prebuilt.go
index 041afb3..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
@@ -414,7 +411,7 @@
 		OutputFile: android.OptionalPathForPath(p.inputApex),
 		Include:    "$(BUILD_PREBUILT)",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
 				entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
@@ -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)
 	}
 
@@ -609,7 +606,7 @@
 		Include:       "$(BUILD_PREBUILT)",
 		Host_required: a.hostRequired,
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
 				entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
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..5e9a11f 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -11,12 +11,13 @@
 func TestVndkApexForVndkLite(t *testing.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,7 +96,7 @@
 				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) {
@@ -106,7 +108,7 @@
 		}
 
 		// 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")
 	})
 
@@ -116,7 +118,7 @@
 			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")
 	})
 
@@ -126,10 +128,10 @@
 			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/bazel/properties.go b/bazel/properties.go
index 8055306..a5ffa55 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -14,10 +14,7 @@
 
 package bazel
 
-import (
-	"fmt"
-	"strings"
-)
+import "fmt"
 
 type bazelModuleProperties struct {
 	// The label of the Bazel target replacing this Soong module.
@@ -37,32 +34,15 @@
 // BazelTargetModuleProperties contain properties and metadata used for
 // Blueprint to BUILD file conversion.
 type BazelTargetModuleProperties struct {
-	Name *string
-
 	// The Bazel rule class for this target.
-	Rule_class string
+	Rule_class string `blueprint:"mutated"`
 
 	// The target label for the bzl file containing the definition of the rule class.
-	Bzl_load_location string
+	Bzl_load_location string `blueprint:"mutated"`
 }
 
 const BazelTargetModuleNamePrefix = "__bp2build__"
 
-func NewBazelTargetModuleProperties(name string, ruleClass string, bzlLoadLocation string) BazelTargetModuleProperties {
-	if strings.HasPrefix(name, BazelTargetModuleNamePrefix) {
-		panic(fmt.Errorf(
-			"The %s name prefix is added automatically, do not set it manually: %s",
-			BazelTargetModuleNamePrefix,
-			name))
-	}
-	name = BazelTargetModuleNamePrefix + name
-	return BazelTargetModuleProperties{
-		Name:              &name,
-		Rule_class:        ruleClass,
-		Bzl_load_location: bzlLoadLocation,
-	}
-}
-
 // Label is used to represent a Bazel compatible Label. Also stores the original bp text to support
 // string replacement.
 type Label struct {
@@ -85,3 +65,72 @@
 		ll.Excludes = append(other.Excludes, other.Excludes...)
 	}
 }
+
+// StringListAttribute corresponds to the string_list Bazel attribute type with
+// support for additional metadata, like configurations.
+type StringListAttribute struct {
+	// The base value of the string list attribute.
+	Value []string
+
+	// Optional additive set of list values to the base value.
+	ArchValues stringListArchValues
+}
+
+// Arch-specific string_list typed Bazel attribute values. This should correspond
+// to the types of architectures supported for compilation in arch.go.
+type stringListArchValues struct {
+	X86     []string
+	X86_64  []string
+	Arm     []string
+	Arm64   []string
+	Default []string
+	// TODO(b/181299724): this is currently missing the "common" arch, which
+	// doesn't have an equivalent platform() definition yet.
+}
+
+// HasArchSpecificValues returns true if the attribute contains
+// architecture-specific string_list values.
+func (attrs *StringListAttribute) HasArchSpecificValues() bool {
+	for _, arch := range []string{"x86", "x86_64", "arm", "arm64", "default"} {
+		if len(attrs.GetValueForArch(arch)) > 0 {
+			return true
+		}
+	}
+	return false
+}
+
+// GetValueForArch returns the string_list attribute value for an architecture.
+func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
+	switch arch {
+	case "x86":
+		return attrs.ArchValues.X86
+	case "x86_64":
+		return attrs.ArchValues.X86_64
+	case "arm":
+		return attrs.ArchValues.Arm
+	case "arm64":
+		return attrs.ArchValues.Arm64
+	case "default":
+		return attrs.ArchValues.Default
+	default:
+		panic(fmt.Errorf("Unknown arch: %s", arch))
+	}
+}
+
+// SetValueForArch sets the string_list attribute value for an architecture.
+func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
+	switch arch {
+	case "x86":
+		attrs.ArchValues.X86 = value
+	case "x86_64":
+		attrs.ArchValues.X86_64 = value
+	case "arm":
+		attrs.ArchValues.Arm = value
+	case "arm64":
+		attrs.ArchValues.Arm64 = value
+	case "default":
+		attrs.ArchValues.Default = value
+	default:
+		panic(fmt.Errorf("Unknown arch: %s", arch))
+	}
+}
diff --git a/bloaty/Android.bp b/bloaty/Android.bp
new file mode 100644
index 0000000..b1f1e39
--- /dev/null
+++ b/bloaty/Android.bp
@@ -0,0 +1,40 @@
+bootstrap_go_package {
+    name: "soong-bloaty",
+    pkgPath: "android/soong/bloaty",
+    deps: [
+        "blueprint",
+        "soong-android",
+    ],
+    srcs: [
+        "bloaty.go",
+    ],
+    pluginFor: ["soong_build"],
+}
+
+python_test_host {
+    name: "bloaty_merger_test",
+    srcs: [
+        "bloaty_merger_test.py",
+        "bloaty_merger.py",
+        "file_sections.proto",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+    libs: [
+        "pyfakefs",
+        "ninja_rsp",
+    ],
+}
+
+python_binary_host {
+    name: "bloaty_merger",
+    srcs: [
+        "bloaty_merger.py",
+        "file_sections.proto",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+    libs: ["ninja_rsp"],
+}
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
new file mode 100644
index 0000000..21bf4ac
--- /dev/null
+++ b/bloaty/bloaty.go
@@ -0,0 +1,97 @@
+// 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 bloaty implements a singleton that measures binary (e.g. ELF
+// executable, shared library or Rust rlib) section sizes at build time.
+package bloaty
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+const bloatyDescriptorExt = "bloaty.csv"
+const protoFilename = "binary_sizes.pb"
+
+var (
+	fileSizeMeasurerKey blueprint.ProviderKey
+	pctx                = android.NewPackageContext("android/soong/bloaty")
+
+	// bloaty is used to measure a binary section sizes.
+	bloaty = pctx.AndroidStaticRule("bloaty",
+		blueprint.RuleParams{
+			Command:     "${bloaty} -n 0 --csv ${in} > ${out}",
+			CommandDeps: []string{"${bloaty}"},
+		})
+
+	// The bloaty merger script is used to combine the outputs from bloaty
+	// into a single protobuf.
+	bloatyMerger = pctx.AndroidStaticRule("bloatyMerger",
+		blueprint.RuleParams{
+			Command:        "${bloatyMerger} ${out}.lst ${out}",
+			CommandDeps:    []string{"${bloatyMerger}"},
+			Rspfile:        "${out}.lst",
+			RspfileContent: "${in}",
+		})
+)
+
+func init() {
+	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
+	pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
+	pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
+	android.RegisterSingletonType("file_metrics", fileSizesSingleton)
+	fileSizeMeasurerKey = blueprint.NewProvider(android.ModuleOutPath{})
+}
+
+// MeasureSizeForPath should be called by binary producers (e.g. in builder.go).
+func MeasureSizeForPath(ctx android.ModuleContext, filePath android.WritablePath) {
+	ctx.SetProvider(fileSizeMeasurerKey, filePath)
+}
+
+type sizesSingleton struct{}
+
+func fileSizesSingleton() android.Singleton {
+	return &sizesSingleton{}
+}
+
+func (singleton *sizesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var deps android.Paths
+	// Visit all modules. If the size provider give us a binary path to measure,
+	// create the rule to measure it.
+	ctx.VisitAllModules(func(m android.Module) {
+		if !ctx.ModuleHasProvider(m, fileSizeMeasurerKey) {
+			return
+		}
+		filePath := ctx.ModuleProvider(m, fileSizeMeasurerKey).(android.ModuleOutPath)
+		sizeFile := filePath.ReplaceExtension(ctx, bloatyDescriptorExt)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        bloaty,
+			Description: "bloaty " + filePath.Rel(),
+			Input:       filePath,
+			Output:      sizeFile,
+		})
+		deps = append(deps, sizeFile)
+	})
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   bloatyMerger,
+		Inputs: android.SortedUniquePaths(deps),
+		Output: android.PathForOutput(ctx, protoFilename),
+	})
+}
+
+func (singleton *sizesSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoalWithFilename("checkbuild", android.PathForOutput(ctx, protoFilename), protoFilename)
+}
diff --git a/bloaty/bloaty_merger.py b/bloaty/bloaty_merger.py
new file mode 100644
index 0000000..c873fb8
--- /dev/null
+++ b/bloaty/bloaty_merger.py
@@ -0,0 +1,79 @@
+# 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.
+"""Bloaty CSV Merger
+
+Merges a list of .csv files from Bloaty into a protobuf.  It takes the list as
+a first argument and the output as second. For instance:
+
+    $ bloaty_merger binary_sizes.lst binary_sizes.pb
+
+"""
+
+import argparse
+import csv
+
+import ninja_rsp
+
+import file_sections_pb2
+
+BLOATY_EXTENSION = ".bloaty.csv"
+
+def parse_csv(path):
+  """Parses a Bloaty-generated CSV file into a protobuf.
+
+  Args:
+    path: The filepath to the CSV file, relative to $ANDROID_TOP.
+
+  Returns:
+    A file_sections_pb2.File if the file was found; None otherwise.
+  """
+  file_proto = None
+  with open(path, newline='') as csv_file:
+    file_proto = file_sections_pb2.File()
+    if path.endswith(BLOATY_EXTENSION):
+      file_proto.path = path[:-len(BLOATY_EXTENSION)]
+    section_reader = csv.DictReader(csv_file)
+    for row in section_reader:
+      section = file_proto.sections.add()
+      section.name = row["sections"]
+      section.vm_size = int(row["vmsize"])
+      section.file_size = int(row["filesize"])
+  return file_proto
+
+def create_file_size_metrics(input_list, output_proto):
+  """Creates a FileSizeMetrics proto from a list of CSV files.
+
+  Args:
+    input_list: The path to the file which contains the list of CSV files. Each
+        filepath is separated by a space.
+    output_proto: The path for the output protobuf.
+  """
+  metrics = file_sections_pb2.FileSizeMetrics()
+  reader = ninja_rsp.NinjaRspFileReader(input_list)
+  for csv_path in reader:
+    file_proto = parse_csv(csv_path)
+    if file_proto:
+      metrics.files.append(file_proto)
+  with open(output_proto, "wb") as output:
+    output.write(metrics.SerializeToString())
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("input_list_file", help="List of bloaty csv files.")
+  parser.add_argument("output_proto", help="Output proto.")
+  args = parser.parse_args()
+  create_file_size_metrics(args.input_list_file, args.output_proto)
+
+if __name__ == '__main__':
+  main()
diff --git a/bloaty/bloaty_merger_test.py b/bloaty/bloaty_merger_test.py
new file mode 100644
index 0000000..0e3641d
--- /dev/null
+++ b/bloaty/bloaty_merger_test.py
@@ -0,0 +1,65 @@
+# 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.
+import unittest
+
+from pyfakefs import fake_filesystem_unittest
+
+import bloaty_merger
+import file_sections_pb2
+
+
+class BloatyMergerTestCase(fake_filesystem_unittest.TestCase):
+  def setUp(self):
+    self.setUpPyfakefs()
+
+  def test_parse_csv(self):
+    csv_content = "sections,vmsize,filesize\nsection1,2,3\n"
+    self.fs.create_file("file1.bloaty.csv", contents=csv_content)
+    pb = bloaty_merger.parse_csv("file1.bloaty.csv")
+    self.assertEqual(pb.path, "file1")
+    self.assertEqual(len(pb.sections), 1)
+    s = pb.sections[0]
+    self.assertEqual(s.name, "section1")
+    self.assertEqual(s.vm_size, 2)
+    self.assertEqual(s.file_size, 3)
+
+  def test_missing_file(self):
+    with self.assertRaises(FileNotFoundError):
+      bloaty_merger.parse_csv("missing.bloaty.csv")
+
+  def test_malformed_csv(self):
+    csv_content = "header1,heaVder2,header3\n4,5,6\n"
+    self.fs.create_file("file1.bloaty.csv", contents=csv_content)
+    with self.assertRaises(KeyError):
+      bloaty_merger.parse_csv("file1.bloaty.csv")
+
+  def test_create_file_metrics(self):
+    file_list = "file1.bloaty.csv file2.bloaty.csv"
+    file1_content = "sections,vmsize,filesize\nsection1,2,3\nsection2,7,8"
+    file2_content = "sections,vmsize,filesize\nsection1,4,5\n"
+
+    self.fs.create_file("files.lst", contents=file_list)
+    self.fs.create_file("file1.bloaty.csv", contents=file1_content)
+    self.fs.create_file("file2.bloaty.csv", contents=file2_content)
+
+    bloaty_merger.create_file_size_metrics("files.lst", "output.pb")
+
+    metrics = file_sections_pb2.FileSizeMetrics()
+    with open("output.pb", "rb") as output:
+      metrics.ParseFromString(output.read())
+
+
+if __name__ == '__main__':
+  suite = unittest.TestLoader().loadTestsFromTestCase(BloatyMergerTestCase)
+  unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/bloaty/file_sections.proto b/bloaty/file_sections.proto
new file mode 100644
index 0000000..34a32db
--- /dev/null
+++ b/bloaty/file_sections.proto
@@ -0,0 +1,39 @@
+// 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.
+syntax = "proto2";
+
+package file_sections;
+
+message SectionDescriptior {
+  // Name of the section (e.g. .rodata)
+  optional string name = 1;
+
+  // Size of that section as part of the file.
+  optional uint64 file_size = 2;
+
+  // Size of that section when loaded in memory.
+  optional uint64 vm_size = 3;
+}
+
+message File {
+  // Relative path from $OUT_DIR.
+  optional string path = 1;
+
+  // File sections.
+  repeated SectionDescriptior sections = 2;
+}
+
+message FileSizeMetrics {
+  repeated File files = 1;
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 6570b16..8deb5a2 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -10,17 +10,22 @@
         "bp2build.go",
         "build_conversion.go",
         "bzl_conversion.go",
+        "configurability.go",
         "conversion.go",
+        "metrics.go",
     ],
     deps: [
         "soong-android",
         "soong-bazel",
+        "soong-cc",
         "soong-genrule",
         "soong-sh",
     ],
     testSrcs: [
         "build_conversion_test.go",
         "bzl_conversion_test.go",
+        "cc_library_headers_conversion_test.go",
+        "cc_object_conversion_test.go",
         "conversion_test.go",
         "sh_conversion_test.go",
         "testing.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index b89d0a0..7169d7e 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -22,13 +22,13 @@
 
 // The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
 // Android.bp files that are capable of being built with Bazel.
-func Codegen(ctx CodegenContext) {
+func Codegen(ctx CodegenContext) CodegenMetrics {
 	outputDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(outputDir)
 
 	ruleShims := CreateRuleShims(android.ModuleTypeFactories())
 
-	buildToTargets := GenerateBazelTargets(ctx.Context(), ctx.mode)
+	buildToTargets, metrics := GenerateBazelTargets(ctx)
 
 	filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, ctx.mode)
 	for _, f := range filesToWrite {
@@ -36,6 +36,8 @@
 			fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
 		}
 	}
+
+	return metrics
 }
 
 func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 7ffcfa4..7fa4996 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -19,7 +19,6 @@
 	"android/soong/bazel"
 	"fmt"
 	"reflect"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -105,6 +104,10 @@
 	mode    CodegenMode
 }
 
+func (c *CodegenContext) Mode() CodegenMode {
+	return c.mode
+}
+
 // CodegenMode is an enum to differentiate code-generation modes.
 type CodegenMode int
 
@@ -160,65 +163,58 @@
 	return attributes
 }
 
-func GenerateBazelTargets(ctx bpToBuildContext, codegenMode CodegenMode) map[string]BazelTargets {
+func GenerateBazelTargets(ctx CodegenContext) (map[string]BazelTargets, CodegenMetrics) {
 	buildFileToTargets := make(map[string]BazelTargets)
-	ctx.VisitAllModules(func(m blueprint.Module) {
-		dir := ctx.ModuleDir(m)
+
+	// Simple metrics tracking for bp2build
+	totalModuleCount := 0
+	ruleClassCount := make(map[string]int)
+
+	bpCtx := ctx.Context()
+	bpCtx.VisitAllModules(func(m blueprint.Module) {
+		dir := bpCtx.ModuleDir(m)
 		var t BazelTarget
 
-		switch codegenMode {
+		switch ctx.Mode() {
 		case Bp2Build:
-			if _, ok := m.(android.BazelTargetModule); !ok {
+			if b, ok := m.(android.BazelTargetModule); !ok {
+				// Only include regular Soong modules (non-BazelTargetModules) into the total count.
+				totalModuleCount += 1
 				return
+			} else {
+				t = generateBazelTarget(bpCtx, m, b)
+				ruleClassCount[t.ruleClass] += 1
 			}
-			t = generateBazelTarget(ctx, m)
 		case QueryView:
 			// Blocklist certain module types from being generated.
-			if canonicalizeModuleType(ctx.ModuleType(m)) == "package" {
+			if canonicalizeModuleType(bpCtx.ModuleType(m)) == "package" {
 				// package module name contain slashes, and thus cannot
 				// be mapped cleanly to a bazel label.
 				return
 			}
-			t = generateSoongModuleTarget(ctx, m)
+			t = generateSoongModuleTarget(bpCtx, m)
 		default:
-			panic(fmt.Errorf("Unknown code-generation mode: %s", codegenMode))
+			panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
 		}
 
 		buildFileToTargets[dir] = append(buildFileToTargets[dir], t)
 	})
-	return buildFileToTargets
+
+	metrics := CodegenMetrics{
+		TotalModuleCount: totalModuleCount,
+		RuleClassCount:   ruleClassCount,
+	}
+
+	return buildFileToTargets, metrics
 }
 
-// Helper method to trim quotes around strings.
-func trimQuotes(s string) string {
-	if s == "" {
-		// strconv.Unquote would error out on empty strings, but this method
-		// allows them, so return the empty string directly.
-		return ""
-	}
-	ret, err := strconv.Unquote(s)
-	if err != nil {
-		// Panic the error immediately.
-		panic(fmt.Errorf("Trying to unquote '%s', but got error: %s", s, err))
-	}
-	return ret
-}
+func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, b android.BazelTargetModule) BazelTarget {
+	ruleClass := b.RuleClass()
+	bzlLoadLocation := b.BzlLoadLocation()
 
-func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
 	// extract the bazel attributes from the module.
 	props := getBuildProperties(ctx, m)
 
-	// extract the rule class name from the attributes. Since the string value
-	// will be string-quoted, remove the quotes here.
-	ruleClass := trimQuotes(props.Attrs["rule_class"])
-	// Delete it from being generated in the BUILD file.
-	delete(props.Attrs, "rule_class")
-
-	// extract the bzl_load_location, and also remove the quotes around it here.
-	bzlLoadLocation := trimQuotes(props.Attrs["bzl_load_location"])
-	// Delete it from being generated in the BUILD file.
-	delete(props.Attrs, "bzl_load_location")
-
 	delete(props.Attrs, "bp2build_available")
 
 	// Return the Bazel target with rule class and attributes, ready to be
@@ -358,11 +354,42 @@
 		ret += makeIndent(indent)
 		ret += "]"
 	case reflect.Struct:
+		// Special cases where the bp2build sends additional information to the codegenerator
+		// by wrapping the attributes in a custom struct type.
 		if labels, ok := propertyValue.Interface().(bazel.LabelList); ok {
 			// TODO(b/165114590): convert glob syntax
 			return prettyPrint(reflect.ValueOf(labels.Includes), indent)
 		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
 			return fmt.Sprintf("%q", label.Label), nil
+		} else if stringList, ok := propertyValue.Interface().(bazel.StringListAttribute); ok {
+			// A Bazel string_list attribute that may contain a select statement.
+			ret, err := prettyPrint(reflect.ValueOf(stringList.Value), indent)
+			if err != nil {
+				return ret, err
+			}
+
+			if !stringList.HasArchSpecificValues() {
+				// Select statement not needed.
+				return ret, nil
+			}
+
+			ret += " + " + "select({\n"
+			for _, arch := range android.ArchTypeList() {
+				value := stringList.GetValueForArch(arch.Name)
+				if len(value) > 0 {
+					ret += makeIndent(indent + 1)
+					list, _ := prettyPrint(reflect.ValueOf(value), indent+1)
+					ret += fmt.Sprintf("\"%s\": %s,\n", platformArchMap[arch], list)
+				}
+			}
+
+			ret += makeIndent(indent + 1)
+			list, _ := prettyPrint(reflect.ValueOf(stringList.GetValueForArch("default")), indent+1)
+			ret += fmt.Sprintf("\"%s\": %s,\n", "//conditions:default", list)
+
+			ret += makeIndent(indent)
+			ret += "})"
+			return ret, err
 		}
 
 		ret = "{\n"
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 27212d1..aa4fc1d 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -194,6 +194,7 @@
 	for _, testCase := range testCases {
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
+
 		ctx.RegisterModuleType("custom", customModuleFactory)
 		ctx.Register()
 
@@ -202,7 +203,8 @@
 		_, errs = ctx.PrepareBuildActions(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, QueryView)[dir]
+		codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
 		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
 			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
 		}
@@ -245,6 +247,7 @@
 	for _, testCase := range testCases {
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
+
 		ctx.RegisterModuleType("custom", customModuleFactory)
 		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
 		ctx.RegisterForBazelConversion()
@@ -258,7 +261,9 @@
 			continue
 		}
 
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+
 		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
 			t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
 		} else {
@@ -415,7 +420,8 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
 			t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
 		}
@@ -469,6 +475,21 @@
 		dir                                string
 	}{
 		{
+			description:                        "filegroup with does not specify srcs",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			bp: `filegroup {
+    name: "fg_foo",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				`filegroup(
+    name = "fg_foo",
+)`,
+			},
+		},
+		{
 			description:                        "filegroup with no srcs",
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
@@ -889,7 +910,9 @@
 		if testCase.dir != "" {
 			checkDir = testCase.dir
 		}
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir]
+
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
 		} else {
@@ -1103,7 +1126,8 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
 		if actualCount := len(bazelTargets); actualCount != 1 {
 			t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
 		}
@@ -1190,7 +1214,8 @@
 		_, errs = ctx.ResolveDependencies(config)
 		android.FailIfErrored(t, errs)
 
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
 			t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
 		}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
new file mode 100644
index 0000000..5bf5c80
--- /dev/null
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -0,0 +1,222 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"strings"
+	"testing"
+)
+
+const (
+	// See cc/testing.go for more context
+	soongCcLibraryPreamble = `
+cc_defaults {
+	name: "linux_bionic_supported",
+}
+
+toolchain_library {
+	name: "libclang_rt.builtins-x86_64-android",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}
+
+toolchain_library {
+	name: "libatomic",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}`
+)
+
+func TestCcLibraryHeadersLoadStatement(t *testing.T) {
+	testCases := []struct {
+		bazelTargets           BazelTargets
+		expectedLoadStatements string
+	}{
+		{
+			bazelTargets: BazelTargets{
+				BazelTarget{
+					name:      "cc_library_headers_target",
+					ruleClass: "cc_library_headers",
+					// Note: no bzlLoadLocation for native rules
+				},
+			},
+			expectedLoadStatements: ``,
+		},
+	}
+
+	for _, testCase := range testCases {
+		actual := testCase.bazelTargets.LoadStatements()
+		expected := testCase.expectedLoadStatements
+		if actual != expected {
+			t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+		}
+	}
+
+}
+
+func TestCcLibraryHeadersBp2Build(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		preArchMutators                    []android.RegisterMutatorFunc
+		depsMutators                       []android.RegisterMutatorFunc
+		bp                                 string
+		expectedBazelTargets               []string
+		filesystem                         map[string]string
+		dir                                string
+	}{
+		{
+			description:                        "cc_library_headers test",
+			moduleTypeUnderTest:                "cc_library_headers",
+			moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+			filesystem: map[string]string{
+				"lib-1/lib1a.h": "",
+				"lib-1/lib1b.h": "",
+				"lib-2/lib2a.h": "",
+				"lib-2/lib2b.h": "",
+				"dir-1/dir1a.h": "",
+				"dir-1/dir1b.h": "",
+				"dir-2/dir2a.h": "",
+				"dir-2/dir2b.h": "",
+			},
+			bp: soongCcLibraryPreamble + `
+cc_library_headers {
+    name: "lib-1",
+    export_include_dirs: ["lib-1"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_headers {
+    name: "lib-2",
+    export_include_dirs: ["lib-2"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_headers {
+    name: "foo_headers",
+    export_include_dirs: ["dir-1", "dir-2"],
+    header_libs: ["lib-1", "lib-2"],
+    export_header_lib_headers: ["lib-1", "lib-2"],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`cc_library_headers(
+    name = "foo_headers",
+    deps = [
+        ":lib-1",
+        ":lib-2",
+    ],
+    hdrs = [
+        "dir-1/dir1a.h",
+        "dir-1/dir1b.h",
+        "dir-2/dir2a.h",
+        "dir-2/dir2b.h",
+    ],
+    includes = [
+        "dir-1",
+        "dir-2",
+    ],
+)`, `cc_library_headers(
+    name = "lib-1",
+    hdrs = [
+        "lib-1/lib1a.h",
+        "lib-1/lib1b.h",
+    ],
+    includes = [
+        "lib-1",
+    ],
+)`, `cc_library_headers(
+    name = "lib-2",
+    hdrs = [
+        "lib-2/lib2a.h",
+        "lib-2/lib2b.h",
+    ],
+    includes = [
+        "lib-2",
+    ],
+)`},
+		},
+	}
+
+	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.bp, filesystem)
+		ctx := android.NewTestContext(config)
+
+		cc.RegisterCCBuildComponents(ctx)
+		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		for _, m := range testCase.depsMutators {
+			ctx.DepsBp2BuildMutators(m)
+		}
+		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
+		}
+
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			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/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
new file mode 100644
index 0000000..1d4e322
--- /dev/null
+++ b/bp2build/cc_object_conversion_test.go
@@ -0,0 +1,360 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"fmt"
+	"strings"
+	"testing"
+)
+
+func TestCcObjectBp2Build(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 cc_object generates cc_object with include header dep",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			filesystem: map[string]string{
+				"a/b/foo.h":     "",
+				"a/b/bar.h":     "",
+				"a/b/exclude.c": "",
+				"a/b/c.c":       "",
+			},
+			blueprint: `cc_object {
+    name: "foo",
+    local_include_dirs: ["include"],
+    cflags: [
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: [
+        "a/b/*.h",
+        "a/b/*.c"
+    ],
+    exclude_srcs: ["a/b/exclude.c"],
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "foo",
+    copts = [
+        "-fno-addrsig",
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+    ],
+    local_include_dirs = [
+        "include",
+    ],
+    srcs = [
+        "a/b/bar.h",
+        "a/b/foo.h",
+        "a/b/c.c",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "simple cc_object with defaults",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			blueprint: `cc_object {
+    name: "foo",
+    local_include_dirs: ["include"],
+    srcs: [
+        "a/b/*.h",
+        "a/b/c.c"
+    ],
+
+    defaults: ["foo_defaults"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_defaults {
+    name: "foo_defaults",
+    defaults: ["foo_bar_defaults"],
+}
+
+cc_defaults {
+    name: "foo_bar_defaults",
+    cflags: [
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+    ],
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "foo",
+    copts = [
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+        "-fno-addrsig",
+    ],
+    local_include_dirs = [
+        "include",
+    ],
+    srcs = [
+        "a/b/c.c",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "cc_object with cc_object deps in objs props",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			filesystem: map[string]string{
+				"a/b/c.c": "",
+				"x/y/z.c": "",
+			},
+			blueprint: `cc_object {
+    name: "foo",
+    srcs: ["a/b/c.c"],
+    objs: ["bar"],
+
+    bazel_module: { bp2build_available: true },
+}
+
+cc_object {
+    name: "bar",
+    srcs: ["x/y/z.c"],
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "bar",
+    copts = [
+        "-fno-addrsig",
+    ],
+    srcs = [
+        "x/y/z.c",
+    ],
+)`, `cc_object(
+    name = "foo",
+    copts = [
+        "-fno-addrsig",
+    ],
+    deps = [
+        ":bar",
+    ],
+    srcs = [
+        "a/b/c.c",
+    ],
+)`,
+			},
+		},
+	}
+
+	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)
+		// Always register cc_defaults module factory
+		ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+
+		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,
+					)
+				}
+			}
+		}
+	}
+}
+
+func TestCcObjectConfigurableAttributesBp2Build(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:                        "cc_object setting cflags for one arch",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			blueprint: `cc_object {
+    name: "foo",
+    arch: {
+        x86: {
+            cflags: ["-fPIC"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{
+				`cc_object(
+    name = "foo",
+    copts = [
+        "-fno-addrsig",
+    ] + select({
+        "@bazel_tools//platforms:x86_32": [
+            "-fPIC",
+        ],
+        "//conditions:default": [
+        ],
+    }),
+)`,
+			},
+		},
+		{
+			description:                        "cc_object setting cflags for 4 architectures",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			blueprint: `cc_object {
+    name: "foo",
+    arch: {
+        x86: {
+            cflags: ["-fPIC"],
+        },
+        x86_64: {
+            cflags: ["-fPIC"],
+        },
+        arm: {
+            cflags: ["-Wall"],
+        },
+        arm64: {
+            cflags: ["-Wall"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{
+				`cc_object(
+    name = "foo",
+    copts = [
+        "-fno-addrsig",
+    ] + select({
+        "@bazel_tools//platforms:arm": [
+            "-Wall",
+        ],
+        "@bazel_tools//platforms:aarch64": [
+            "-Wall",
+        ],
+        "@bazel_tools//platforms:x86_32": [
+            "-fPIC",
+        ],
+        "@bazel_tools//platforms:x86_64": [
+            "-fPIC",
+        ],
+        "//conditions:default": [
+        ],
+    }),
+)`,
+			},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		filesystem := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
+		ctx := android.NewTestContext(config)
+		// Always register cc_defaults module factory
+		ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+
+		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/bp2build/configurability.go b/bp2build/configurability.go
new file mode 100644
index 0000000..47cf3c6
--- /dev/null
+++ b/bp2build/configurability.go
@@ -0,0 +1,15 @@
+package bp2build
+
+import "android/soong/android"
+
+// Configurability support for bp2build.
+
+var (
+	// A map of architectures to the Bazel label of the constraint_value.
+	platformArchMap = map[android.ArchType]string{
+		android.Arm:    "@bazel_tools//platforms:arm",
+		android.Arm64:  "@bazel_tools//platforms:aarch64",
+		android.X86:    "@bazel_tools//platforms:x86_32",
+		android.X86_64: "@bazel_tools//platforms:x86_64",
+	}
+)
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 081e082..1225f2b 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -93,6 +93,7 @@
 		"name":       true, // redundant, since this is explicitly generated for every target
 		"from":       true, // reserved keyword
 		"in":         true, // reserved keyword
+		"size":       true, // reserved for tests
 		"arch":       true, // interface prop type is not supported yet.
 		"multilib":   true, // interface prop type is not supported yet.
 		"target":     true, // interface prop type is not supported yet.
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
new file mode 100644
index 0000000..916129f
--- /dev/null
+++ b/bp2build/metrics.go
@@ -0,0 +1,30 @@
+package bp2build
+
+import (
+	"android/soong/android"
+	"fmt"
+)
+
+// Simple metrics struct to collect information about a Blueprint to BUILD
+// conversion process.
+type CodegenMetrics struct {
+	// Total number of Soong/Blueprint modules
+	TotalModuleCount int
+
+	// Counts of generated Bazel targets per Bazel rule class
+	RuleClassCount map[string]int
+}
+
+// Print the codegen metrics to stdout.
+func (metrics CodegenMetrics) Print() {
+	generatedTargetCount := 0
+	for _, ruleClass := range android.SortedStringKeys(metrics.RuleClassCount) {
+		count := metrics.RuleClassCount[ruleClass]
+		fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
+		generatedTargetCount += count
+	}
+	fmt.Printf(
+		"[bp2build] Generated %d total BUILD targets from %d Android.bp modules.\n",
+		generatedTargetCount,
+		metrics.TotalModuleCount)
+}
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index dcc75bd..2aa373c 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -115,7 +115,8 @@
 		if testCase.dir != "" {
 			checkDir = testCase.dir
 		}
-		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir]
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
 		} else {
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 2e59999..bd75a8f 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -25,10 +25,9 @@
 
 type customModule struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	props customProps
-
-	bazelProps bazel.Properties
 }
 
 // OutputFiles is needed because some instances of this module use dist with a
@@ -44,7 +43,7 @@
 func customModuleFactoryBase() android.Module {
 	module := &customModule{}
 	module.AddProperties(&module.props)
-	module.AddProperties(&module.bazelProps)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -127,7 +126,7 @@
 
 func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
-		if !m.bazelProps.Bazel_module.Bp2build_available {
+		if !m.ConvertWithBp2build() {
 			return
 		}
 
@@ -136,9 +135,11 @@
 			String_list_prop: m.props.String_list_prop,
 		}
 
-		props := bazel.NewBazelTargetModuleProperties(m.Name(), "custom", "")
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class: "custom",
+		}
 
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, props, attrs)
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, m.Name(), props, attrs)
 	}
 }
 
@@ -146,32 +147,35 @@
 // module to target.
 func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
-		if !m.bazelProps.Bazel_module.Bp2build_available {
+		if !m.ConvertWithBp2build() {
 			return
 		}
 
 		baseName := m.Name()
 		attrs := &customBazelModuleAttributes{}
 
-		myLibraryProps := bazel.NewBazelTargetModuleProperties(
-			baseName,
-			"my_library",
-			"//build/bazel/rules:rules.bzl",
-		)
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, myLibraryProps, attrs)
+		myLibraryProps := bazel.BazelTargetModuleProperties{
+			Rule_class:        "my_library",
+			Bzl_load_location: "//build/bazel/rules:rules.bzl",
+		}
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName, myLibraryProps, attrs)
 
-		protoLibraryProps := bazel.NewBazelTargetModuleProperties(
-			baseName+"_proto_library_deps",
-			"proto_library",
-			"//build/bazel/rules:proto.bzl",
-		)
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, protoLibraryProps, attrs)
+		protoLibraryProps := bazel.BazelTargetModuleProperties{
+			Rule_class:        "proto_library",
+			Bzl_load_location: "//build/bazel/rules:proto.bzl",
+		}
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_proto_library_deps", protoLibraryProps, attrs)
 
-		myProtoLibraryProps := bazel.NewBazelTargetModuleProperties(
-			baseName+"_my_proto_library_deps",
-			"my_proto_library",
-			"//build/bazel/rules:proto.bzl",
-		)
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, myProtoLibraryProps, attrs)
+		myProtoLibraryProps := bazel.BazelTargetModuleProperties{
+			Rule_class:        "my_proto_library",
+			Bzl_load_location: "//build/bazel/rules:proto.bzl",
+		}
+		ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_my_proto_library_deps", myProtoLibraryProps, attrs)
 	}
 }
+
+// Helper method for tests to easily access the targets in a dir.
+func generateBazelTargetsForDir(codegenCtx CodegenContext, dir string) BazelTargets {
+	buildFileToTargets, _ := GenerateBazelTargets(codegenCtx)
+	return buildFileToTargets[dir]
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 94b8252..fae6101 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -670,6 +670,26 @@
 	return nil
 }
 
+func RewriteRuntimeResourceOverlay(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !(ok && mod.Type == "runtime_resource_overlay") {
+			continue
+		}
+		// runtime_resource_overlays are always product specific in Make.
+		if _, ok := mod.GetProperty("product_specific"); !ok {
+			prop := &parser.Property{
+				Name: "product_specific",
+				Value: &parser.Bool{
+					Value: true,
+				},
+			}
+			mod.Properties = append(mod.Properties, prop)
+		}
+	}
+	return nil
+}
+
 // Removes library dependencies which are empty (and restricted from usage in Soong)
 func removeEmptyLibDependencies(f *Fixer) error {
 	emptyLibraries := []string{
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index ef9814f..61dfe1a 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -1056,3 +1056,71 @@
 		})
 	}
 }
+
+func TestRewriteRuntimeResourceOverlay(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "product_specific runtime_resource_overlay",
+			in: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: true,
+				}
+			`,
+			out: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: true,
+				}
+			`,
+		},
+		{
+			// It's probably wrong for runtime_resource_overlay not to be product specific, but let's not
+			// debate it here.
+			name: "non-product_specific runtime_resource_overlay",
+			in: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: false,
+				}
+			`,
+			out: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: false,
+				}
+			`,
+		},
+		{
+			name: "runtime_resource_overlay without product_specific value",
+			in: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+				}
+			`,
+			out: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: true,
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return RewriteRuntimeResourceOverlay(fixer)
+			})
+		})
+	}
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index 0018ea9..9564723 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -7,14 +7,16 @@
 #   BUILD_NUMBER          build number, used to generate unique ID (will use UUID if not set)
 #   DIST_DIR              where the resulting all.kzip will be placed
 #   KYTHE_KZIP_ENCODING   proto or json (proto is default)
+#   KYTHE_JAVA_SOURCE_BATCH_SIZE maximum number of the Java source files in a compilation unit
 #   OUT_DIR               output directory (out if not specified})
 #   TARGET_BUILD_VARIANT  variant, e.g., `userdebug`
 #   TARGET_PRODUCT        target device name, e.g., 'aosp_blueline'
 #   XREF_CORPUS           source code repository URI, e.g., 'android.googlesource.com/platform/superproject'
 
 : ${BUILD_NUMBER:=$(uuidgen)}
+: ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500}
 : ${KYTHE_KZIP_ENCODING:=proto}
-export KYTHE_KZIP_ENCODING
+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
 # sufficiently many files were generated.
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 8652c10..536efa4 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -83,7 +83,7 @@
 		Include:  "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
 
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				if len(c.Properties.Logtags) > 0 {
 					entries.AddStrings("LOCAL_LOGTAGS_FILES", c.Properties.Logtags...)
 				}
@@ -162,16 +162,17 @@
 
 func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkEntries) {
 	if len(extraTestConfigs) > 0 {
-		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-			entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
-		})
+		entries.ExtraEntries = append(entries.ExtraEntries,
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
+			})
 	}
 }
 
 func AndroidMkWriteTestData(data []android.DataPath, entries *android.AndroidMkEntries) {
 	testFiles := android.AndroidMkDataPaths(data)
 	if len(testFiles) > 0 {
-		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.AddStrings("LOCAL_TEST_DATA", testFiles...)
 		})
 	}
@@ -224,7 +225,7 @@
 		entries.Class = "STATIC_LIBRARIES"
 	} else if library.shared() {
 		entries.Class = "SHARED_LIBRARIES"
-		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
 			if !library.buildStubs() {
 				entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
@@ -244,7 +245,7 @@
 		entries.DistFiles = android.MakeDefaultDistFiles(library.distFile)
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		library.androidMkWriteExportedFlags(entries)
 		library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
 
@@ -272,7 +273,7 @@
 		if library.buildStubs() && library.stubsVersion() != "" {
 			entries.SubName = "." + library.stubsVersion()
 		}
-		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			// library.makeUninstallable() depends on this to bypass HideFromMake() for
 			// static libraries.
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
@@ -315,7 +316,7 @@
 
 	entries.Class = "EXECUTABLES"
 	entries.DistFiles = binary.distFiles
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
 		if len(binary.symlinks) > 0 {
 			entries.AddStrings("LOCAL_MODULE_SYMLINKS", binary.symlinks...)
@@ -337,7 +338,7 @@
 func (benchmark *benchmarkDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	ctx.subAndroidMk(entries, benchmark.binaryDecorator)
 	entries.Class = "NATIVE_TESTS"
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if len(benchmark.Properties.Test_suites) > 0 {
 			entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
 		}
@@ -362,7 +363,7 @@
 	if Bool(test.Properties.Test_per_src) {
 		entries.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
 	}
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if len(test.Properties.Test_suites) > 0 {
 			entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
 		}
@@ -406,7 +407,7 @@
 			filepath.Dir(fuzz.config.String())+":config.json")
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
 		if len(fuzzFiles) > 0 {
 			entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
@@ -423,7 +424,7 @@
 
 func (library *toolchainLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.Class = "STATIC_LIBRARIES"
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		_, suffix, _ := android.SplitFileExt(entries.OutputFile.Path().Base())
 		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
 	})
@@ -439,7 +440,7 @@
 		entries.OutputFile = android.OptionalPathForPath(installer.path)
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		path, file := filepath.Split(installer.path.ToMakePath().String())
 		stem, suffix, _ := android.SplitFileExt(file)
 		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
@@ -457,7 +458,7 @@
 		return
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		path, file := filepath.Split(c.installPath.String())
 		stem, suffix, _ := android.SplitFileExt(file)
 		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
@@ -481,7 +482,7 @@
 
 	entries.SubName = c.androidMkSuffix
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		c.libraryDecorator.androidMkWriteExportedFlags(entries)
 
 		// Specifying stem is to pass check_elf_files when vendor modules link against vndk prebuilt.
@@ -521,7 +522,7 @@
 
 	entries.SubName += c.baseProperties.Androidmk_suffix
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		c.libraryDecorator.androidMkWriteExportedFlags(entries)
 
 		if c.shared() || c.static() {
@@ -548,7 +549,7 @@
 	entries.Class = "EXECUTABLES"
 	entries.SubName = c.baseProperties.Androidmk_suffix
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
 	})
 }
@@ -575,7 +576,7 @@
 	entries.Class = "SHARED_LIBRARIES"
 	entries.SubName = vendorPublicLibrarySuffix
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		c.libraryDecorator.androidMkWriteExportedFlags(entries)
 		_, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
 
@@ -586,7 +587,7 @@
 }
 
 func (p *prebuiltLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if p.properties.Check_elf_files != nil {
 			entries.SetBool("LOCAL_CHECK_ELF_FILES", *p.properties.Check_elf_files)
 		} else {
@@ -616,7 +617,7 @@
 func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, entries *android.AndroidMkEntries) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
-		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetBool("LOCAL_ALLOW_UNDEFINED_SYMBOLS", *allow)
 		})
 	}
diff --git a/cc/cc.go b/cc/cc.go
index 7f59158..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
 }
@@ -762,6 +773,7 @@
 	android.DefaultableModuleBase
 	android.ApexModuleBase
 	android.SdkBase
+	android.BazelModuleBase
 
 	Properties       BaseProperties
 	VendorProperties VendorProperties
@@ -778,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
@@ -1051,6 +1064,7 @@
 	}
 
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
+	android.InitBazelModule(c)
 	android.InitApexModule(c)
 	android.InitSdkAwareModule(c)
 	android.InitDefaultableModule(c)
@@ -1557,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 {
@@ -1604,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,
@@ -2418,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
@@ -2631,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 b1c1b2e..e640f12 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -20,6 +20,7 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"strings"
 	"testing"
 
@@ -566,7 +567,7 @@
 	ctx := testCcWithConfig(t, config)
 
 	module := ctx.ModuleForTests("llndk.libraries.txt", "")
-	entries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
 	assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.VER.txt"})
 }
 
@@ -712,7 +713,7 @@
 	if !strings.HasSuffix(outputPath, "/main_test") {
 		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
 	}
-	entries := android.AndroidMkEntriesForTest(t, config, "", module)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
 	if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
 		t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
 			" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
@@ -3104,7 +3105,7 @@
 	if !strings.HasSuffix(outputPath, "/main_test") {
 		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
 	}
-	entries := android.AndroidMkEntriesForTest(t, config, "", module)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
 	if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
 		t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
 			" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
@@ -3910,3 +3911,274 @@
 	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
 	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
 }
+
+func TestIncludeDirsExporting(t *testing.T) {
+
+	// Trim spaces from the beginning, end and immediately after any newline characters. Leaves
+	// embedded newline characters alone.
+	trimIndentingSpaces := func(s string) string {
+		return strings.TrimSpace(regexp.MustCompile("(^|\n)\\s+").ReplaceAllString(s, "$1"))
+	}
+
+	checkPaths := func(t *testing.T, message string, expected string, paths android.Paths) {
+		t.Helper()
+		expected = trimIndentingSpaces(expected)
+		actual := trimIndentingSpaces(strings.Join(android.FirstUniqueStrings(android.NormalizePathsForTesting(paths)), "\n"))
+		if expected != actual {
+			t.Errorf("%s: expected:\n%s\n actual:\n%s\n", message, expected, actual)
+		}
+	}
+
+	type exportedChecker func(t *testing.T, name string, exported FlagExporterInfo)
+
+	checkIncludeDirs := func(t *testing.T, ctx *android.TestContext, module android.Module, checkers ...exportedChecker) {
+		t.Helper()
+		exported := ctx.ModuleProvider(module, FlagExporterInfoProvider).(FlagExporterInfo)
+		name := module.Name()
+
+		for _, checker := range checkers {
+			checker(t, name, exported)
+		}
+	}
+
+	expectedIncludeDirs := func(expectedPaths string) exportedChecker {
+		return func(t *testing.T, name string, exported FlagExporterInfo) {
+			t.Helper()
+			checkPaths(t, fmt.Sprintf("%s: include dirs", name), expectedPaths, exported.IncludeDirs)
+		}
+	}
+
+	expectedSystemIncludeDirs := func(expectedPaths string) exportedChecker {
+		return func(t *testing.T, name string, exported FlagExporterInfo) {
+			t.Helper()
+			checkPaths(t, fmt.Sprintf("%s: system include dirs", name), expectedPaths, exported.SystemIncludeDirs)
+		}
+	}
+
+	expectedGeneratedHeaders := func(expectedPaths string) exportedChecker {
+		return func(t *testing.T, name string, exported FlagExporterInfo) {
+			t.Helper()
+			checkPaths(t, fmt.Sprintf("%s: generated headers", name), expectedPaths, exported.GeneratedHeaders)
+		}
+	}
+
+	expectedOrderOnlyDeps := func(expectedPaths string) exportedChecker {
+		return func(t *testing.T, name string, exported FlagExporterInfo) {
+			t.Helper()
+			checkPaths(t, fmt.Sprintf("%s: order only deps", name), expectedPaths, exported.Deps)
+		}
+	}
+
+	genRuleModules := `
+		genrule {
+			name: "genrule_foo",
+			cmd: "generate-foo",
+			out: [
+				"generated_headers/foo/generated_header.h",
+			],
+			export_include_dirs: [
+				"generated_headers",
+			],
+		}
+
+		genrule {
+			name: "genrule_bar",
+			cmd: "generate-bar",
+			out: [
+				"generated_headers/bar/generated_header.h",
+			],
+			export_include_dirs: [
+				"generated_headers",
+			],
+		}
+	`
+
+	t.Run("ensure exported include dirs are not automatically re-exported from shared_libs", func(t *testing.T) {
+		ctx := testCc(t, genRuleModules+`
+		cc_library {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			export_include_dirs: ["foo/standard"],
+			export_system_include_dirs: ["foo/system"],
+			generated_headers: ["genrule_foo"],
+			export_generated_headers: ["genrule_foo"],
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["bar.c"],
+			shared_libs: ["libfoo"],
+			export_include_dirs: ["bar/standard"],
+			export_system_include_dirs: ["bar/system"],
+			generated_headers: ["genrule_bar"],
+			export_generated_headers: ["genrule_bar"],
+		}
+		`)
+		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, foo,
+			expectedIncludeDirs(`
+				foo/standard
+				.intermediates/genrule_foo/gen/generated_headers
+			`),
+			expectedSystemIncludeDirs(`foo/system`),
+			expectedGeneratedHeaders(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+			expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+		)
+
+		bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, bar,
+			expectedIncludeDirs(`
+				bar/standard
+				.intermediates/genrule_bar/gen/generated_headers
+			`),
+			expectedSystemIncludeDirs(`bar/system`),
+			expectedGeneratedHeaders(`.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h`),
+			expectedOrderOnlyDeps(`.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h`),
+		)
+	})
+
+	t.Run("ensure exported include dirs are automatically re-exported from whole_static_libs", func(t *testing.T) {
+		ctx := testCc(t, genRuleModules+`
+		cc_library {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			export_include_dirs: ["foo/standard"],
+			export_system_include_dirs: ["foo/system"],
+			generated_headers: ["genrule_foo"],
+			export_generated_headers: ["genrule_foo"],
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["bar.c"],
+			whole_static_libs: ["libfoo"],
+			export_include_dirs: ["bar/standard"],
+			export_system_include_dirs: ["bar/system"],
+			generated_headers: ["genrule_bar"],
+			export_generated_headers: ["genrule_bar"],
+		}
+		`)
+		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, foo,
+			expectedIncludeDirs(`
+				foo/standard
+				.intermediates/genrule_foo/gen/generated_headers
+			`),
+			expectedSystemIncludeDirs(`foo/system`),
+			expectedGeneratedHeaders(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+			expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+		)
+
+		bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, bar,
+			expectedIncludeDirs(`
+				bar/standard
+				foo/standard
+				.intermediates/genrule_foo/gen/generated_headers
+				.intermediates/genrule_bar/gen/generated_headers
+			`),
+			expectedSystemIncludeDirs(`
+				bar/system
+				foo/system
+			`),
+			expectedGeneratedHeaders(`
+				.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h
+				.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h
+			`),
+			expectedOrderOnlyDeps(`
+				.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h
+				.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h
+			`),
+		)
+	})
+
+	t.Run("ensure only aidl headers are exported", func(t *testing.T) {
+		ctx := testCc(t, genRuleModules+`
+		cc_library_shared {
+			name: "libfoo",
+			srcs: [
+				"foo.c",
+				"b.aidl",
+				"a.proto",
+			],
+			aidl: {
+				export_aidl_headers: true,
+			}
+		}
+		`)
+		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, foo,
+			expectedIncludeDirs(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl
+			`),
+			expectedSystemIncludeDirs(``),
+			expectedGeneratedHeaders(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+			`),
+			expectedOrderOnlyDeps(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+			`),
+		)
+	})
+
+	t.Run("ensure only proto headers are exported", func(t *testing.T) {
+		ctx := testCc(t, genRuleModules+`
+		cc_library_shared {
+			name: "libfoo",
+			srcs: [
+				"foo.c",
+				"b.aidl",
+				"a.proto",
+			],
+			proto: {
+				export_proto_headers: true,
+			}
+		}
+		`)
+		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, foo,
+			expectedIncludeDirs(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto
+			`),
+			expectedSystemIncludeDirs(``),
+			expectedGeneratedHeaders(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto/a.pb.h
+			`),
+			expectedOrderOnlyDeps(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto/a.pb.h
+			`),
+		)
+	})
+
+	t.Run("ensure only sysprop headers are exported", func(t *testing.T) {
+		ctx := testCc(t, genRuleModules+`
+		cc_library_shared {
+			name: "libfoo",
+			srcs: [
+				"foo.c",
+				"a.sysprop",
+				"b.aidl",
+				"a.proto",
+			],
+		}
+		`)
+		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		checkIncludeDirs(t, ctx, foo,
+			expectedIncludeDirs(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include
+			`),
+			expectedSystemIncludeDirs(``),
+			expectedGeneratedHeaders(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
+			`),
+			expectedOrderOnlyDeps(`
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/a.sysprop.h
+			`),
+		)
+	})
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index 5f30d3d..2e71922 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -230,6 +230,8 @@
 	// other modules and filegroups. May include source files that have not yet been translated to
 	// C/C++ (.aidl, .proto, etc.)
 	srcsBeforeGen android.Paths
+
+	generatedSourceInfo
 }
 
 var _ compiler = (*baseCompiler)(nil)
@@ -440,7 +442,7 @@
 		fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
 
 	if isThirdParty(modulePath) {
-		flags.Global.CommonFlags = append([]string{"${config.ClangExternalCflags}"}, flags.Global.CommonFlags...)
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "${config.ClangExternalCflags}")
 	}
 
 	if tc.Bionic() {
@@ -634,10 +636,11 @@
 
 	srcs := append(android.Paths(nil), compiler.srcsBeforeGen...)
 
-	srcs, genDeps := genSources(ctx, srcs, buildFlags)
+	srcs, genDeps, info := genSources(ctx, srcs, buildFlags)
 	pathDeps = append(pathDeps, genDeps...)
 
 	compiler.pathDeps = pathDeps
+	compiler.generatedSourceInfo = info
 	compiler.cFlagsDeps = flags.CFlagsDeps
 
 	// Save src, buildFlags and context
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index e6024aa..d083d2a 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -31,6 +31,10 @@
 		"armv8-a": []string{
 			"-march=armv8-a",
 		},
+		"armv8-a-branchprot": []string{
+			"-march=armv8-a",
+			"-mbranch-protection=standard",
+		},
 		"armv8-2a": []string{
 			"-march=armv8.2-a",
 		},
@@ -102,6 +106,7 @@
 	pctx.StaticVariable("Arm64ClangCppflags", strings.Join(ClangFilterUnknownCflags(arm64Cppflags), " "))
 
 	pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
+	pctx.StaticVariable("Arm64ClangArmv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
 	pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
 	pctx.StaticVariable("Arm64ClangArmv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
 
@@ -124,6 +129,7 @@
 var (
 	arm64ClangArchVariantCflagsVar = map[string]string{
 		"armv8-a":  "${config.Arm64ClangArmv8ACflags}",
+		"armv8-a-branchprot": "${config.Arm64ClangArmv8ABranchProtCflags}",
 		"armv8-2a": "${config.Arm64ClangArmv82ACflags}",
 		"armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
 	}
@@ -202,6 +208,7 @@
 func arm64ToolchainFactory(arch android.Arch) Toolchain {
 	switch arch.ArchVariant {
 	case "armv8-a":
+	case "armv8-a-branchprot":
 	case "armv8-2a":
 	case "armv8-2a-dotprod":
 		// Nothing extra for armv8-a/armv8-2a
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 71c7626..5e46d5a 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -150,7 +150,7 @@
 		"-Wunguarded-availability",
 		// This macro allows the bionic versioning.h to indirectly determine whether the
 		// option -Wunguarded-availability is on or not.
-		"-D__ANDROID_UNGUARDED_AVAILABILITY__",
+		"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
 	}, " "))
 
 	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
diff --git a/cc/config/global.go b/cc/config/global.go
index ee41125..e60bb3d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -144,8 +144,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r407598b"
-	ClangDefaultShortVersion = "12.0.2"
+	ClangDefaultVersion      = "clang-r412851"
+	ClangDefaultShortVersion = "12.0.3"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 7cc9f43..c4563e2 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -31,7 +31,6 @@
 		}
 		checks := strings.Join([]string{
 			"-*",
-			"abseil-*",
 			"android-*",
 			"bugprone-*",
 			"cert-*",
@@ -87,27 +86,11 @@
 		}, ",")
 	})
 
-	// Give warnings to header files only in selected directories.
-	// Do not give warnings to external or vendor header files, which contain too
-	// many warnings.
+	// To reduce duplicate warnings from the same header files,
+	// header-filter will contain only the module directory and
+	// those specified by DEFAULT_TIDY_HEADER_DIRS.
 	pctx.VariableFunc("TidyDefaultHeaderDirs", func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS"); override != "" {
-			return override
-		}
-		return strings.Join([]string{
-			"art/",
-			"bionic/",
-			"bootable/",
-			"build/",
-			"cts/",
-			"dalvik/",
-			"developers/",
-			"development/",
-			"frameworks/",
-			"libcore/",
-			"libnativehelper/",
-			"system/",
-		}, "|")
+		return ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS")
 	})
 
 	// Use WTIH_TIDY_FLAGS to pass extra global default clang-tidy flags.
diff --git a/cc/fuzz.go b/cc/fuzz.go
index d7da5ab..9fe5b17 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -20,6 +20,8 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc/config"
 )
@@ -172,7 +174,7 @@
 // This function takes a module and determines if it is a unique shared library
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
-//  - The module is not a shared library, or
+//  - The module is not an installable shared library, or
 //  - The module is a header, stub, or vendor-linked library, or
 //  - The module is a prebuilt and its source is available, or
 //  - The module is a versioned member of an SDK snapshot.
@@ -209,6 +211,11 @@
 		if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary {
 			return false
 		}
+		// Discard installable:false libraries because they are expected to be absent
+		// in runtime.
+		if !proptools.BoolDefault(ccLibrary.Properties.Installable, true) {
+			return false
+		}
 	}
 
 	// If the same library is present both as source and a prebuilt we must pick
diff --git a/cc/gen.go b/cc/gen.go
index 75b9e49..83c019c 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -220,8 +220,35 @@
 	return rcFile, headerFile
 }
 
+// Used to communicate information from the genSources method back to the library code that uses
+// it.
+type generatedSourceInfo struct {
+	// The headers created from .proto files
+	protoHeaders android.Paths
+
+	// The files that can be used as order only dependencies in order to ensure that the proto header
+	// files are up to date.
+	protoOrderOnlyDeps android.Paths
+
+	// The headers created from .aidl files
+	aidlHeaders android.Paths
+
+	// The files that can be used as order only dependencies in order to ensure that the aidl header
+	// files are up to date.
+	aidlOrderOnlyDeps android.Paths
+
+	// The headers created from .sysprop files
+	syspropHeaders android.Paths
+
+	// The files that can be used as order only dependencies in order to ensure that the sysprop
+	// header files are up to date.
+	syspropOrderOnlyDeps android.Paths
+}
+
 func genSources(ctx android.ModuleContext, srcFiles android.Paths,
-	buildFlags builderFlags) (android.Paths, android.Paths) {
+	buildFlags builderFlags) (android.Paths, android.Paths, generatedSourceInfo) {
+
+	var info generatedSourceInfo
 
 	var deps android.Paths
 	var rsFiles android.Paths
@@ -258,7 +285,9 @@
 		case ".proto":
 			ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
 			srcFiles[i] = ccFile
-			deps = append(deps, headerFile)
+			info.protoHeaders = append(info.protoHeaders, headerFile)
+			// Use the generated header as an order only dep to ensure that it is up to date when needed.
+			info.protoOrderOnlyDeps = append(info.protoOrderOnlyDeps, headerFile)
 		case ".aidl":
 			if aidlRule == nil {
 				aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
@@ -267,7 +296,12 @@
 			cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp")
 			depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
 			srcFiles[i] = cppFile
-			deps = append(deps, genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)...)
+			aidlHeaders := genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)
+			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
+			// Use the generated headers as order only deps to ensure that they are up to date when
+			// needed.
+			// TODO: Reduce the size of the ninja file by using one order only dep for the whole rule
+			info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...)
 		case ".rscript", ".fs":
 			cppFile := rsGeneratedCppFile(ctx, srcFile)
 			rsFiles = append(rsFiles, srcFiles[i])
@@ -279,7 +313,10 @@
 		case ".sysprop":
 			cppFile, headerFiles := genSysprop(ctx, srcFile)
 			srcFiles[i] = cppFile
-			deps = append(deps, headerFiles...)
+			info.syspropHeaders = append(info.syspropHeaders, headerFiles...)
+			// Use the generated headers as order only deps to ensure that they are up to date when
+			// needed.
+			info.syspropOrderOnlyDeps = append(info.syspropOrderOnlyDeps, headerFiles...)
 		}
 	}
 
@@ -291,9 +328,13 @@
 		yaccRule_.Build("yacc", "gen yacc")
 	}
 
+	deps = append(deps, info.protoOrderOnlyDeps...)
+	deps = append(deps, info.aidlOrderOnlyDeps...)
+	deps = append(deps, info.syspropOrderOnlyDeps...)
+
 	if len(rsFiles) > 0 {
 		deps = append(deps, rsGenerateCpp(ctx, rsFiles, buildFlags.rsFlags)...)
 	}
 
-	return srcFiles, deps
+	return srcFiles, deps, info
 }
diff --git a/cc/library.go b/cc/library.go
index 65533bc..0e6e107 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1313,9 +1313,8 @@
 			dir := android.PathForModuleGen(ctx, "aidl")
 			library.reexportDirs(dir)
 
-			// TODO: restrict to aidl deps
-			library.reexportDeps(library.baseCompiler.pathDeps...)
-			library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
+			library.reexportDeps(library.baseCompiler.aidlOrderOnlyDeps...)
+			library.addExportedGeneratedHeaders(library.baseCompiler.aidlHeaders...)
 		}
 	}
 
@@ -1329,9 +1328,8 @@
 			includes = append(includes, flags.proto.Dir)
 			library.reexportDirs(includes...)
 
-			// TODO: restrict to proto deps
-			library.reexportDeps(library.baseCompiler.pathDeps...)
-			library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
+			library.reexportDeps(library.baseCompiler.protoOrderOnlyDeps...)
+			library.addExportedGeneratedHeaders(library.baseCompiler.protoHeaders...)
 		}
 	}
 
@@ -1350,10 +1348,16 @@
 			}
 		}
 
+		// Make sure to only export headers which are within the include directory.
+		_, headers := android.FilterPathListPredicate(library.baseCompiler.syspropHeaders, func(path android.Path) bool {
+			_, isRel := android.MaybeRel(ctx, dir.String(), path.String())
+			return isRel
+		})
+
 		// Add sysprop-related directories to the exported directories of this library.
 		library.reexportDirs(dir)
-		library.reexportDeps(library.baseCompiler.pathDeps...)
-		library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
+		library.reexportDeps(library.baseCompiler.syspropOrderOnlyDeps...)
+		library.addExportedGeneratedHeaders(headers...)
 	}
 
 	// Add stub-related flags if this library is a stub library.
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 8b3dbeb..dc851a5 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -14,13 +14,18 @@
 
 package cc
 
-import "android/soong/android"
+import (
+	"android/soong/android"
+	"android/soong/bazel"
+)
 
 func init() {
 	RegisterLibraryHeadersBuildComponents(android.InitRegistrationContext)
 
 	// Register sdk member types.
 	android.RegisterSdkMemberType(headersLibrarySdkMemberType)
+
+	android.RegisterBp2BuildMutator("cc_library_headers", CcLibraryHeadersBp2Build)
 }
 
 var headersLibrarySdkMemberType = &librarySdkMemberType{
@@ -55,3 +60,85 @@
 	library.HeaderOnly()
 	return module.Init()
 }
+
+type bazelCcLibraryHeadersAttributes struct {
+	Hdrs     bazel.LabelList
+	Includes bazel.LabelList
+	Deps     bazel.LabelList
+}
+
+type bazelCcLibraryHeaders struct {
+	android.BazelTargetModuleBase
+	bazelCcLibraryHeadersAttributes
+}
+
+func BazelCcLibraryHeadersFactory() android.Module {
+	module := &bazelCcLibraryHeaders{}
+	module.AddProperties(&module.bazelCcLibraryHeadersAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		// Not a cc module
+		return
+	}
+
+	if !module.ConvertWithBp2build() {
+		return
+	}
+
+	lib, ok := module.linker.(*libraryDecorator)
+	if !ok {
+		// Not a cc_library module
+		return
+	}
+	if !lib.header() {
+		// Not a cc_library_headers module
+		return
+	}
+
+	// list of directories that will be added to the include path (using -I) for this
+	// module and any module that links against this module.
+	includeDirs := lib.flagExporter.Properties.Export_system_include_dirs
+	includeDirs = append(includeDirs, lib.flagExporter.Properties.Export_include_dirs...)
+	includeDirLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
+
+	var includeDirGlobs []string
+	for _, includeDir := range includeDirs {
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
+	}
+
+	headerLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
+
+	// list of modules that should only provide headers for this module.
+	var headerLibs []string
+	for _, linkerProps := range lib.linkerProps() {
+		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
+			headerLibs = baseLinkerProps.Export_header_lib_headers
+			break
+		}
+	}
+	headerLibLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+
+	attrs := &bazelCcLibraryHeadersAttributes{
+		Includes: includeDirLabels,
+		Hdrs:     headerLabels,
+		Deps:     headerLibLabels,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library_headers",
+		Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelCcLibraryHeadersFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelCcLibraryHeaders) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelCcLibraryHeaders) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 1402991..d5f2adf 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -206,22 +206,22 @@
 		dirs:         true,
 	},
 	{
-		// exportedGeneratedIncludeDirs lists directories that contains some header files
-		// that are explicitly listed in the exportedGeneratedHeaders property. So, the contents
+		// ExportedGeneratedIncludeDirs lists directories that contains some header files
+		// that are explicitly listed in the ExportedGeneratedHeaders property. So, the contents
 		// of these directories do not need to be copied, but these directories do need adding to
 		// the export_include_dirs property in the prebuilt module in the snapshot.
-		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs },
+		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedGeneratedIncludeDirs },
 		propertyName: "export_include_dirs",
 		snapshotDir:  nativeGeneratedIncludeDir,
 		copy:         false,
 		dirs:         true,
 	},
 	{
-		// exportedGeneratedHeaders lists header files that are in one of the directories
-		// specified in exportedGeneratedIncludeDirs must be copied into the snapshot.
-		// As they are in a directory in exportedGeneratedIncludeDirs they do not need adding to a
+		// ExportedGeneratedHeaders lists header files that are in one of the directories
+		// specified in ExportedGeneratedIncludeDirs must be copied into the snapshot.
+		// As they are in a directory in ExportedGeneratedIncludeDirs they do not need adding to a
 		// property in the prebuilt module in the snapshot.
-		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders },
+		pathsGetter:  func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedGeneratedHeaders },
 		propertyName: "",
 		snapshotDir:  nativeGeneratedIncludeDir,
 		copy:         true,
@@ -258,42 +258,52 @@
 	// values where necessary.
 	for _, propertyInfo := range includeDirProperties {
 		// Calculate the base directory in the snapshot into which the files will be copied.
-		// lib.ArchType is "" for common properties.
+		// lib.archType is "" for common properties.
 		targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir)
 
 		propertyName := propertyInfo.propertyName
 
 		// Iterate over each path in one of the include directory properties.
 		for _, path := range propertyInfo.pathsGetter(libInfo) {
+			inputPath := path.String()
+
+			// Map the input path to a snapshot relative path. The mapping is independent of the module
+			// that references them so that if multiple modules within the same snapshot export the same
+			// header files they end up in the same place in the snapshot and so do not get duplicated.
+			targetRelativePath := inputPath
+			if isGeneratedHeaderDirectory(path) {
+				// Remove everything up to the .intermediates/ from the generated output directory to
+				// leave a module relative path.
+				base := android.PathForIntermediates(sdkModuleContext, "")
+				targetRelativePath = android.Rel(sdkModuleContext, base.String(), inputPath)
+			}
+
+			snapshotRelativePath := filepath.Join(targetDir, targetRelativePath)
 
 			// Copy the files/directories when necessary.
 			if propertyInfo.copy {
 				if propertyInfo.dirs {
 					// When copying a directory glob and copy all the headers within it.
 					// TODO(jiyong) copy headers having other suffixes
-					headers, _ := sdkModuleContext.GlobWithDeps(path.String()+"/**/*.h", nil)
+					headers, _ := sdkModuleContext.GlobWithDeps(inputPath+"/**/*.h", nil)
 					for _, file := range headers {
 						src := android.PathForSource(sdkModuleContext, file)
-						dest := filepath.Join(targetDir, file)
+
+						// The destination path in the snapshot is constructed from the snapshot relative path
+						// of the input directory and the input directory relative path of the header file.
+						inputRelativePath := android.Rel(sdkModuleContext, inputPath, file)
+						dest := filepath.Join(snapshotRelativePath, inputRelativePath)
 						builder.CopyToSnapshot(src, dest)
 					}
 				} else {
-					// Otherwise, just copy the files.
-					dest := filepath.Join(targetDir, libInfo.name, path.Rel())
-					builder.CopyToSnapshot(path, dest)
+					// Otherwise, just copy the file to its snapshot relative path.
+					builder.CopyToSnapshot(path, snapshotRelativePath)
 				}
 			}
 
 			// Only directories are added to a property.
 			if propertyInfo.dirs {
-				var snapshotPath string
-				if isGeneratedHeaderDirectory(path) {
-					snapshotPath = filepath.Join(targetDir, libInfo.name)
-				} else {
-					snapshotPath = filepath.Join(targetDir, path.String())
-				}
-
-				includeDirs[propertyName] = append(includeDirs[propertyName], snapshotPath)
+				includeDirs[propertyName] = append(includeDirs[propertyName], snapshotRelativePath)
 			}
 		}
 	}
@@ -330,9 +340,6 @@
 
 	memberType *librarySdkMemberType
 
-	// The name of the library, is not exported as this must not be changed during optimization.
-	name string
-
 	// archType is not exported as if set (to a non default value) it is always arch specific.
 	// This is "" for common properties.
 	archType string
@@ -344,13 +351,13 @@
 
 	// The list of arch specific exported generated include dirs.
 	//
-	// This field is not exported as its contents are always arch specific.
-	exportedGeneratedIncludeDirs android.Paths
+	// This field is exported as its contents may not be arch specific, e.g. protos.
+	ExportedGeneratedIncludeDirs android.Paths `android:"arch_variant"`
 
 	// The list of arch specific exported generated header files.
 	//
-	// This field is not exported as its contents are is always arch specific.
-	exportedGeneratedHeaders android.Paths
+	// This field is exported as its contents may not be arch specific, e.g. protos.
+	ExportedGeneratedHeaders android.Paths `android:"arch_variant"`
 
 	// The list of possibly common exported system include dirs.
 	//
@@ -419,12 +426,11 @@
 	exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
 		exportedInfo.IncludeDirs, isGeneratedHeaderDirectory)
 
-	p.name = variant.Name()
 	p.archType = ccModule.Target().Arch.ArchType.String()
 
 	// Make sure that the include directories are unique.
 	p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
-	p.exportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs)
+	p.ExportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs)
 
 	// Take a copy before filtering out duplicates to avoid changing the slice owned by the
 	// ccModule.
@@ -450,7 +456,7 @@
 		}
 		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
 	}
-	p.exportedGeneratedHeaders = exportedInfo.GeneratedHeaders
+	p.ExportedGeneratedHeaders = exportedInfo.GeneratedHeaders
 
 	if !p.memberType.noOutputFiles && addOutputFile {
 		p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
diff --git a/cc/object.go b/cc/object.go
index 3ce7676..126bd65 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -18,6 +18,7 @@
 	"fmt"
 
 	"android/soong/android"
+	"android/soong/bazel"
 )
 
 //
@@ -27,6 +28,8 @@
 func init() {
 	android.RegisterModuleType("cc_object", ObjectFactory)
 	android.RegisterSdkMemberType(ccObjectSdkMemberType)
+
+	android.RegisterBp2BuildMutator("cc_object", ObjectBp2Build)
 }
 
 var ccObjectSdkMemberType = &librarySdkMemberType{
@@ -43,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"`
@@ -77,14 +100,104 @@
 		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"})
 
 	module.sdkMemberTypes = []android.SdkMemberType{ccObjectSdkMemberType}
+
 	return module.Init()
 }
 
+// For bp2build conversion.
+type bazelObjectAttributes struct {
+	Srcs               bazel.LabelList
+	Deps               bazel.LabelList
+	Copts              bazel.StringListAttribute
+	Local_include_dirs []string
+}
+
+type bazelObject struct {
+	android.BazelTargetModuleBase
+	bazelObjectAttributes
+}
+
+func (m *bazelObject) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelObject) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func BazelObjectFactory() android.Module {
+	module := &bazelObject{}
+	module.AddProperties(&module.bazelObjectAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+// ObjectBp2Build is the bp2build converter from cc_object modules to the
+// Bazel equivalent target, plus any necessary include deps for the cc_object.
+func ObjectBp2Build(ctx android.TopDownMutatorContext) {
+	m, ok := ctx.Module().(*Module)
+	if !ok || !m.ConvertWithBp2build() {
+		return
+	}
+
+	// a Module can be something other than a cc_object.
+	if ctx.ModuleType() != "cc_object" {
+		return
+	}
+
+	if m.compiler == nil {
+		// a cc_object must have access to the compiler decorator for its props.
+		ctx.ModuleErrorf("compiler must not be nil for a cc_object module")
+	}
+
+	// Set arch-specific configurable attributes
+	var copts bazel.StringListAttribute
+	var srcs []string
+	var excludeSrcs []string
+	var localIncludeDirs []string
+	for _, props := range m.compiler.compilerProps() {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			copts.Value = baseCompilerProps.Cflags
+			srcs = baseCompilerProps.Srcs
+			excludeSrcs = baseCompilerProps.Exclude_srcs
+			localIncludeDirs = baseCompilerProps.Local_include_dirs
+			break
+		}
+	}
+
+	var deps bazel.LabelList
+	for _, props := range m.linker.linkerProps() {
+		if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
+			deps = android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs)
+		}
+	}
+
+	for arch, p := range m.GetArchProperties(&BaseCompilerProperties{}) {
+		if cProps, ok := p.(*BaseCompilerProperties); ok {
+			copts.SetValueForArch(arch.Name, cProps.Cflags)
+		}
+	}
+	copts.SetValueForArch("default", []string{})
+
+	attrs := &bazelObjectAttributes{
+		Srcs:               android.BazelLabelForModuleSrcExcludes(ctx, srcs, excludeSrcs),
+		Deps:               deps,
+		Copts:              copts,
+		Local_include_dirs: localIncludeDirs,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_object",
+		Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelObjectFactory, m.Name(), props, attrs)
+}
+
 func (object *objectLinker) appendLdflags(flags []string) {
 	panic(fmt.Errorf("appendLdflags on objectLinker not supported"))
 }
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/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 62daafd..f9aea0c 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -280,50 +280,36 @@
 	// Nothing, the snapshot module is only used to forward dependency information in DepsMutator.
 }
 
+func getSnapshotNameSuffix(moduleSuffix, version, arch string) string {
+	versionSuffix := version
+	if arch != "" {
+		versionSuffix += "." + arch
+	}
+	return moduleSuffix + versionSuffix
+}
+
 func (s *snapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
-	collectSnapshotMap := func(variations []blueprint.Variation, depTag blueprint.DependencyTag,
-		names []string, snapshotSuffix, moduleSuffix string) map[string]string {
-
-		decoratedNames := make([]string, 0, len(names))
-		for _, name := range names {
-			decoratedNames = append(decoratedNames, name+
-				snapshotSuffix+moduleSuffix+
-				s.baseSnapshot.version()+
-				"."+ctx.Arch().ArchType.Name)
-		}
-
-		deps := ctx.AddVariationDependencies(variations, depTag, decoratedNames...)
+	collectSnapshotMap := func(names []string, snapshotSuffix, moduleSuffix string) map[string]string {
 		snapshotMap := make(map[string]string)
-		for _, dep := range deps {
-			if dep == nil {
-				continue
-			}
-
-			snapshotMap[dep.(*Module).BaseModuleName()] = ctx.OtherModuleName(dep)
+		for _, name := range names {
+			snapshotMap[name] = name +
+				getSnapshotNameSuffix(snapshotSuffix+moduleSuffix,
+					s.baseSnapshot.version(), ctx.Arch().ArchType.Name)
 		}
 		return snapshotMap
 	}
 
 	snapshotSuffix := s.image.moduleNameSuffix()
-	headers := collectSnapshotMap(nil, HeaderDepTag(), s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix)
-	binaries := collectSnapshotMap(nil, nil, s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix)
-	objects := collectSnapshotMap(nil, nil, s.properties.Objects, snapshotSuffix, snapshotObjectSuffix)
-
-	staticLibs := collectSnapshotMap([]blueprint.Variation{
-		{Mutator: "link", Variation: "static"},
-	}, StaticDepTag(), s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix)
-
-	sharedLibs := collectSnapshotMap([]blueprint.Variation{
-		{Mutator: "link", Variation: "shared"},
-	}, SharedDepTag(), s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix)
-
-	vndkLibs := collectSnapshotMap([]blueprint.Variation{
-		{Mutator: "link", Variation: "shared"},
-	}, SharedDepTag(), s.properties.Vndk_libs, "", vndkSuffix)
-
+	headers := collectSnapshotMap(s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix)
+	binaries := collectSnapshotMap(s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix)
+	objects := collectSnapshotMap(s.properties.Objects, snapshotSuffix, snapshotObjectSuffix)
+	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix)
+	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix)
+	vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix)
 	for k, v := range vndkLibs {
 		sharedLibs[k] = v
 	}
+
 	ctx.SetProvider(SnapshotInfoProvider, SnapshotInfo{
 		HeaderLibs: headers,
 		Binaries:   binaries,
@@ -395,12 +381,7 @@
 }
 
 func (p *baseSnapshotDecorator) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-
-	return p.baseProperties.ModuleSuffix + versionSuffix
+	return getSnapshotNameSuffix(p.moduleSuffix(), p.version(), p.arch())
 }
 
 func (p *baseSnapshotDecorator) version() string {
diff --git a/cc/test.go b/cc/test.go
index 17ac534..9b77e45 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -87,14 +87,10 @@
 	// Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
 	Disable_framework *bool
 
-	// Add MinApiLevelModuleController to auto generated test config. If the device property of
-	// "ro.product.first_api_level" < Test_min_api_level, then skip this module.
+	// Add ShippingApiLevelModuleController to auto generated test config. If the device properties
+	// for the shipping api level is less than the test_min_api_level, skip this module.
 	Test_min_api_level *int64
 
-	// Add MinApiLevelModuleController to auto generated test config. If the device property of
-	// "ro.build.version.sdk" < Test_min_sdk_version, then skip this module.
-	Test_min_sdk_version *int64
-
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
@@ -232,7 +228,6 @@
 type testDecorator struct {
 	Properties TestProperties
 	linker     *baseLinker
-	hod        android.HostOrDeviceSupported
 }
 
 func (test *testDecorator) gtest() bool {
@@ -376,9 +371,7 @@
 		}
 	})
 
-	var apiLevelProp string
 	var configs []tradefed.Config
-	var minLevel string
 	for _, module := range test.Properties.Test_mainline_modules {
 		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
 	}
@@ -402,20 +395,10 @@
 	for _, tag := range test.Properties.Test_options.Test_suite_tag {
 		configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
 	}
-	if test.Properties.Test_min_api_level != nil && test.Properties.Test_min_sdk_version != nil {
-		ctx.PropertyErrorf("test_min_api_level", "'test_min_api_level' and 'test_min_sdk_version' should not be set at the same time.")
-	} else if test.Properties.Test_min_api_level != nil {
-		apiLevelProp = "ro.product.first_api_level"
-		minLevel = strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)
-	} else if test.Properties.Test_min_sdk_version != nil {
-		apiLevelProp = "ro.build.version.sdk"
-		minLevel = strconv.FormatInt(int64(*test.Properties.Test_min_sdk_version), 10)
-	}
-	if apiLevelProp != "" {
+	if test.Properties.Test_min_api_level != nil {
 		var options []tradefed.Option
-		options = append(options, tradefed.Option{Name: "min-api-level", Value: minLevel})
-		options = append(options, tradefed.Option{Name: "api-level-prop", Value: apiLevelProp})
-		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
+		options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)})
+		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
 	}
 
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
@@ -432,8 +415,7 @@
 		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
 	}
 
-	// TODO(179092189): Clean up to use Ctx.Host() when generalizing to cc_test
-	if test.testDecorator.hod == android.HostSupported && test.gtest() && test.Properties.Test_options.Unit_test == nil {
+	if ctx.Host() && test.gtest() && test.Properties.Test_options.Unit_test == nil {
 		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
 	test.binaryDecorator.baseInstaller.install(ctx, file)
@@ -447,7 +429,6 @@
 	test := &testBinary{
 		testDecorator: testDecorator{
 			linker: binary.baseLinker,
-			hod:    hod,
 		},
 		binaryDecorator: binary,
 		baseCompiler:    NewBaseCompiler(),
diff --git a/cc/testing.go b/cc/testing.go
index 45e5312..f62c5f1 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)
@@ -591,6 +590,7 @@
 
 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)
diff --git a/cc/tidy.go b/cc/tidy.go
index 972ad7b..616cf8a 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -20,6 +20,7 @@
 
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/android"
 	"android/soong/cc/config"
 )
 
@@ -41,6 +42,21 @@
 	Properties TidyProperties
 }
 
+var quotedFlagRegexp, _ = regexp.Compile(`^-?-[^=]+=('|").*('|")$`)
+
+// When passing flag -name=value, if user add quotes around 'value',
+// the quotation marks will be preserved by NinjaAndShellEscapeList
+// and the 'value' string with quotes won't work like the intended value.
+// So here we report an error if -*='*' is found.
+func checkNinjaAndShellEscapeList(ctx ModuleContext, prop string, slice []string) []string {
+	for _, s := range slice {
+		if quotedFlagRegexp.MatchString(s) {
+			ctx.PropertyErrorf(prop, "Extra quotes in: %s", s)
+		}
+	}
+	return proptools.NinjaAndShellEscapeList(slice)
+}
+
 func (tidy *tidyFeature) props() []interface{} {
 	return []interface{}{&tidy.Properties}
 }
@@ -73,11 +89,19 @@
 	if len(withTidyFlags) > 0 {
 		flags.TidyFlags = append(flags.TidyFlags, withTidyFlags)
 	}
-	esc := proptools.NinjaAndShellEscapeList
-	flags.TidyFlags = append(flags.TidyFlags, esc(tidy.Properties.Tidy_flags)...)
-	// If TidyFlags is empty, add default header filter.
-	if len(flags.TidyFlags) == 0 {
-		headerFilter := "-header-filter=\"(" + ctx.ModuleDir() + "|${config.TidyDefaultHeaderDirs})\""
+	esc := checkNinjaAndShellEscapeList
+	flags.TidyFlags = append(flags.TidyFlags, esc(ctx, "tidy_flags", tidy.Properties.Tidy_flags)...)
+	// If TidyFlags does not contain -header-filter, add default header filter.
+	// Find the substring because the flag could also appear as --header-filter=...
+	// and with or without single or double quotes.
+	if !android.SubstringInList(flags.TidyFlags, "-header-filter=") {
+		defaultDirs := ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS")
+		headerFilter := "-header-filter="
+		if defaultDirs == "" {
+			headerFilter += ctx.ModuleDir() + "/"
+		} else {
+			headerFilter += "\"(" + ctx.ModuleDir() + "/|" + defaultDirs + ")\""
+		}
 		flags.TidyFlags = append(flags.TidyFlags, headerFilter)
 	}
 
@@ -110,7 +134,7 @@
 		tidyChecks += config.TidyChecksForDir(ctx.ModuleDir())
 	}
 	if len(tidy.Properties.Tidy_checks) > 0 {
-		tidyChecks = tidyChecks + "," + strings.Join(esc(
+		tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
 			config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",")
 	}
 	if ctx.Windows() {
@@ -156,7 +180,7 @@
 			flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
 		}
 	} else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
-		tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
+		tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",")
 		flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
 	}
 	return flags
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 7e283fb..0833277 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -416,7 +416,13 @@
 		name: "libvendor",
 		version: "BOARD",
 		target_arch: "arm64",
+		compile_multilib: "64",
 		vendor: true,
+		shared_libs: [
+			"libvendor_without_snapshot",
+			"libvendor_available",
+			"libvndk",
+		],
 		arch: {
 			arm64: {
 				src: "libvendor.so",
diff --git a/cc/vndk.go b/cc/vndk.go
index ff38db5..85028d0 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -561,7 +561,7 @@
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(txt.outputFile),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
 			},
 		},
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 c17e23d..d022f49 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,11 +29,19 @@
 )
 
 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")
 }
@@ -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"))
@@ -134,7 +148,9 @@
 
 	// Convert the Soong module graph into Bazel BUILD files.
 	if bazelQueryViewDir != "" {
-		if err := createBazelQueryView(ctx, bazelQueryViewDir); err != nil {
+		// 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 {
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
 		}
@@ -188,9 +204,15 @@
 	// from the regular Modules.
 	bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
 
-	// Run the code-generation phase to convert BazelTargetModules to BUILD files.
+	// Run the code-generation phase to convert BazelTargetModules to BUILD files
+	// and print conversion metrics to the user.
 	codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
-	bp2build.Codegen(codegenContext)
+	metrics := bp2build.Codegen(codegenContext)
+
+	// Only report metrics when in bp2build mode. The metrics aren't relevant
+	// for queryview, since that's a total repo-wide conversion and there's a
+	// 1:1 mapping for each module.
+	metrics.Print()
 
 	// Workarounds to support running bp2build in a clean AOSP checkout with no
 	// prior builds, and exiting early as soon as the BUILD files get generated,
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index dc0b323..0a77d67 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -22,9 +22,12 @@
 	"path/filepath"
 )
 
-func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
+func createBazelQueryView(ctx bp2build.CodegenContext, bazelQueryViewDir string) error {
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
-	buildToTargets := bp2build.GenerateBazelTargets(*ctx, bp2build.QueryView)
+
+	// Ignore metrics reporting for queryview, since queryview is already a full-repo
+	// conversion and can use data from bazel query directly.
+	buildToTargets, _ := bp2build.GenerateBazelTargets(ctx)
 
 	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
 	for _, f := range filesToWrite {
diff --git a/cmd/soong_env/Android.bp b/cmd/soong_env/Android.bp
deleted file mode 100644
index ad717d0..0000000
--- a/cmd/soong_env/Android.bp
+++ /dev/null
@@ -1,28 +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.
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_binary {
-    name: "soong_env",
-    deps: [
-        "soong-env",
-    ],
-    srcs: [
-        "soong_env.go",
-    ],
-    default: true,
-}
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 d55204b..888466a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -84,6 +84,15 @@
 	BootFlags         string        // extra flags to pass to dex2oat for the boot image
 	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 "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
+	// modules that need to add <uses-library> information in their build files. The flag allows to
+	// quickly silence build errors. This flag should be used with caution and only as a temporary
+	// measure, as it masks real errors and affects performance.
+	RelaxUsesLibraryCheck bool
 }
 
 // GlobalSoongConfig contains the global config that is generated from Soong,
@@ -113,9 +122,10 @@
 	ProfileIsTextListing bool
 	ProfileBootListing   android.OptionalPath
 
-	EnforceUsesLibraries bool
-	ProvidesUsesLibrary  string // the name of the <uses-library> (usually the same as its module)
-	ClassLoaderContexts  ClassLoaderContextMap
+	EnforceUsesLibraries           bool         // turn on build-time verify_uses_libraries check
+	EnforceUsesLibrariesStatusFile android.Path // a file with verify_uses_libraries errors (if any)
+	ProvidesUsesLibrary            string       // library name (usually the same as module name)
+	ClassLoaderContexts            ClassLoaderContextMap
 
 	Archs                   []android.ArchType
 	DexPreoptImages         []android.Path
@@ -258,14 +268,15 @@
 
 		// Copies of entries in ModuleConfig that are not constructable without extra parameters.  They will be
 		// used to construct the real value manually below.
-		BuildPath                   string
-		DexPath                     string
-		ManifestPath                string
-		ProfileClassListing         string
-		ClassLoaderContexts         jsonClassLoaderContextMap
-		DexPreoptImages             []string
-		DexPreoptImageLocations     []string
-		PreoptBootClassPathDexFiles []string
+		BuildPath                      string
+		DexPath                        string
+		ManifestPath                   string
+		ProfileClassListing            string
+		EnforceUsesLibrariesStatusFile string
+		ClassLoaderContexts            jsonClassLoaderContextMap
+		DexPreoptImages                []string
+		DexPreoptImageLocations        []string
+		PreoptBootClassPathDexFiles    []string
 	}
 
 	config := ModuleJSONConfig{}
@@ -280,6 +291,7 @@
 	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
 	config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
 	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
+	config.ModuleConfig.EnforceUsesLibrariesStatusFile = constructPath(ctx, config.EnforceUsesLibrariesStatusFile)
 	config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts)
 	config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
 	config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e17d756..fdb00bd 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -279,11 +279,12 @@
 
 		// Generate command that saves host and target class loader context in shell variables.
 		clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
-		cmd := rule.Command().
-			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
+		rule.Command().
+			Text("if ! test -s ").Input(module.EnforceUsesLibrariesStatusFile).
+			Text(` ; then eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`).
-			Text(clc).Implicits(paths)
-		cmd.Text(`)"`)
+			Text(clc).Implicits(paths).
+			Text(`)" ; fi`)
 
 	} else {
 		// Other libraries or APKs for which the exact <uses-library> list is unknown.
@@ -363,7 +364,16 @@
 		} else {
 			compilerFilter = "quicken"
 		}
-		cmd.FlagWithArg("--compiler-filter=", compilerFilter)
+		if module.EnforceUsesLibraries {
+			// If the verify_uses_libraries check failed (in this case status file contains a
+			// 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 verify ; else echo " + compilerFilter + " ; fi)")
+		} else {
+			cmd.FlagWithArg("--compiler-filter=", compilerFilter)
+		}
 	}
 
 	if generateDM {
@@ -539,6 +549,12 @@
 	}
 }
 
+// Returns path to a file containing the reult of verify_uses_libraries check (empty if the check
+// has succeeded, or an error message if it failed).
+func UsesLibrariesStatusFile(ctx android.ModuleContext) android.WritablePath {
+	return android.PathForModuleOut(ctx, "enforce_uses_libraries.status")
+}
+
 func contains(l []string, s string) bool {
 	for _, e := range l {
 		if e == s {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index af73d0c..12df36b 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -43,6 +43,7 @@
 		PreoptFlags:                     nil,
 		ProfileClassListing:             android.OptionalPath{},
 		ProfileIsTextListing:            false,
+		EnforceUsesLibrariesStatusFile:  android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)),
 		EnforceUsesLibraries:            false,
 		ClassLoaderContexts:             nil,
 		Archs:                           []android.ArchType{android.Arm},
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 850c8f9..b07ad91 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -28,6 +28,8 @@
 // various `prebuilt_*` mutators.
 
 import (
+	"fmt"
+
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -208,6 +210,17 @@
 	return p.outputFilePath
 }
 
+var _ android.OutputFileProducer = (*PrebuiltEtc)(nil)
+
+func (p *PrebuiltEtc) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.outputFilePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (p *PrebuiltEtc) SubDir() string {
 	if subDir := proptools.String(p.properties.Sub_dir); subDir != "" {
 		return subDir
@@ -269,11 +282,14 @@
 		Input:  p.sourceFilePath,
 	})
 
-	if p.Installable() {
-		installPath := ctx.InstallFile(p.installDirPath, p.outputFilePath.Base(), p.outputFilePath)
-		for _, sl := range p.properties.Symlinks {
-			ctx.InstallSymlink(p.installDirPath, sl, installPath)
-		}
+	if !p.Installable() {
+		p.SkipInstall()
+	}
+
+	// Call InstallFile even when uninstallable to make the module included in the package
+	installPath := ctx.InstallFile(p.installDirPath, p.outputFilePath.Base(), p.outputFilePath)
+	for _, sl := range p.properties.Symlinks {
+		ctx.InstallSymlink(p.installDirPath, sl, installPath)
 	}
 }
 
@@ -293,7 +309,7 @@
 		SubName:    nameSuffix,
 		OutputFile: android.OptionalPathForPath(p.outputFilePath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
 				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 6c4c0b6..585760d 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -175,7 +175,7 @@
 }
 
 func TestPrebuiltEtcAndroidMk(t *testing.T) {
-	ctx, config := testPrebuiltEtc(t, `
+	ctx, _ := testPrebuiltEtc(t, `
 		prebuilt_etc {
 			name: "foo",
 			src: "foo.conf",
@@ -198,7 +198,7 @@
 	}
 
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
 			if !reflect.DeepEqual(value, expectedValue) {
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 42a4c88..dcdbdcf 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -13,6 +13,7 @@
     srcs: [
         "bootimg.go",
         "filesystem.go",
+        "logical_partition.go",
     ],
     testSrcs: [
     ],
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 4bc1823..764f045 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -38,6 +38,9 @@
 }
 
 type bootimgProperties struct {
+	// Set the name of the output. Defaults to <module_name>.img.
+	Stem *string
+
 	// Path to the linux kernel prebuilt file
 	Kernel_prebuilt *string `android:"arch_variant,path"`
 
@@ -96,7 +99,7 @@
 }
 
 func (b *bootimg) installFileName() string {
-	return b.BaseModuleName() + ".img"
+	return proptools.StringDefault(b.properties.Stem, b.BaseModuleName()+".img")
 }
 
 func (b *bootimg) partitionName() string {
@@ -104,13 +107,8 @@
 }
 
 func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	var unsignedOutput android.OutputPath
-	if proptools.Bool(b.properties.Vendor_boot) {
-		unsignedOutput = b.buildVendorBootImage(ctx)
-	} else {
-		// TODO(jiyong): fix this
-		ctx.PropertyErrorf("vendor_boot", "only vendor_boot:true is supported")
-	}
+	vendor := proptools.Bool(b.properties.Vendor_boot)
+	unsignedOutput := b.buildBootImage(ctx, vendor)
 
 	if proptools.Bool(b.properties.Use_avb) {
 		b.output = b.signImage(ctx, unsignedOutput)
@@ -122,16 +120,24 @@
 	ctx.InstallFile(b.installDir, b.installFileName(), b.output)
 }
 
-func (b *bootimg) buildVendorBootImage(ctx android.ModuleContext) android.OutputPath {
-	output := android.PathForModuleOut(ctx, "unsigned.img").OutputPath
+func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath {
+	output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()).OutputPath
+
 	builder := android.NewRuleBuilder(pctx, ctx)
 	cmd := builder.Command().BuiltTool("mkbootimg")
 
-	kernel := android.OptionalPathForModuleSrc(ctx, b.properties.Kernel_prebuilt)
-	if kernel.Valid() {
+	kernel := proptools.String(b.properties.Kernel_prebuilt)
+	if vendor && kernel != "" {
 		ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
 		return output
 	}
+	if !vendor && kernel == "" {
+		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
+		return output
+	}
+	if kernel != "" {
+		cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel))
+	}
 
 	dtbName := proptools.String(b.properties.Dtb_prebuilt)
 	if dtbName == "" {
@@ -143,7 +149,11 @@
 
 	cmdline := proptools.String(b.properties.Cmdline)
 	if cmdline != "" {
-		cmd.FlagWithArg("--vendor_cmdline ", "\""+cmdline+"\"")
+		flag := "--cmdline "
+		if vendor {
+			flag = "--vendor_cmdline "
+		}
+		cmd.FlagWithArg(flag, "\""+proptools.ShellEscape(cmdline)+"\"")
 	}
 
 	headerVersion := proptools.String(b.properties.Header_version)
@@ -169,34 +179,41 @@
 	}
 	ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
 	if filesystem, ok := ramdisk.(*filesystem); ok {
-		cmd.FlagWithInput("--vendor_ramdisk ", filesystem.OutputPath())
+		flag := "--ramdisk "
+		if vendor {
+			flag = "--vendor_ramdisk "
+		}
+		cmd.FlagWithInput(flag, filesystem.OutputPath())
 	} else {
 		ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
 		return output
 	}
 
-	cmd.FlagWithOutput("--vendor_boot ", output)
+	flag := "--output "
+	if vendor {
+		flag = "--vendor_boot "
+	}
+	cmd.FlagWithOutput(flag, output)
 
-	builder.Build("build_vendor_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName()))
+	builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName()))
 	return output
 }
 
 func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.OutputPath) android.OutputPath {
-	signedImage := android.PathForModuleOut(ctx, "signed.img").OutputPath
+	output := android.PathForModuleOut(ctx, b.installFileName()).OutputPath
 	key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
 
 	builder := android.NewRuleBuilder(pctx, ctx)
-	builder.Command().Text("cp").Input(unsignedImage).Output(signedImage)
+	builder.Command().Text("cp").Input(unsignedImage).Output(output)
 	builder.Command().
 		BuiltTool("avbtool").
 		Flag("add_hash_footer").
 		FlagWithArg("--partition_name ", b.partitionName()).
 		FlagWithInput("--key ", key).
-		FlagWithOutput("--image ", signedImage)
+		FlagWithOutput("--image ", output)
 
 	builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName()))
-
-	return signedImage
+	return output
 }
 
 var _ android.AndroidMkEntriesProvider = (*bootimg)(nil)
@@ -207,7 +224,7 @@
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(b.output),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", b.installDir.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName())
 			},
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 7658154..3b0a7ae 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"path/filepath"
+	"strings"
 
 	"android/soong/android"
 
@@ -37,6 +39,11 @@
 	installDir android.InstallPath
 }
 
+type symlinkDefinition struct {
+	Target *string
+	Name   *string
+}
+
 type filesystemProperties struct {
 	// When set to true, sign the image with avbtool. Default is false.
 	Use_avb *bool
@@ -54,6 +61,16 @@
 
 	// file_contexts file to make image. Currently, only ext4 is supported.
 	File_contexts *string `android:"path"`
+
+	// Base directory relative to root, to which deps are installed, e.g. "system". Default is "."
+	// (root).
+	Base_dir *string
+
+	// Directories to be created under root. e.g. /dev, /proc, etc.
+	Dirs []string
+
+	// Symbolic links to be created under root with "ln -sf <target> <name>".
+	Symlinks []symlinkDefinition
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -124,16 +141,75 @@
 	ctx.InstallFile(f.installDir, f.installFileName(), f.output)
 }
 
+// root zip will contain stuffs like dirs or symlinks.
+func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath {
+	rootDir := android.PathForModuleGen(ctx, "root").OutputPath
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("rm -rf").Text(rootDir.String())
+	builder.Command().Text("mkdir -p").Text(rootDir.String())
+
+	// create dirs and symlinks
+	for _, dir := range f.properties.Dirs {
+		// OutputPath.Join verifies dir
+		builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
+	}
+
+	for _, symlink := range f.properties.Symlinks {
+		name := strings.TrimSpace(proptools.String(symlink.Name))
+		target := strings.TrimSpace(proptools.String(symlink.Target))
+
+		if name == "" {
+			ctx.PropertyErrorf("symlinks", "Name can't be empty")
+			continue
+		}
+
+		if target == "" {
+			ctx.PropertyErrorf("symlinks", "Target can't be empty")
+			continue
+		}
+
+		// OutputPath.Join verifies name. don't need to verify target.
+		dst := rootDir.Join(ctx, name)
+
+		builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
+		builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
+	}
+
+	zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath
+
+	builder.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", zipOut).
+		FlagWithArg("-C ", rootDir.String()).
+		Flag("-L 0"). // no compression because this will be unzipped soon
+		FlagWithArg("-D ", rootDir.String()).
+		Flag("-d") // include empty directories
+	builder.Command().Text("rm -rf").Text(rootDir.String())
+
+	builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName()))
+	return zipOut
+}
+
 func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
-	zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
-	f.CopyDepsToZip(ctx, zipFile)
+	depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
+	f.CopyDepsToZip(ctx, depsZipFile)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
+	rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
+	builder.Command().
+		BuiltTool("zip2zip").
+		FlagWithInput("-i ", depsZipFile).
+		FlagWithOutput("-o ", rebasedDepsZip).
+		Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
 
 	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
-	builder := android.NewRuleBuilder(pctx, ctx)
+	rootZip := f.buildRootZip(ctx)
 	builder.Command().
 		BuiltTool("zipsync").
 		FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
-		Input(zipFile)
+		Input(rootZip).
+		Input(rebasedDepsZip)
 
 	propFile, toolDeps := f.buildPropFile(ctx)
 	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
@@ -187,7 +263,7 @@
 	}
 
 	addStr("fs_type", fsTypeStr(f.fsType(ctx)))
-	addStr("mount_point", "system")
+	addStr("mount_point", "/")
 	addStr("use_dynamic_partition_size", "true")
 	addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs"))
 	// b/177813163 deps of the host tools have to be added. Remove this.
@@ -233,15 +309,25 @@
 		ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
 	}
 
-	zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
-	f.CopyDepsToZip(ctx, zipFile)
+	depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
+	f.CopyDepsToZip(ctx, depsZipFile)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
+	rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
+	builder.Command().
+		BuiltTool("zip2zip").
+		FlagWithInput("-i ", depsZipFile).
+		FlagWithOutput("-o ", rebasedDepsZip).
+		Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
 
 	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
-	builder := android.NewRuleBuilder(pctx, ctx)
+	rootZip := f.buildRootZip(ctx)
 	builder.Command().
 		BuiltTool("zipsync").
 		FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
-		Input(zipFile)
+		Input(rootZip).
+		Input(rebasedDepsZip)
 
 	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
 	cmd := builder.Command().
@@ -272,7 +358,7 @@
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(f.output),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
 			},
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
new file mode 100644
index 0000000..e547203
--- /dev/null
+++ b/filesystem/logical_partition.go
@@ -0,0 +1,210 @@
+// 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 filesystem
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("logical_partition", logicalPartitionFactory)
+}
+
+type logicalPartition struct {
+	android.ModuleBase
+
+	properties logicalPartitionProperties
+
+	output     android.OutputPath
+	installDir android.InstallPath
+}
+
+type logicalPartitionProperties struct {
+	// Set the name of the output. Defaults to <module_name>.img.
+	Stem *string
+
+	// Total size of the logical partition
+	Size *string
+
+	// List of groups. A group defines a fixed sized region. It can host one or more logical
+	// partitions and their total size is limited by the size of the group they are in.
+	Groups []groupProperties
+
+	// Whether the output is a sparse image or not. Default is false.
+	Sparse *bool
+}
+
+type groupProperties struct {
+	// Name of the partition group
+	Name *string
+
+	// Size of the partition group
+	Size *string
+
+	// List of logical partitions in this group
+	Partitions []partitionProperties
+}
+
+type partitionProperties struct {
+	// Name of the partition
+	Name *string
+
+	// Filesystem that is placed on the partition
+	Filesystem *string `android:"path"`
+}
+
+// logical_partition is a partition image which has one or more logical partitions in it.
+func logicalPartitionFactory() android.Module {
+	module := &logicalPartition{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// do nothing
+}
+
+func (l *logicalPartition) installFileName() string {
+	return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img")
+}
+
+func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	builder := android.NewRuleBuilder(pctx, ctx)
+
+	// Sparse the filesystem images and calculate their sizes
+	sparseImages := make(map[string]android.OutputPath)
+	sparseImageSizes := make(map[string]android.OutputPath)
+	for _, group := range l.properties.Groups {
+		for _, part := range group.Partitions {
+			sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
+			pName := proptools.String(part.Name)
+			sparseImages[pName] = sparseImg
+			sparseImageSizes[pName] = sizeTxt
+		}
+	}
+
+	cmd := builder.Command().BuiltTool("lpmake")
+
+	size := proptools.String(l.properties.Size)
+	if size == "" {
+		ctx.PropertyErrorf("size", "must be set")
+	}
+	if _, err := strconv.Atoi(size); err != nil {
+		ctx.PropertyErrorf("size", "must be a number")
+	}
+	cmd.FlagWithArg("--device-size=", size)
+
+	// TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots.
+	cmd.FlagWithArg("--metadata-slots=", "2")
+	cmd.FlagWithArg("--metadata-size=", "65536")
+
+	if proptools.Bool(l.properties.Sparse) {
+		cmd.Flag("--sparse")
+	}
+
+	groupNames := make(map[string]bool)
+	partitionNames := make(map[string]bool)
+
+	for _, group := range l.properties.Groups {
+		gName := proptools.String(group.Name)
+		if gName == "" {
+			ctx.PropertyErrorf("groups.name", "must be set")
+		}
+		if _, ok := groupNames[gName]; ok {
+			ctx.PropertyErrorf("group.name", "already exists")
+		} else {
+			groupNames[gName] = true
+		}
+		gSize := proptools.String(group.Size)
+		if gSize == "" {
+			ctx.PropertyErrorf("groups.size", "must be set")
+		}
+		if _, err := strconv.Atoi(gSize); err != nil {
+			ctx.PropertyErrorf("groups.size", "must be a number")
+		}
+		cmd.FlagWithArg("--group=", gName+":"+gSize)
+
+		for _, part := range group.Partitions {
+			pName := proptools.String(part.Name)
+			if pName == "" {
+				ctx.PropertyErrorf("groups.partitions.name", "must be set")
+			}
+			if _, ok := partitionNames[pName]; ok {
+				ctx.PropertyErrorf("groups.partitions.name", "already exists")
+			} else {
+				partitionNames[pName] = true
+			}
+			// Get size of the partition by reading the -size.txt file
+			pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+			cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
+			cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+		}
+	}
+
+	l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
+	cmd.FlagWithOutput("--output=", l.output)
+
+	builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName()))
+
+	l.installDir = android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+}
+
+// Add a rule that converts the filesystem for the given partition to the given rule builder. The
+// path to the sparse file and the text file having the size of the partition are returned.
+func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) {
+	img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem))
+	name := proptools.String(p.Name)
+	sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath
+
+	builder.Temporary(sparseImg)
+	builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg)
+
+	sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath
+	builder.Temporary(sizeTxt)
+	builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg).
+		Text("| ").Text("tr").FlagWithArg("-d ", "'\n'").
+		Text("> ").Output(sizeTxt)
+
+	return sparseImg, sizeTxt
+}
+
+var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(l.output),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName())
+			},
+		},
+	}}
+}
+
+var _ Filesystem = (*logicalPartition)(nil)
+
+func (l *logicalPartition) OutputPath() android.Path {
+	return l.output
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 9fa6c48..50c77cf 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -124,14 +124,12 @@
 
 	// input files to exclude
 	Exclude_srcs []string `android:"path,arch_variant"`
-
-	// Properties for Bazel migration purposes.
-	bazel.Properties
 }
 
 type Module struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.BazelModuleBase
 	android.ApexModuleBase
 
 	// For other packages to make their own genrules with extra
@@ -208,7 +206,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 {
@@ -519,7 +517,7 @@
 
 	g.outputFiles = outputFiles.Paths()
 
-	bazelModuleLabel := g.properties.Bazel_module.Label
+	bazelModuleLabel := g.GetBazelLabel()
 	bazelActionsUsed := false
 	if ctx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
 		bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
@@ -771,6 +769,7 @@
 	m := NewGenRule()
 	android.InitAndroidModule(m)
 	android.InitDefaultableModule(m)
+	android.InitBazelModule(m)
 	return m
 }
 
@@ -800,7 +799,7 @@
 
 func GenruleBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
-	if !ok || !m.properties.Bazel_module.Bp2build_available {
+	if !ok || !m.ConvertWithBp2build() {
 		return
 	}
 
@@ -853,10 +852,12 @@
 		Tools: tools,
 	}
 
-	props := bazel.NewBazelTargetModuleProperties(m.Name(), "genrule", "")
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class: "genrule",
+	}
 
 	// Create the BazelTargetModule.
-	ctx.CreateBazelTargetModule(BazelGenruleFactory, props, attrs)
+	ctx.CreateBazelTargetModule(BazelGenruleFactory, m.Name(), props, attrs)
 }
 
 func (m *bazelGenrule) Name() string {
diff --git a/java/Android.bp b/java/Android.bp
index 9bfd009..461b16d 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -58,7 +58,6 @@
         "sdk_library.go",
         "sdk_library_external.go",
         "support_libraries.go",
-        "sysprop.go",
         "system_modules.go",
         "testing.go",
         "tradefed.go",
diff --git a/java/aar.go b/java/aar.go
index ac7ae25..554ea67 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -160,8 +160,8 @@
 func (a *aapt) IsRROEnforced(ctx android.BaseModuleContext) bool {
 	// True if RRO is enforced for this module or...
 	return ctx.Config().EnforceRROForModule(ctx.ModuleName()) ||
-		// if RRO is enforced for any of its dependents, and this module is not exempted.
-		(a.aaptProperties.RROEnforcedForDependent && !ctx.Config().EnforceRROExemptedForModule(ctx.ModuleName()))
+		// if RRO is enforced for any of its dependents.
+		a.aaptProperties.RROEnforcedForDependent
 }
 
 func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext,
@@ -443,16 +443,14 @@
 					assets = append(assets, aarDep.ExportedAssets().Path())
 				}
 
-				if !ctx.Config().EnforceRROExemptedForModule(ctx.ModuleName()) {
-				outer:
-					for _, d := range aarDep.ExportedRRODirs() {
-						for _, e := range staticRRODirs {
-							if d.path == e.path {
-								continue outer
-							}
+			outer:
+				for _, d := range aarDep.ExportedRRODirs() {
+					for _, e := range staticRRODirs {
+						if d.path == e.path {
+							continue outer
 						}
-						staticRRODirs = append(staticRRODirs, d)
 					}
+					staticRRODirs = append(staticRRODirs, d)
 				}
 			}
 		}
@@ -807,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}
 }
@@ -827,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/androidmk.go b/java/androidmk.go
index 6e7c437..3d3eae5 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -41,7 +41,7 @@
 			Required:   library.deviceProperties.Target.Hostdex.Required,
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(entries *android.AndroidMkEntries) {
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetBool("LOCAL_IS_HOST_MODULE", true)
 					entries.SetPath("LOCAL_PREBUILT_MODULE_FILE", output)
 					if library.dexJarFile != nil {
@@ -74,7 +74,7 @@
 				OutputFile: android.OptionalPathForPath(checkedModulePaths[0]),
 				Include:    "$(BUILD_PHONY_PACKAGE)",
 				ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-					func(entries *android.AndroidMkEntries) {
+					func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", checkedModulePaths.Strings()...)
 					},
 				},
@@ -88,7 +88,7 @@
 			OutputFile: android.OptionalPathForPath(library.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(entries *android.AndroidMkEntries) {
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					if len(library.logtagsSrcs) > 0 {
 						var logtags []string
 						for _, l := range library.logtagsSrcs {
@@ -152,7 +152,7 @@
 func (j *Test) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := j.Library.AndroidMkEntries()
 	entries := &entriesList[0]
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		testSuiteComponent(entries, j.testProperties.Test_suites)
 		if j.testConfig != nil {
 			entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig)
@@ -180,7 +180,7 @@
 func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := j.Library.AndroidMkEntries()
 	entries := &entriesList[0]
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites)
 	})
 
@@ -198,7 +198,7 @@
 		OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
 				if prebuilt.dexJarFile != nil {
 					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
@@ -220,10 +220,10 @@
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
+		OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				if prebuilt.dexJarFile != nil {
 					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
 				}
@@ -247,7 +247,7 @@
 		OutputFile: android.OptionalPathForPath(prebuilt.classpathFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
@@ -269,7 +269,7 @@
 			OutputFile: android.OptionalPathForPath(binary.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(entries *android.AndroidMkEntries) {
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
 					if binary.dexJarFile != nil {
@@ -287,11 +287,18 @@
 			},
 		}}
 	} else {
+		outputFile := binary.wrapperFile
+		// Have Make installation trigger Soong installation by using Soong's install path as
+		// the output file.
+		if binary.Host() {
+			outputFile = binary.binaryFile
+		}
+
 		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Class:      "EXECUTABLES",
-			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
+			OutputFile: android.OptionalPathForPath(outputFile),
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(entries *android.AndroidMkEntries) {
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetBool("LOCAL_STRIP_MODULE", false)
 				},
 			},
@@ -317,7 +324,7 @@
 		OutputFile: android.OptionalPathForPath(app.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				// App module names can be overridden.
 				entries.SetString("LOCAL_MODULE", app.installApkName)
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall)
@@ -432,7 +439,7 @@
 func (a *AndroidTest) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := a.AndroidApp.AndroidMkEntries()
 	entries := &entriesList[0]
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		testSuiteComponent(entries, a.testProperties.Test_suites)
 		if a.testConfig != nil {
 			entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig)
@@ -448,7 +455,7 @@
 func (a *AndroidTestHelperApp) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := a.AndroidApp.AndroidMkEntries()
 	entries := &entriesList[0]
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites)
 	})
 
@@ -464,7 +471,7 @@
 	entriesList := a.Library.AndroidMkEntries()
 	entries := &entriesList[0]
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if a.aarFile != nil {
 			entries.SetPath("LOCAL_SOONG_AAR", a.aarFile)
 		}
@@ -492,7 +499,7 @@
 		OutputFile: android.OptionalPathForPath(jd.stubsSrcJar),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				if BoolDefault(jd.properties.Installable, true) {
 					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", jd.docZip)
 				}
@@ -510,7 +517,7 @@
 		OutputFile: android.OptionalPathForPath(ddoc.Javadoc.docZip),
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				if ddoc.Javadoc.docZip != nil {
 					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", ddoc.Javadoc.docZip)
 				}
@@ -539,7 +546,7 @@
 		OutputFile: outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				if dstubs.Javadoc.stubsSrcJar != nil {
 					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.stubsSrcJar)
 				}
@@ -638,7 +645,7 @@
 		OutputFile: android.OptionalPathForPath(a.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged())
 				entries.SetString("LOCAL_CERTIFICATE", a.certificate.AndroidMkString())
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
@@ -657,7 +664,7 @@
 func (a *AndroidTestImport) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := a.AndroidAppImport.AndroidMkEntries()
 	entries := &entriesList[0]
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		testSuiteComponent(entries, a.testProperties.Test_suites)
 		androidMkWriteTestData(a.data, entries)
 	})
@@ -678,7 +685,7 @@
 		OutputFile: android.OptionalPathForPath(r.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString())
 				entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath())
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...)
@@ -694,7 +701,7 @@
 			OutputFile: android.OptionalPathForPath(apkSet.packedOutput),
 			Include:    "$(BUILD_SYSTEM)/soong_android_app_set.mk",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(entries *android.AndroidMkEntries) {
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
 					entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile())
 					entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index e2647cf..e758a92 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -22,7 +22,7 @@
 )
 
 func TestRequired(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -31,7 +31,7 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
 	expected := []string{"libfoo"}
 	actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
@@ -41,7 +41,7 @@
 }
 
 func TestHostdex(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -50,7 +50,7 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
 	}
@@ -71,7 +71,7 @@
 }
 
 func TestHostdexRequired(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -81,7 +81,7 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
 	}
@@ -102,7 +102,7 @@
 }
 
 func TestHostdexSpecificRequired(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -116,7 +116,7 @@
 	`)
 
 	mod := ctx.ModuleForTests("foo", "android_common").Module()
-	entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
 	}
@@ -135,7 +135,7 @@
 }
 
 func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_sdk_library {
 			name: "foo-shared_library",
 			srcs: ["a.java"],
@@ -159,7 +159,7 @@
 	}
 	for _, tc := range testCases {
 		mod := ctx.ModuleForTests(tc.moduleName, "android_common").Module()
-		entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+		entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 		actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
 		if !reflect.DeepEqual(tc.expected, actual) {
 			t.Errorf("Unexpected required modules - expected: %q, actual: %q", tc.expected, actual)
@@ -168,7 +168,7 @@
 }
 
 func TestImportSoongDexJar(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_import {
 			name: "my-java-import",
 			jars: ["a.jar"],
@@ -178,7 +178,7 @@
 	`)
 
 	mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 	expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
 	actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
 
diff --git a/java/app.go b/java/app.go
index ce89e9b..2d918e9 100755
--- a/java/app.go
+++ b/java/app.go
@@ -469,7 +469,7 @@
 		a.Module.compile(ctx, a.aaptSrcJar)
 	}
 
-	return a.maybeStrippedDexJarFile
+	return a.dexJarFile
 }
 
 func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
@@ -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})
@@ -1260,13 +1278,19 @@
 // in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the manifest.
 func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
 	outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
+	statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 	cmd := rule.Command().BuiltTool("manifest_check").
 		Flag("--enforce-uses-libraries").
 		Input(manifest).
+		FlagWithOutput("--enforce-uses-libraries-status ", statusFile).
 		FlagWithOutput("-o ", outputFile)
 
+	if dexpreopt.GetGlobalConfig(ctx).RelaxUsesLibraryCheck {
+		cmd.Flag("--enforce-uses-libraries-relax")
+	}
+
 	for _, lib := range u.usesLibraryProperties.Uses_libs {
 		cmd.FlagWithArg("--uses-library ", lib)
 	}
@@ -1284,6 +1308,7 @@
 // in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the APK.
 func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
 	outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
+	statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 	aapt := ctx.Config().HostToolPath(ctx, "aapt")
@@ -1291,7 +1316,8 @@
 		Textf("aapt_binary=%s", aapt.String()).Implicit(aapt).
 		Textf(`uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Uses_libs, " ")).
 		Textf(`optional_uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Optional_uses_libs, " ")).
-		Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk)
+		Textf(`relax_check="%t"`, dexpreopt.GetGlobalConfig(ctx).RelaxUsesLibraryCheck).
+		Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk).Output(statusFile)
 	rule.Command().Text("cp -f").Input(apk).Output(outputFile)
 
 	rule.Build("verify_uses_libraries", "verify <uses-library>")
diff --git a/java/app_import.go b/java/app_import.go
index 59eb10a..d69dd10 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -244,10 +244,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 +272,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")
diff --git a/java/app_import_test.go b/java/app_import_test.go
index d7f69eb..dc31d07 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -232,7 +232,7 @@
 }
 
 func TestAndroidAppImport_Filename(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
 			apk: "prebuilts/apk/app.apk",
@@ -269,8 +269,7 @@
 
 		a := variant.Module().(*AndroidAppImport)
 		expectedValues := []string{test.expected}
-		actualValues := android.AndroidMkEntriesForTest(
-			t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+		actualValues := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
 		if !reflect.DeepEqual(actualValues, expectedValues) {
 			t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
 				actualValues, expectedValues)
@@ -394,7 +393,7 @@
 }
 
 func TestAndroidAppImport_frameworkRes(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "framework-res",
 			certificate: "platform",
@@ -424,7 +423,7 @@
 
 	}
 
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
 	expectedPath := "."
 	// From apk property above, in the root of the source tree.
@@ -457,7 +456,7 @@
 }
 
 func TestAndroidTestImport(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
 			apk: "prebuilts/apk/app.apk",
@@ -471,7 +470,7 @@
 	test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
 
 	// Check android mks.
-	entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
 	expected := []string{"tests"}
 	actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
 	if !reflect.DeepEqual(expected, actual) {
diff --git a/java/app_set_test.go b/java/app_set_test.go
index d31900d..ab55758 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -22,7 +22,7 @@
 )
 
 func TestAndroidAppSet(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		android_app_set {
 			name: "foo",
 			set: "prebuilts/apks/app.apks",
@@ -40,7 +40,7 @@
 	if s := params.Args["partition"]; s != "system" {
 		t.Errorf("wrong partition value: '%s', expected 'system'", s)
 	}
-	mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+	mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
 	actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
 	expectedInstallFile := []string{"foo.apk"}
 	if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
diff --git a/java/app_test.go b/java/app_test.go
index b1abe3d..f41047a 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -685,6 +685,51 @@
 	}
 }
 
+func TestAppJavaResources(t *testing.T) {
+	bp := `
+			android_app {
+				name: "foo",
+				sdk_version: "current",
+				java_resources: ["resources/a"],
+				srcs: ["a.java"],
+			}
+
+			android_app {
+				name: "bar",
+				sdk_version: "current",
+				java_resources: ["resources/a"],
+			}
+		`
+
+	ctx := testApp(t, bp)
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+	fooResources := foo.Output("res/foo.jar")
+	fooDexJar := foo.Output("dex-withres/foo.jar")
+	fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar")
+	fooApk := foo.Rule("combineApk")
+
+	if g, w := fooDexJar.Inputs.Strings(), fooResources.Output.String(); !android.InList(w, g) {
+		t.Errorf("expected resource jar %q in foo dex jar inputs %q", w, g)
+	}
+
+	if g, w := fooDexJarAligned.Input.String(), fooDexJar.Output.String(); g != w {
+		t.Errorf("expected dex jar %q in foo aligned dex jar inputs %q", w, g)
+	}
+
+	if g, w := fooApk.Inputs.Strings(), fooDexJarAligned.Output.String(); !android.InList(w, g) {
+		t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g)
+	}
+
+	bar := ctx.ModuleForTests("bar", "android_common")
+	barResources := bar.Output("res/bar.jar")
+	barApk := bar.Rule("combineApk")
+
+	if g, w := barApk.Inputs.Strings(), barResources.Output.String(); !android.InList(w, g) {
+		t.Errorf("expected resources jar %q in bar apk inputs %q", w, g)
+	}
+}
+
 func TestAndroidResources(t *testing.T) {
 	testCases := []struct {
 		name                       string
@@ -2245,17 +2290,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",
@@ -2267,7 +2328,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",
@@ -2286,39 +2351,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) {
@@ -2348,6 +2425,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/builder.go b/java/builder.go
index 22a891a..33206ce 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -80,6 +80,8 @@
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
 	_ = pctx.VariableFunc("kytheCuEncoding",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
+	_ = pctx.VariableFunc("kytheCuJavaSourceMax",
+		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuJavaSourceMax() })
 	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
 	// Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
 	// "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
@@ -93,6 +95,7 @@
 				`KYTHE_CORPUS=${kytheCorpus} ` +
 				`KYTHE_VNAMES=${kytheVnames} ` +
 				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+				`KYTHE_JAVA_SOURCE_BATCH_SIZE=${kytheCuJavaSourceMax} ` +
 				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
 				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
 				`-jar ${config.JavaKytheExtractorJar} ` +
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/dex.go b/java/dex.go
index e52fdb5..b2a998f 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -260,6 +260,9 @@
 		r8Flags = append(r8Flags, "--debug")
 	}
 
+	// TODO(b/180878971): missing classes should be added to the relevant builds.
+	r8Flags = append(r8Flags, "-ignorewarnings")
+
 	return r8Flags, r8Deps
 }
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 29c73c1..a2961c2 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -35,6 +35,7 @@
 	isPresignedPrebuilt bool
 
 	manifestFile        android.Path
+	statusFile          android.WritablePath
 	enforceUsesLibs     bool
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
 
@@ -226,9 +227,10 @@
 		ProfileIsTextListing: profileIsTextListing,
 		ProfileBootListing:   profileBootListing,
 
-		EnforceUsesLibraries: d.enforceUsesLibs,
-		ProvidesUsesLibrary:  providesUsesLib,
-		ClassLoaderContexts:  d.classLoaderContexts,
+		EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
+		EnforceUsesLibraries:           d.enforceUsesLibs,
+		ProvidesUsesLibrary:            providesUsesLib,
+		ClassLoaderContexts:            d.classLoaderContexts,
 
 		Archs:                   archs,
 		DexPreoptImages:         images,
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_config.go b/java/dexpreopt_config.go
index c315124..282e936 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -82,10 +82,6 @@
 		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
 		artModules := global.ArtApexJars
-		// With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.
-		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			artModules = artModules.Append("com.android.art", "jacocoagent")
-		}
 		frameworkModules := global.BootJars.RemoveList(artModules)
 
 		artSubdir := "apex/art_boot_images/javalib"
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 1651c1c..208ced7 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -15,8 +15,6 @@
 package java
 
 import (
-	"strings"
-
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -29,8 +27,8 @@
 
 type hiddenAPI struct {
 	// The name of the module as it would be used in the boot jars configuration, e.g. without any
-	// prebuilt_ prefix (if it is a prebuilt), without any "-hiddenapi" suffix if it just provides
-	// annotations and without any ".impl" suffix if it is a java_sdk_library implementation library.
+	// prebuilt_ prefix (if it is a prebuilt) and without any ".impl" suffix if it is a
+	// java_sdk_library implementation library.
 	configurationName string
 
 	// True if the module containing this structure contributes to the hiddenapi information or has
@@ -49,11 +47,6 @@
 	// annotation information.
 	primary bool
 
-	// True if the module only contains additional annotations and so does not require hiddenapi
-	// information to be encoded in its dex file and should not be used to generate the
-	// hiddenAPISingletonPathsStruct.stubFlags file.
-	annotationsOnly bool
-
 	// The path to the dex jar that is in the boot class path. If this is nil then the associated
 	// module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
 	// annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
@@ -119,55 +112,67 @@
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
 // Initialize the hiddenapi structure
-func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, name string) {
+func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) {
 	// If hiddenapi processing is disabled treat this as inactive.
 	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
 		return
 	}
 
-	// Modules whose names are of the format <x>-hiddenapi provide hiddenapi information for the boot
-	// jar module <x>. Otherwise, the module provides information for itself. Either way extract the
-	// configurationName of the boot jar module.
-	configurationName := strings.TrimSuffix(name, "-hiddenapi")
 	h.configurationName = configurationName
 
 	// It is important that hiddenapi information is only gathered for/from modules that are actually
 	// on the boot jars list because the runtime only enforces access to the hidden API for the
 	// bootclassloader. If information is gathered for modules not on the list then that will cause
 	// failures in the CtsHiddenApiBlocklist... tests.
-	h.active = inList(configurationName, ctx.Config().BootJars())
+	module := ctx.Module()
+	h.active = isModuleInBootClassPath(ctx, module)
 	if !h.active {
 		// The rest of the properties will be ignored if active is false.
 		return
 	}
 
-	// If this module has a suffix of -hiddenapi then it only provides additional annotation
-	// information for a module on the boot jars list.
-	h.annotationsOnly = strings.HasSuffix(name, "-hiddenapi")
-
 	// Determine whether this module is the primary module or not.
 	primary := true
 
 	// A prebuilt module is only primary if it is preferred and conversely a source module is only
 	// primary if it has not been replaced by a prebuilt module.
-	module := ctx.Module()
 	if pi, ok := module.(android.PrebuiltInterface); ok {
 		if p := pi.Prebuilt(); p != nil {
 			primary = p.UsePrebuilt()
 		}
 	} else {
-		// The only module that will pass a different name to its module name to this method is the
-		// implementation library of a java_sdk_library. It has a configuration name of <x> the same
-		// as its parent java_sdk_library but a module name of <x>.impl. It is not the primary module,
-		// the java_sdk_library with the name of <x> is.
-		primary = name == ctx.ModuleName()
+		// The only module that will pass a different configurationName to its module name to this
+		// method is the implementation library of a java_sdk_library. It has a configuration name of
+		// <x> the same as its parent java_sdk_library but a module name of <x>.impl. It is not the
+		// primary module, the java_sdk_library with the name of <x> is.
+		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
 }
 
+func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool {
+	// Get the configured non-updatable and updatable boot jars.
+	nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars()
+	updatableBootJars := ctx.Config().UpdatableBootJars()
+	active := isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) ||
+		isModuleInConfiguredList(ctx, module, updatableBootJars)
+	return active
+}
+
 // hiddenAPIExtractAndEncode is called by any module that could contribute to the hiddenapi
 // processing.
 //
@@ -191,15 +196,13 @@
 
 	h.hiddenAPIExtractInformation(ctx, dexJar, implementationJar)
 
-	if !h.annotationsOnly {
-		hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath
+	hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath
 
-		// Create a copy of the dex jar which has been encoded with hiddenapi flags.
-		hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
+	// Create a copy of the dex jar which has been encoded with hiddenapi flags.
+	hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
 
-		// Use the encoded dex jar from here onwards.
-		dexJar = hiddenAPIJar
-	}
+	// Use the encoded dex jar from here onwards.
+	dexJar = hiddenAPIJar
 
 	return dexJar
 }
@@ -221,13 +224,19 @@
 		return
 	}
 
+	classesJars := android.Paths{classesJar}
+	ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
+		javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+		classesJars = append(classesJars, javaInfo.ImplementationJars...)
+	})
+
 	stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags
 
 	flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        hiddenAPIGenerateCSVRule,
 		Description: "hiddenapi flags",
-		Input:       classesJar,
+		Inputs:      classesJars,
 		Output:      flagsCSV,
 		Implicit:    stubFlagsCSV,
 		Args: map[string]string{
@@ -241,7 +250,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        hiddenAPIGenerateCSVRule,
 		Description: "hiddenapi metadata",
-		Input:       classesJar,
+		Inputs:      classesJars,
 		Output:      metadataCSV,
 		Implicit:    stubFlagsCSV,
 		Args: map[string]string{
@@ -255,8 +264,10 @@
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
 		BuiltTool("merge_csv").
-		FlagWithInput("--zip_input=", classesJar).
-		FlagWithOutput("--output=", indexCSV)
+		Flag("--zip_input").
+		Flag("--key_field signature").
+		FlagWithOutput("--output=", indexCSV).
+		Inputs(classesJars)
 	rule.Build("merged-hiddenapi-index", "Merged Hidden API index")
 	h.indexCSVPath = indexCSV
 
@@ -335,3 +346,16 @@
 		TransformZipAlign(ctx, output, tmpOutput)
 	}
 }
+
+type hiddenApiAnnotationsDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// Tag used to mark dependencies on java_library instances that contains Java source files whose
+// sole purpose is to provide additional hiddenapi annotations.
+var hiddenApiAnnotationsTag hiddenApiAnnotationsDependencyTag
+
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t hiddenApiAnnotationsDependencyTag) ExcludeFromApexContents() {}
+
+var _ android.ExcludeFromApexContentsTag = hiddenApiAnnotationsTag
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 6341a34..82e8b3f 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -217,10 +217,6 @@
 
 	var bootDexJars android.Paths
 
-	// Get the configured non-updatable and updatable boot jars.
-	nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars()
-	updatableBootJars := ctx.Config().UpdatableBootJars()
-
 	ctx.VisitAllModules(func(module android.Module) {
 		// Collect dex jar paths for the modules listed above.
 		if j, ok := module.(UsesLibraryDependency); ok {
@@ -235,11 +231,6 @@
 		// Collect dex jar paths for modules that had hiddenapi encode called on them.
 		if h, ok := module.(hiddenAPIIntf); ok {
 			if jar := h.bootDexJar(); jar != nil {
-				if !isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) &&
-					!isModuleInConfiguredList(ctx, module, updatableBootJars) {
-					return
-				}
-
 				bootDexJars = append(bootDexJars, jar)
 			}
 		}
@@ -291,8 +282,8 @@
 // there too.
 //
 // TODO(b/179354495): Avoid having to perform this type of check or if necessary dedup it.
-func isModuleInConfiguredList(ctx android.SingletonContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool {
-	name := ctx.ModuleName(module)
+func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool {
+	name := ctx.OtherModuleName(module)
 
 	// Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
 	name = android.RemoveOptionalPrebuiltPrefix(name)
@@ -305,11 +296,11 @@
 
 	// It is an error if the module is not an ApexModule.
 	if _, ok := module.(android.ApexModule); !ok {
-		ctx.Errorf("module %q configured in boot jars does not support being added to an apex", module)
+		ctx.ModuleErrorf("is configured in boot jars but does not support being added to an apex")
 		return false
 	}
 
-	apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
 
 	// Now match the apex part of the boot image configuration.
 	requiredApex := configuredBootJars.Apex(index)
@@ -433,6 +424,7 @@
 
 	rule.Command().
 		BuiltTool("merge_csv").
+		Flag("--key_field signature").
 		FlagWithOutput("--output=", outputPath).
 		Inputs(metadataCSV)
 
@@ -544,6 +536,7 @@
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
 		BuiltTool("merge_csv").
+		Flag("--key_field signature").
 		FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
 		FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
 		Inputs(indexes)
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index df825bb..fb63820 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -82,10 +82,14 @@
 			name: "foo",
 			srcs: ["a.java"],
 			compile_dex: true,
+
+			hiddenapi_additional_annotations: [
+				"foo-hiddenapi-annotations",
+			],
 		}
 
 		java_library {
-			name: "foo-hiddenapi",
+			name: "foo-hiddenapi-annotations",
 			srcs: ["a.java"],
 			compile_dex: true,
 		}
@@ -108,10 +112,41 @@
 	indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
 	CheckHiddenAPIRuleInputs(t, `
 .intermediates/bar/android_common/hiddenapi/index.csv
-.intermediates/foo-hiddenapi/android_common/hiddenapi/index.csv
 .intermediates/foo/android_common/hiddenapi/index.csv
 `,
 		indexRule)
+
+	// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
+	// creates the index.csv file.
+	foo := ctx.ModuleForTests("foo", "android_common")
+	indexParams := foo.Output("hiddenapi/index.csv")
+	CheckHiddenAPIRuleInputs(t, `
+.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
+.intermediates/foo/android_common/javac/foo.jar
+`, 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) {
diff --git a/java/java.go b/java/java.go
index 338140b..9e35835 100644
--- a/java/java.go
+++ b/java/java.go
@@ -298,6 +298,9 @@
 
 	// If true, package the kotlin stdlib into the jar.  Defaults to true.
 	Static_kotlin_stdlib *bool `android:"arch_variant"`
+
+	// A list of java_library instances that provide additional hiddenapi annotations for the library.
+	Hiddenapi_additional_annotations []string
 }
 
 type CompilerDeviceProperties struct {
@@ -367,6 +370,10 @@
 	// If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
 	// Defaults to false.
 	V4_signature *bool
+
+	// Only for libraries created by a sysprop_library module, SyspropPublicStub is the name of the
+	// public stubs library.
+	SyspropPublicStub string `blueprint:"mutated"`
 }
 
 // Functionality common to Module and Import
@@ -431,9 +438,6 @@
 	// output file containing classes.dex and resources
 	dexJarFile android.Path
 
-	// output file that contains classes.dex if it should be in the output file
-	maybeStrippedDexJarFile android.Path
-
 	// output file containing uninstrumented classes that will be instrumented by jacoco
 	jacocoReportClassesFile android.Path
 
@@ -580,6 +584,16 @@
 
 var JavaInfoProvider = blueprint.NewProvider(JavaInfo{})
 
+// SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
+// the sysprop implementation library.
+type SyspropPublicStubInfo struct {
+	// JavaInfo is the JavaInfoProvider of the sysprop public stub library that corresponds to
+	// the sysprop implementation library.
+	JavaInfo JavaInfo
+}
+
+var SyspropPublicStubInfoProvider = blueprint.NewProvider(SyspropPublicStubInfo{})
+
 // Methods that need to be implemented for a module that is added to apex java_libs property.
 type ApexDependency interface {
 	HeaderJars() android.Paths
@@ -649,29 +663,30 @@
 }
 
 var (
-	dataNativeBinsTag     = dependencyTag{name: "dataNativeBins"}
-	staticLibTag          = dependencyTag{name: "staticlib"}
-	libTag                = dependencyTag{name: "javalib"}
-	java9LibTag           = dependencyTag{name: "java9lib"}
-	pluginTag             = dependencyTag{name: "plugin"}
-	errorpronePluginTag   = dependencyTag{name: "errorprone-plugin"}
-	exportedPluginTag     = dependencyTag{name: "exported-plugin"}
-	bootClasspathTag      = dependencyTag{name: "bootclasspath"}
-	systemModulesTag      = dependencyTag{name: "system modules"}
-	frameworkResTag       = dependencyTag{name: "framework-res"}
-	kotlinStdlibTag       = dependencyTag{name: "kotlin-stdlib"}
-	kotlinAnnotationsTag  = dependencyTag{name: "kotlin-annotations"}
-	proguardRaiseTag      = dependencyTag{name: "proguard-raise"}
-	certificateTag        = dependencyTag{name: "certificate"}
-	instrumentationForTag = dependencyTag{name: "instrumentation_for"}
-	extraLintCheckTag     = dependencyTag{name: "extra-lint-check"}
-	jniLibTag             = dependencyTag{name: "jnilib"}
-	jniInstallTag         = installDependencyTag{name: "jni install"}
-	binaryInstallTag      = installDependencyTag{name: "binary install"}
-	usesLibTag            = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
-	usesLibCompat28Tag    = makeUsesLibraryDependencyTag(28)
-	usesLibCompat29Tag    = makeUsesLibraryDependencyTag(29)
-	usesLibCompat30Tag    = makeUsesLibraryDependencyTag(30)
+	dataNativeBinsTag       = dependencyTag{name: "dataNativeBins"}
+	staticLibTag            = dependencyTag{name: "staticlib"}
+	libTag                  = dependencyTag{name: "javalib"}
+	java9LibTag             = dependencyTag{name: "java9lib"}
+	pluginTag               = dependencyTag{name: "plugin"}
+	errorpronePluginTag     = dependencyTag{name: "errorprone-plugin"}
+	exportedPluginTag       = dependencyTag{name: "exported-plugin"}
+	bootClasspathTag        = dependencyTag{name: "bootclasspath"}
+	systemModulesTag        = dependencyTag{name: "system modules"}
+	frameworkResTag         = dependencyTag{name: "framework-res"}
+	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib"}
+	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations"}
+	proguardRaiseTag        = dependencyTag{name: "proguard-raise"}
+	certificateTag          = dependencyTag{name: "certificate"}
+	instrumentationForTag   = dependencyTag{name: "instrumentation_for"}
+	extraLintCheckTag       = dependencyTag{name: "extra-lint-check"}
+	jniLibTag               = dependencyTag{name: "jnilib"}
+	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
+	jniInstallTag           = installDependencyTag{name: "jni install"}
+	binaryInstallTag        = installDependencyTag{name: "binary install"}
+	usesLibTag              = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
+	usesLibCompat28Tag      = makeUsesLibraryDependencyTag(28)
+	usesLibCompat29Tag      = makeUsesLibraryDependencyTag(29)
+	usesLibCompat30Tag      = makeUsesLibraryDependencyTag(30)
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -810,35 +825,20 @@
 		j.linter.deps(ctx)
 
 		sdkDeps(ctx, sdkContext(j), j.dexer)
-	}
 
-	syspropPublicStubs := syspropPublicStubs(ctx.Config())
-
-	// rewriteSyspropLibs validates if a java module can link against platform's sysprop_library,
-	// and redirects dependency to public stub depending on the link type.
-	rewriteSyspropLibs := func(libs []string, prop string) []string {
-		// make a copy
-		ret := android.CopyOf(libs)
-
-		for idx, lib := range libs {
-			stub, ok := syspropPublicStubs[lib]
-
-			if !ok {
-				continue
-			}
-
-			linkType, _ := j.getLinkType(ctx.ModuleName())
-			// only platform modules can use internal props
-			if linkType != javaPlatform {
-				ret[idx] = stub
-			}
+		if j.deviceProperties.SyspropPublicStub != "" {
+			// This is a sysprop implementation library that has a corresponding sysprop public
+			// stubs library, and a dependency on it so that dependencies on the implementation can
+			// be forwarded to the public stubs library when necessary.
+			ctx.AddVariationDependencies(nil, syspropPublicStubDepTag, j.deviceProperties.SyspropPublicStub)
 		}
-
-		return ret
 	}
 
-	libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...)
-	ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...)
+	libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
+
+	// Add dependency on libraries that provide additional hidden api annotations.
+	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
 
 	if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
 		// Require java_sdk_library at inter-partition java dependency to ensure stable
@@ -850,15 +850,11 @@
 		//      if true, enable enforcement
 		//    PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
 		//      exception list of java_library names to allow inter-partition dependency
-		for idx, lib := range j.properties.Libs {
+		for idx := range j.properties.Libs {
 			if libDeps[idx] == nil {
 				continue
 			}
 
-			if _, ok := syspropPublicStubs[lib]; ok {
-				continue
-			}
-
 			if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
 				// java_sdk_library is always allowed at inter-partition dependency.
 				// So, skip check.
@@ -1008,6 +1004,7 @@
 const (
 	// TODO(jiyong) rename these for better readability. Make the allowed
 	// and disallowed link types explicit
+	// order is important here. See rank()
 	javaCore linkType = iota
 	javaSdk
 	javaSystem
@@ -1016,6 +1013,31 @@
 	javaPlatform
 )
 
+func (lt linkType) String() string {
+	switch lt {
+	case javaCore:
+		return "core Java API"
+	case javaSdk:
+		return "Android API"
+	case javaSystem:
+		return "system API"
+	case javaModule:
+		return "module API"
+	case javaSystemServer:
+		return "system server API"
+	case javaPlatform:
+		return "private API"
+	default:
+		panic(fmt.Errorf("unrecognized linktype: %d", lt))
+	}
+}
+
+// rank determins the total order among linkTypes. A link type of rank A can link to another link
+// type of rank B only when B <= A
+func (lt linkType) rank() int {
+	return int(lt)
+}
+
 type linkTypeContext interface {
 	android.Module
 	getLinkType(name string) (ret linkType, stubs bool)
@@ -1075,44 +1097,13 @@
 		return
 	}
 	otherLinkType, _ := to.getLinkType(ctx.OtherModuleName(to))
-	commonMessage := " In order to fix this, consider adjusting sdk_version: OR platform_apis: " +
-		"property of the source or target module so that target module is built with the same " +
-		"or smaller API set when compared to the source."
 
-	switch myLinkType {
-	case javaCore:
-		if otherLinkType != javaCore {
-			ctx.ModuleErrorf("compiles against core Java API, but dependency %q is compiling against non-core Java APIs."+commonMessage,
-				ctx.OtherModuleName(to))
-		}
-		break
-	case javaSdk:
-		if otherLinkType != javaCore && otherLinkType != javaSdk {
-			ctx.ModuleErrorf("compiles against Android API, but dependency %q is compiling against non-public Android API."+commonMessage,
-				ctx.OtherModuleName(to))
-		}
-		break
-	case javaSystem:
-		if otherLinkType == javaPlatform || otherLinkType == javaModule || otherLinkType == javaSystemServer {
-			ctx.ModuleErrorf("compiles against system API, but dependency %q is compiling against private API."+commonMessage,
-				ctx.OtherModuleName(to))
-		}
-		break
-	case javaModule:
-		if otherLinkType == javaPlatform || otherLinkType == javaSystemServer {
-			ctx.ModuleErrorf("compiles against module API, but dependency %q is compiling against private API."+commonMessage,
-				ctx.OtherModuleName(to))
-		}
-		break
-	case javaSystemServer:
-		if otherLinkType == javaPlatform {
-			ctx.ModuleErrorf("compiles against system server API, but dependency %q is compiling against private API."+commonMessage,
-				ctx.OtherModuleName(to))
-		}
-		break
-	case javaPlatform:
-		// no restriction on link-type
-		break
+	if myLinkType.rank() < otherLinkType.rank() {
+		ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+
+			"In order to fix this, consider adjusting sdk_version: OR platform_apis: "+
+			"property of the source or target module so that target module is built "+
+			"with the same or smaller API set when compared to the source.",
+			myLinkType, ctx.OtherModuleName(to), otherLinkType)
 	}
 }
 
@@ -1133,6 +1124,8 @@
 		}
 	}
 
+	linkType, _ := j.getLinkType(ctx.ModuleName())
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -1155,6 +1148,14 @@
 			}
 		} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
 			dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+			if linkType != javaPlatform &&
+				ctx.OtherModuleHasProvider(module, SyspropPublicStubInfoProvider) {
+				// dep is a sysprop implementation library, but this module is not linking against
+				// the platform, so it gets the sysprop public stubs library instead.  Replace
+				// dep with the JavaInfo from the SyspropPublicStubInfoProvider.
+				syspropDep := ctx.OtherModuleProvider(module, SyspropPublicStubInfoProvider).(SyspropPublicStubInfo)
+				dep = syspropDep.JavaInfo
+			}
 			switch tag {
 			case bootClasspathTag:
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
@@ -1213,6 +1214,12 @@
 				deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...)
 			case kotlinAnnotationsTag:
 				deps.kotlinAnnotations = dep.HeaderJars
+			case syspropPublicStubDepTag:
+				// This is a sysprop implementation library, forward the JavaInfoProvider from
+				// the corresponding sysprop public stub library as SyspropPublicStubInfoProvider.
+				ctx.SetProvider(SyspropPublicStubInfoProvider, SyspropPublicStubInfo{
+					JavaInfo: dep,
+				})
 			}
 		} else if dep, ok := module.(android.SourceFileProducer); ok {
 			switch tag {
@@ -1817,47 +1824,51 @@
 		}
 	}
 
-	if ctx.Device() && j.hasCode(ctx) &&
-		(Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
-		if j.shouldInstrumentStatic(ctx) {
-			j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
-				android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
-		}
-		// Dex compilation
-		var dexOutputFile android.OutputPath
-		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
-		if ctx.Failed() {
-			return
-		}
-
-		// Hidden API CSV generation and dex encoding
-		dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, j.implementationJarFile,
-			proptools.Bool(j.dexProperties.Uncompress_dex))
-
-		// merge dex jar with resources if necessary
-		if j.resourceJar != nil {
-			jars := android.Paths{dexOutputFile, j.resourceJar}
-			combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
-			TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
-				false, nil, nil)
-			if *j.dexProperties.Uncompress_dex {
-				combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
-				TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
-				dexOutputFile = combinedAlignedJar
-			} else {
-				dexOutputFile = combinedJar
+	if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+		if j.hasCode(ctx) {
+			if j.shouldInstrumentStatic(ctx) {
+				j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
+					android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
 			}
+			// Dex compilation
+			var dexOutputFile android.OutputPath
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
+			if ctx.Failed() {
+				return
+			}
+
+			// Hidden API CSV generation and dex encoding
+			dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, j.implementationJarFile,
+				proptools.Bool(j.dexProperties.Uncompress_dex))
+
+			// merge dex jar with resources if necessary
+			if j.resourceJar != nil {
+				jars := android.Paths{dexOutputFile, j.resourceJar}
+				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
+				TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
+					false, nil, nil)
+				if *j.dexProperties.Uncompress_dex {
+					combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
+					TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
+					dexOutputFile = combinedAlignedJar
+				} else {
+					dexOutputFile = combinedJar
+				}
+			}
+
+			j.dexJarFile = dexOutputFile
+
+			// Dexpreopting
+			j.dexpreopt(ctx, dexOutputFile)
+
+			outputFile = dexOutputFile
+		} else {
+			// There is no code to compile into a dex jar, make sure the resources are propagated
+			// to the APK if this is an app.
+			outputFile = implementationAndResourcesJar
+			j.dexJarFile = j.resourceJar
 		}
 
-		j.dexJarFile = dexOutputFile
-
-		// Dexpreopting
-		j.dexpreopt(ctx, dexOutputFile)
-
-		j.maybeStrippedDexJarFile = dexOutputFile
-
-		outputFile = dexOutputFile
-
 		if ctx.Failed() {
 			return
 		}
@@ -2035,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
@@ -2058,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 {
@@ -2426,7 +2419,7 @@
 
 	// list of files or filegroup modules that provide data that should be installed alongside
 	// the test
-	Data []string `android:"path,arch_variant"`
+	Data []string `android:"path"`
 
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
@@ -2504,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)
 
@@ -2664,6 +2662,7 @@
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
 	InitJavaModuleMultiTargets(module, android.HostSupported)
+
 	return module
 }
 
@@ -3035,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
@@ -3061,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
@@ -3182,8 +3158,7 @@
 
 	properties DexImportProperties
 
-	dexJarFile              android.Path
-	maybeStrippedDexJarFile android.Path
+	dexJarFile android.Path
 
 	dexpreopter
 
@@ -3270,8 +3245,6 @@
 
 	j.dexpreopt(ctx, dexOutputFile)
 
-	j.maybeStrippedDexJarFile = dexOutputFile
-
 	if apexInfo.IsForPlatform() {
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			j.Stem()+".jar", dexOutputFile)
diff --git a/java/java_test.go b/java/java_test.go
index e7776c3..9112655 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"
@@ -74,11 +75,13 @@
 
 	ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-	ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory))
-	ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(ctx.Context, sdkPreSingletonFactory))
+	ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+	ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
 
 	android.RegisterPrebuiltMutators(ctx)
 
+	genrule.RegisterGenruleBuildComponents(ctx)
+
 	// Register module types and mutators from cc needed for JNI testing
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 
@@ -114,21 +117,26 @@
 	pathCtx := android.PathContextForTesting(config)
 	dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
 
+	runWithErrors(t, ctx, config, pattern)
+
+	return ctx, config
+}
+
+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) {
@@ -1179,6 +1187,110 @@
 	}
 }
 
+func TestJavaLint(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+				"b.java",
+				"c.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+		}
+       `, map[string][]byte{
+		"lint-baseline.xml": nil,
+	})
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+	rule := foo.Rule("lint")
+
+	if !strings.Contains(rule.RuleParams.Command, "--baseline lint-baseline.xml") {
+		t.Error("did not pass --baseline flag")
+	}
+}
+
+func TestJavaLintWithoutBaseline(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+				"b.java",
+				"c.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+		}
+       `, map[string][]byte{})
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+	rule := foo.Rule("lint")
+
+	if strings.Contains(rule.RuleParams.Command, "--baseline") {
+		t.Error("passed --baseline flag for non existent file")
+	}
+}
+
+func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
+	config := testConfig(
+		nil,
+		`
+		java_library {
+			name: "foo",
+			srcs: [
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+			lint: {
+				baseline_filename: "mybaseline.xml",
+			},
+		}
+     `, map[string][]byte{
+			"build/soong/java/lint_defaults.txt":                   nil,
+			"prebuilts/cmdline-tools/tools/bin/lint":               nil,
+			"prebuilts/cmdline-tools/tools/lib/lint-classpath.jar": nil,
+			"framework/aidl":                     nil,
+			"a.java":                             nil,
+			"AndroidManifest.xml":                nil,
+			"build/make/target/product/security": nil,
+		})
+	config.TestAllowNonExistentPaths = false
+	testJavaErrorWithConfig(t,
+		"source path \"mybaseline.xml\" does not exist",
+		config,
+	)
+}
+
+func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+				"b.java",
+				"c.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+			lint: {
+				error_checks: ["SomeCheck"],
+				baseline_filename: "mybaseline.xml",
+			},
+		}
+       `, map[string][]byte{
+		"mybaseline.xml": nil,
+	})
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+	rule := foo.Rule("lint")
+
+	if !strings.Contains(rule.RuleParams.Command, "--baseline mybaseline.xml") {
+		t.Error("did not use the correct file for baseline")
+	}
+}
+
 func TestGeneratedSources(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
@@ -2406,7 +2518,7 @@
 }
 
 func TestDataNativeBinaries(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo",
 			srcs: ["a.java"],
@@ -2422,7 +2534,7 @@
 	buildOS := android.BuildOs.String()
 
 	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
-	entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
 	expected := []string{buildDir + "/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
 	actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
 	if !reflect.DeepEqual(expected, actual) {
diff --git a/java/lint.go b/java/lint.go
index c9e0cdd..50b84dc 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -19,6 +19,8 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 )
 
@@ -46,6 +48,9 @@
 
 		// Modules that provide extra lint checks
 		Extra_check_modules []string
+
+		// Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
+		Baseline_filename *string
 	}
 }
 
@@ -308,6 +313,7 @@
 
 	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 -f").Output(html).Output(text).Output(xml)
 
 	var annotationsZipPath, apiVersionsXMLPath android.Path
 	if ctx.Config().AlwaysUsePrebuiltSdks() {
@@ -324,8 +330,7 @@
 		FlagWithArg("ANDROID_SDK_HOME=", 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")).
+		BuiltTool("lint").
 		Flag("--quiet").
 		FlagWithInput("--project ", projectXML).
 		FlagWithInput("--config ", lintXML).
@@ -344,7 +349,20 @@
 		cmd.FlagWithArg("--check ", checkOnly)
 	}
 
-	cmd.Text("|| (").Text("cat").Input(text).Text("; exit 7)").Text(")")
+	if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
+		var lintBaseline android.OptionalPath
+		if String(l.properties.Lint.Baseline_filename) != "" {
+			// if manually specified, we require the file to exist
+			lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
+		} else {
+			lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
+		}
+		if lintBaseline.Valid() {
+			cmd.FlagWithInput("--baseline ", lintBaseline.Path())
+		}
+	}
+
+	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)").Text(")")
 
 	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
 
@@ -420,13 +438,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/platform_compat_config.go b/java/platform_compat_config.go
index 9bc821d..2c47b0a 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -131,7 +131,7 @@
 		OutputFile: android.OptionalPathForPath(p.configFile),
 		Include:    "$(BUILD_PREBUILT)",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
 			},
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 1e90149..c91b321 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -106,14 +106,18 @@
 	mctx.CreateModule(ImportFactory, &props)
 }
 
-func createFilegroup(mctx android.LoadHookContext, name string, path string) {
-	filegroupProps := struct {
+func createApiModule(mctx android.LoadHookContext, name string, path string) {
+	genruleProps := struct {
 		Name *string
 		Srcs []string
+		Out  []string
+		Cmd  *string
 	}{}
-	filegroupProps.Name = proptools.StringPtr(name)
-	filegroupProps.Srcs = []string{path}
-	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
+	genruleProps.Name = proptools.StringPtr(name)
+	genruleProps.Srcs = []string{path}
+	genruleProps.Out = []string{name}
+	genruleProps.Cmd = proptools.StringPtr("cp $(in) $(out)")
+	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
 }
 
 func createEmptyFile(mctx android.LoadHookContext, name string) {
@@ -205,16 +209,16 @@
 		path    string
 	}
 
-	// Create filegroups for all (<module>, <scope, <version>) triplets,
-	// and a "latest" filegroup variant for each (<module>, <scope>) pair
-	moduleName := func(module, scope, version string) string {
+	// Create modules for all (<module>, <scope, <version>) triplets,
+	// and a "latest" module variant for each (<module>, <scope>) pair
+	apiModuleName := func(module, scope, version string) string {
 		return module + ".api." + scope + "." + version
 	}
 	m := make(map[string]latestApiInfo)
 	for _, f := range files {
 		localPath := strings.TrimPrefix(f, mydir)
 		module, apiver, scope := parseApiFilePath(mctx, localPath)
-		createFilegroup(mctx, moduleName(module, scope, apiver), localPath)
+		createApiModule(mctx, apiModuleName(module, scope, apiver), localPath)
 
 		version, err := strconv.Atoi(apiver)
 		if err != nil {
@@ -239,8 +243,8 @@
 	// Sort the keys in order to make build.ninja stable
 	for _, k := range android.SortedStringKeys(m) {
 		info := m[k]
-		name := moduleName(info.module, info.scope, "latest")
-		createFilegroup(mctx, name, info.path)
+		name := apiModuleName(info.module, info.scope, "latest")
+		createApiModule(mctx, name, info.path)
 	}
 
 	// Create incompatibilities tracking files for all modules, if we have a "next" api.
@@ -258,14 +262,14 @@
 				referencedModule = "android"
 			}
 
-			createFilegroup(mctx, moduleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
+			createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
 
 			incompatibilities[referencedModule+"."+scope] = true
 		}
 		// Create empty incompatibilities files for remaining modules
 		for _, k := range android.SortedStringKeys(m) {
 			if _, ok := incompatibilities[k]; !ok {
-				createEmptyFile(mctx, moduleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
+				createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
 			}
 		}
 	}
@@ -279,10 +283,10 @@
 	}
 }
 
-// prebuilt_apis is a meta-module that generates filegroup modules for all
-// API txt files found under the directory where the Android.bp is located.
+// prebuilt_apis is a meta-module that generates modules for all API txt files
+// found under the directory where the Android.bp is located.
 // Specifically, an API file located at ./<ver>/<scope>/api/<module>.txt
-// generates a filegroup module named <module>-api.<scope>.<ver>.
+// generates a module named <module>-api.<scope>.<ver>.
 //
 // It also creates <module>-api.<scope>.latest for the latest <ver>.
 //
diff --git a/java/rro_test.go b/java/rro_test.go
index 345f2ee..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) {
@@ -96,7 +97,7 @@
 	if expected != signingFlag {
 		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
 	}
-	androidMkEntries := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+	androidMkEntries := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0]
 	path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
 	expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
 	if !reflect.DeepEqual(path, expectedPath) {
@@ -105,16 +106,16 @@
 
 	// 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)
 	}
 
 	// A themed module has a different device location
 	m = ctx.ModuleForTests("foo_themed", "android_common")
-	androidMkEntries = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+	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)
 	}
@@ -127,7 +128,7 @@
 }
 
 func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
-	ctx, config := testJava(t, `
+	ctx, _ := testJava(t, `
 		java_defaults {
 			name: "rro_defaults",
 			theme: "default_theme",
@@ -159,8 +160,8 @@
 	}
 
 	// Check device location.
-	path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
+	path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+	expectedPath := []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/default_theme")}
 	if !reflect.DeepEqual(path, expectedPath) {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
 	}
@@ -178,8 +179,8 @@
 	}
 
 	// Check device location.
-	path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
+	path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+	expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/system/overlay")}
 	if !reflect.DeepEqual(path, expectedPath) {
 		t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
 	}
@@ -263,47 +264,34 @@
 
 func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
 	testCases := []struct {
-		name                    string
-		enforceRROTargets       []string
-		enforceRROExemptTargets []string
-		rroDirs                 map[string][]string
+		name              string
+		enforceRROTargets []string
+		rroDirs           map[string][]string
 	}{
 		{
-			name:                    "no RRO",
-			enforceRROTargets:       nil,
-			enforceRROExemptTargets: nil,
+			name:              "no RRO",
+			enforceRROTargets: nil,
 			rroDirs: map[string][]string{
 				"foo": nil,
 				"bar": nil,
 			},
 		},
 		{
-			name:                    "enforce RRO on all",
-			enforceRROTargets:       []string{"*"},
-			enforceRROExemptTargets: nil,
+			name:              "enforce RRO on all",
+			enforceRROTargets: []string{"*"},
 			rroDirs: map[string][]string{
 				"foo": {"product/vendor/blah/overlay/lib2/res"},
 				"bar": {"product/vendor/blah/overlay/lib2/res"},
 			},
 		},
 		{
-			name:                    "enforce RRO on foo",
-			enforceRROTargets:       []string{"foo"},
-			enforceRROExemptTargets: nil,
+			name:              "enforce RRO on foo",
+			enforceRROTargets: []string{"foo"},
 			rroDirs: map[string][]string{
 				"foo": {"product/vendor/blah/overlay/lib2/res"},
 				"bar": {"product/vendor/blah/overlay/lib2/res"},
 			},
 		},
-		{
-			name:                    "enforce RRO on foo, bar exempted",
-			enforceRROTargets:       []string{"foo"},
-			enforceRROExemptTargets: []string{"bar"},
-			rroDirs: map[string][]string{
-				"foo": {"product/vendor/blah/overlay/lib2/res"},
-				"bar": nil,
-			},
-		},
 	}
 
 	productResourceOverlays := []string{
@@ -351,9 +339,6 @@
 			if testCase.enforceRROTargets != nil {
 				config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
 			}
-			if testCase.enforceRROExemptTargets != nil {
-				config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
-			}
 
 			ctx := testContext(config)
 			run(t, ctx, config)
@@ -361,7 +346,7 @@
 			modules := []string{"foo", "bar"}
 			for _, moduleName := range modules {
 				module := ctx.ModuleForTests(moduleName, "android_common")
-				mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+				mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
 				actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
 				if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
 					t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
diff --git a/java/sdk_library.go b/java/sdk_library.go
index aa96e0d..30d120d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1772,6 +1772,8 @@
 	android.ApexModuleBase
 	android.SdkBase
 
+	hiddenAPI
+
 	properties sdkLibraryImportProperties
 
 	// Map from api scope to the scope specific property structure.
@@ -1786,6 +1788,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 +1987,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 +2014,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 +2031,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 +2089,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 {
@@ -2238,7 +2281,7 @@
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(module.outputFilePath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
 				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
index 2934936..0acaa13 100644
--- a/java/sdk_library_external.go
+++ b/java/sdk_library_external.go
@@ -75,10 +75,15 @@
 	return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
 }
 
+func (j *Module) syspropWithPublicStubs() bool {
+	return j.deviceProperties.SyspropPublicStub != ""
+}
+
 type javaSdkLibraryEnforceContext interface {
 	Name() string
 	allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
 	partitionGroup(ctx android.EarlyModuleContext) partitionGroup
+	syspropWithPublicStubs() bool
 }
 
 var _ javaSdkLibraryEnforceContext = (*Module)(nil)
@@ -88,6 +93,10 @@
 		return
 	}
 
+	if dep.syspropWithPublicStubs() {
+		return
+	}
+
 	// If product interface is not enforced, skip check between system and product partition.
 	// But still need to check between product and vendor partition because product interface flag
 	// just represents enforcement between product and system, and vendor interface enforcement
diff --git a/java/sysprop.go b/java/sysprop.go
deleted file mode 100644
index e41aef6..0000000
--- a/java/sysprop.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-// This file contains a map to redirect dependencies towards sysprop_library. If a sysprop_library
-// is owned by Platform, and the client module links against system API, the public stub of the
-// sysprop_library should be used. The map will contain public stub names of sysprop_libraries.
-
-import (
-	"sync"
-
-	"android/soong/android"
-)
-
-type syspropLibraryInterface interface {
-	BaseModuleName() string
-	Owner() string
-	HasPublicStub() bool
-	JavaPublicStubName() string
-}
-
-var (
-	syspropPublicStubsKey  = android.NewOnceKey("syspropPublicStubsJava")
-	syspropPublicStubsLock sync.Mutex
-)
-
-func init() {
-	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("sysprop_java", SyspropMutator).Parallel()
-	})
-}
-
-func syspropPublicStubs(config android.Config) map[string]string {
-	return config.Once(syspropPublicStubsKey, func() interface{} {
-		return make(map[string]string)
-	}).(map[string]string)
-}
-
-// gather list of sysprop libraries owned by platform.
-func SyspropMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(syspropLibraryInterface); ok {
-		if m.Owner() != "Platform" || !m.HasPublicStub() {
-			return
-		}
-
-		syspropPublicStubs := syspropPublicStubs(mctx.Config())
-		syspropPublicStubsLock.Lock()
-		defer syspropPublicStubsLock.Unlock()
-
-		syspropPublicStubs[m.BaseModuleName()] = m.JavaPublicStubName()
-	}
-}
diff --git a/java/testing.go b/java/testing.go
index 781106f..bfa1e6b 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -240,6 +240,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/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index d538ce4..ff548e5 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -98,7 +98,7 @@
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(l.outputFilePath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
diff --git a/linkerconfig/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go
index 01f4657..8eed4b5 100644
--- a/linkerconfig/linkerconfig_test.go
+++ b/linkerconfig/linkerconfig_test.go
@@ -47,7 +47,7 @@
 	os.Exit(run())
 }
 
-func testContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
+func testContext(t *testing.T, bp string) *android.TestContext {
 	t.Helper()
 
 	fs := map[string][]byte{
@@ -65,11 +65,11 @@
 	_, errs = ctx.PrepareBuildActions(config)
 	android.FailIfErrored(t, errs)
 
-	return ctx, config
+	return ctx
 }
 
 func TestBaseLinkerConfig(t *testing.T) {
-	ctx, config := testContext(t, `
+	ctx := testContext(t, `
 	linker_config {
 		name: "linker-config-base",
 		src: "linker.config.json",
@@ -88,7 +88,7 @@
 		t.Errorf("expected linker.config.pb, got %q", p.outputFilePath.Base())
 	}
 
-	entries := android.AndroidMkEntriesForTest(t, config, "", p)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, p)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
 			if !reflect.DeepEqual(value, expectedValue) {
@@ -105,7 +105,7 @@
 }
 
 func TestUninstallableLinkerConfig(t *testing.T) {
-	ctx, config := testContext(t, `
+	ctx := testContext(t, `
 	linker_config {
 		name: "linker-config-base",
 		src: "linker.config.json",
@@ -116,7 +116,7 @@
 	expected := []string{"true"}
 
 	p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
-	entries := android.AndroidMkEntriesForTest(t, config, "", p)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, p)[0]
 	if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
 		if !reflect.DeepEqual(value, expected) {
 			t.Errorf("LOCAL_UNINSTALLABLE_MODULE is expected to be true but %s", value)
diff --git a/python/androidmk.go b/python/androidmk.go
index 60637d3..13b4172 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -48,27 +48,29 @@
 func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
 	entries.Class = "EXECUTABLES"
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
-	})
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+		})
 	base.subAndroidMk(entries, p.pythonInstaller)
 }
 
 func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
 	entries.Class = "NATIVE_TESTS"
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
-		if p.testConfig != nil {
-			entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
-		}
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
+			if p.testConfig != nil {
+				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+			}
 
-		entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
+			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
 
-		entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
+			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
 
-		entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
-	})
+			entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
+		})
 	base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
 }
 
@@ -80,14 +82,15 @@
 	}
 
 	entries.Required = append(entries.Required, "libc++")
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		path, file := filepath.Split(installer.path.ToMakePath().String())
-		stem := strings.TrimSuffix(file, filepath.Ext(file))
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			path, file := filepath.Split(installer.path.ToMakePath().String())
+			stem := strings.TrimSuffix(file, filepath.Ext(file))
 
-		entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
-		entries.SetString("LOCAL_MODULE_PATH", path)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
-		entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
-		entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
-	})
+			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+			entries.SetString("LOCAL_MODULE_PATH", path)
+			entries.SetString("LOCAL_MODULE_STEM", stem)
+			entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
+			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+		})
 }
diff --git a/rust/Android.bp b/rust/Android.bp
index 8b2aa30..a29c474 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -8,6 +8,7 @@
     deps: [
         "soong",
         "soong-android",
+        "soong-bloaty",
         "soong-cc",
         "soong-rust-config",
     ],
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 1a286f7..0f9a17d 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -53,7 +53,7 @@
 		OutputFile: mod.outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.AddStrings("LOCAL_RLIB_LIBRARIES", mod.Properties.AndroidMkRlibs...)
 				entries.AddStrings("LOCAL_DYLIB_LIBRARIES", mod.Properties.AndroidMkDylibs...)
 				entries.AddStrings("LOCAL_PROC_MACRO_LIBRARIES", mod.Properties.AndroidMkProcMacroLibs...)
@@ -89,14 +89,15 @@
 	ctx.SubAndroidMk(ret, test.binaryDecorator)
 
 	ret.Class = "NATIVE_TESTS"
-	ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
-		if test.testConfig != nil {
-			entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
-		}
-		entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
-		entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
-	})
+	ret.ExtraEntries = append(ret.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
+			if test.testConfig != nil {
+				entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
+			}
+			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
+			entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+		})
 
 	cc.AndroidMkWriteTestData(test.data, ret)
 }
@@ -134,13 +135,14 @@
 	ret.Class = "ETC"
 	ret.OutputFile = android.OptionalPathForPath(outFile)
 	ret.SubName += sourceProvider.subName
-	ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		_, file := filepath.Split(outFile.String())
-		stem, suffix, _ := android.SplitFileExt(file)
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-	})
+	ret.ExtraEntries = append(ret.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			_, file := filepath.Split(outFile.String())
+			stem, suffix, _ := android.SplitFileExt(file)
+			entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+			entries.SetString("LOCAL_MODULE_STEM", stem)
+			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+		})
 }
 
 func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
@@ -165,12 +167,13 @@
 		unstrippedOutputFile = ret.OutputFile
 		ret.OutputFile = compiler.strippedOutputFile
 	}
-	ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		entries.SetOptionalPath("LOCAL_SOONG_UNSTRIPPED_BINARY", unstrippedOutputFile)
-		path, file := filepath.Split(compiler.path.ToMakePath().String())
-		stem, suffix, _ := android.SplitFileExt(file)
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-		entries.SetString("LOCAL_MODULE_PATH", path)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
-	})
+	ret.ExtraEntries = append(ret.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.SetOptionalPath("LOCAL_SOONG_UNSTRIPPED_BINARY", unstrippedOutputFile)
+			path, file := filepath.Split(compiler.path.ToMakePath().String())
+			stem, suffix, _ := android.SplitFileExt(file)
+			entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+			entries.SetString("LOCAL_MODULE_PATH", path)
+			entries.SetString("LOCAL_MODULE_STEM", stem)
+		})
 }
diff --git a/rust/builder.go b/rust/builder.go
index 56fe031..6326124 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/bloaty"
 	"android/soong/rust/config"
 )
 
@@ -229,6 +230,8 @@
 		envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
 	}
 
+	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
+
 	if flags.Clippy {
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
 		ctx.Build(pctx, android.BuildParams{
@@ -249,6 +252,8 @@
 		implicits = append(implicits, clippyFile)
 	}
 
+	bloaty.MeasureSizeForPath(ctx, outputFile)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rustc,
 		Description:     "rustc " + main.Rel(),
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 5066428..186e571 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -26,9 +26,10 @@
 	Arm64LinkFlags            = []string{}
 
 	Arm64ArchVariantRustFlags = map[string][]string{
-		"armv8-a":          []string{},
-		"armv8-2a":         []string{},
-		"armv8-2a-dotprod": []string{},
+		"armv8-a":            []string{},
+		"armv8-a-branchprot": []string{},
+		"armv8-2a":           []string{},
+		"armv8-2a-dotprod":   []string{},
 	}
 )
 
diff --git a/rust/config/global.go b/rust/config/global.go
index 182dc6a..12f4972 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.49.0"
+	RustDefaultVersion = "1.50.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 06bb668..7c05e4f 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -52,6 +52,8 @@
 	// deny.
 	defaultClippyLints = []string{
 		"-A clippy::type-complexity",
+		"-A clippy::unnecessary-wraps",
+		"-A clippy::unusual-byte-groupings",
 	}
 
 	// Rust lints for vendor code.
diff --git a/rust/testing.go b/rust/testing.go
index 1afe27e..9534ab5 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/genrule"
 )
 
 func GatherRequiredDepsForTest() string {
@@ -211,6 +212,7 @@
 	ctx := android.NewTestArchContext(config)
 	android.RegisterPrebuiltMutators(ctx)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	genrule.RegisterGenruleBuildComponents(ctx)
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 	RegisterRequiredBuildComponentsForTest(ctx)
 
diff --git a/scripts/Android.bp b/scripts/Android.bp
index b9163cc..310c959 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -209,13 +209,18 @@
     test_suites: ["general-tests"],
 }
 
+python_library_host {
+    name: "ninja_rsp",
+    srcs: ["ninja_rsp.py"],
+}
+
 python_binary_host {
     name: "lint-project-xml",
     main: "lint-project-xml.py",
     srcs: [
         "lint-project-xml.py",
-        "ninja_rsp.py",
     ],
+    libs: ["ninja_rsp"],
 }
 
 python_binary_host {
@@ -223,8 +228,8 @@
     main: "gen-kotlin-build-file.py",
     srcs: [
         "gen-kotlin-build-file.py",
-        "ninja_rsp.py",
     ],
+    libs: ["ninja_rsp"],
 }
 
 python_binary_host {
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index ac67438..0813f1a 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -8,6 +8,7 @@
   com.android.art.testing
   com.android.conscrypt
   com.android.i18n
+  com.android.os.statsd
   com.android.runtime
   com.android.tzdata
 )
@@ -28,6 +29,7 @@
   runtime-module-sdk
   stats-log-api-gen-exports
   statsd-module-sdk
+  statsd-module-sdk-for-art
   tzdata-module-test-exports
 )
 
diff --git a/scripts/check_do_not_merge.sh b/scripts/check_do_not_merge.sh
new file mode 100755
index 0000000..ad6a0a9
--- /dev/null
+++ b/scripts/check_do_not_merge.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# 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.
+
+if git show -s --format=%s $1 | grep -qE '(DO NOT MERGE)|(RESTRICT AUTOMERGE)'; then
+    cat >&2 <<EOF
+DO NOT MERGE and RESTRICT AUTOMERGE very often lead to unintended results
+and are not allowed to be used in this project.
+Please use the Merged-In tag to be more explicit about where this change
+should merge to. Google-internal documentation exists at go/merged-in
+
+If this check is mis-triggering or you know Merged-In is incorrect in this
+situation you can bypass this check with \`repo upload --no-verify\`.
+EOF
+    exit 1
+fi
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index a669cad..af7e7fe 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 python_binary_host {
     name: "merge_csv",
     main: "merge_csv.py",
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
index 6a5b0e1..b047aab 100755
--- a/scripts/hiddenapi/merge_csv.py
+++ b/scripts/hiddenapi/merge_csv.py
@@ -20,13 +20,21 @@
 import argparse
 import csv
 import io
+import heapq
+import itertools
+import operator
 
 from zipfile import ZipFile
 
 args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
 args_parser.add_argument('--header', help='Comma separated field names; '
                                           'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
+args_parser.add_argument('--zip_input', help='Treat files as ZIP archives containing CSV files to merge.',
+                         action="store_true")
+args_parser.add_argument('--key_field', help='The name of the field by which the rows should be sorted. '
+                                             'Must be in the field names. '
+                                             'Will be the first field in the output. '
+                                             'All input files must be sorted by that field.')
 args_parser.add_argument('--output', help='Output file for merged CSV.',
                          default='-', type=argparse.FileType('w'))
 args_parser.add_argument('files', nargs=argparse.REMAINDER)
@@ -36,20 +44,16 @@
 def dict_reader(input):
     return csv.DictReader(input, delimiter=',', quotechar='|')
 
-
-if args.zip_input and len(args.files) > 0:
-    raise ValueError('Expecting either a single ZIP with CSV files'
-                     ' or a list of CSV files as input; not both.')
-
 csv_readers = []
-if len(args.files) > 0:
+if not(args.zip_input):
     for file in args.files:
         csv_readers.append(dict_reader(open(file, 'r')))
-elif args.zip_input:
-    with ZipFile(args.zip_input) as zip:
-        for entry in zip.namelist():
-            if entry.endswith('.uau'):
-                csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+else:
+    for file in args.files:
+        with ZipFile(file) as zip:
+            for entry in zip.namelist():
+                if entry.endswith('.uau'):
+                    csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
 
 headers = set()
 if args.header:
@@ -60,10 +64,29 @@
         headers = headers.union(reader.fieldnames)
     fieldnames = sorted(headers)
 
-# Concatenate all files to output:
+# By default chain the csv readers together so that the resulting output is
+# the concatenation of the rows from each of them:
+all_rows = itertools.chain.from_iterable(csv_readers)
+
+if len(csv_readers) > 0:
+    keyField = args.key_field
+    if keyField:
+        assert keyField in fieldnames, (
+            "--key_field {} not found, must be one of {}\n").format(
+            keyField, ",".join(fieldnames))
+        # Make the key field the first field in the output
+        keyFieldIndex = fieldnames.index(args.key_field)
+        fieldnames.insert(0, fieldnames.pop(keyFieldIndex))
+        # Create an iterable that performs a lazy merge sort on the csv readers
+        # sorting the rows by the key field.
+        all_rows = heapq.merge(*csv_readers, key=operator.itemgetter(keyField))
+
+# Write all rows from the input files to the output:
 writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
                         dialect='unix', fieldnames=fieldnames)
 writer.writeheader()
-for reader in csv_readers:
-    for row in reader:
-        writer.writerow(row)
+
+# Read all the rows from the input and write them to the output in the correct
+# order:
+for row in all_rows:
+  writer.writerow(row)
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 9122da1..0eb1b76 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -48,6 +48,13 @@
                       dest='enforce_uses_libraries',
                       action='store_true',
                       help='check the uses-library entries known to the build system against the manifest')
+  parser.add_argument('--enforce-uses-libraries-relax',
+                      dest='enforce_uses_libraries_relax',
+                      action='store_true',
+                      help='do not fail immediately, just save the error message to file')
+  parser.add_argument('--enforce-uses-libraries-status',
+                      dest='enforce_uses_libraries_status',
+                      help='output file to store check status (error message)')
   parser.add_argument('--extract-target-sdk-version',
                       dest='extract_target_sdk_version',
                       action='store_true',
@@ -57,7 +64,7 @@
   return parser.parse_args()
 
 
-def enforce_uses_libraries(doc, uses_libraries, optional_uses_libraries):
+def enforce_uses_libraries(doc, uses_libraries, optional_uses_libraries, relax):
   """Verify that the <uses-library> tags in the manifest match those provided by the build system.
 
   Args:
@@ -80,10 +87,10 @@
       raise ManifestMismatchError('no <application> tag found')
     return
 
-  verify_uses_library(application, uses_libraries, optional_uses_libraries)
+  return verify_uses_library(application, uses_libraries, optional_uses_libraries, relax)
 
 
-def verify_uses_library(application, uses_libraries, optional_uses_libraries):
+def verify_uses_library(application, uses_libraries, optional_uses_libraries, relax):
   """Verify that the uses-library values known to the build system match the manifest.
 
   Args:
@@ -112,8 +119,12 @@
                (', '.join(optional_uses_libraries), ', '.join(manifest_optional_uses_libraries)))
 
   if err:
-    raise ManifestMismatchError('\n'.join(err))
+    errmsg = '\n'.join(err)
+    if not relax:
+      raise ManifestMismatchError(errmsg)
+    return errmsg
 
+  return None
 
 def parse_uses_library(application):
   """Extract uses-library tags from the manifest.
@@ -195,9 +206,19 @@
     doc = minidom.parse(args.input)
 
     if args.enforce_uses_libraries:
-      enforce_uses_libraries(doc,
-                             args.uses_libraries,
-                             args.optional_uses_libraries)
+      # Check if the <uses-library> lists in the build system agree with those
+      # in the manifest. Raise an exception on mismatch, unless the script was
+      # passed a special parameter to suppress exceptions.
+      errmsg = enforce_uses_libraries(doc, args.uses_libraries,
+        args.optional_uses_libraries, args.enforce_uses_libraries_relax)
+
+      # Create a status file that is empty on success, or contains an error
+      # message on failure. When exceptions are suppressed, dexpreopt command
+      # command will check file size to determine if the check has failed.
+      if args.enforce_uses_libraries_status:
+        with open(args.enforce_uses_libraries_status, 'w') as f:
+          if not errmsg == None:
+            f.write("%s\n" % errmsg)
 
     if args.extract_target_sdk_version:
       print(extract_target_sdk_version(doc))
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 7baad5d..56c2d9e 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -39,7 +39,9 @@
   def run_test(self, input_manifest, uses_libraries=None, optional_uses_libraries=None):
     doc = minidom.parseString(input_manifest)
     try:
-      manifest_check.enforce_uses_libraries(doc, uses_libraries, optional_uses_libraries)
+      relax = False
+      manifest_check.enforce_uses_libraries(doc, uses_libraries,
+        optional_uses_libraries, relax)
       return True
     except manifest_check.ManifestMismatchError:
       return False
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 40f0184..5b7a6da 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -89,7 +89,7 @@
         "${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo"
         "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
         "${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
-        "${XZ}" "${outfile}.mini_debuginfo"
+        "${XZ}" --block-size=64k --threads=0 "${outfile}.mini_debuginfo"
 
         "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
         rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index b1eebe9..3591777 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -22,14 +22,14 @@
 )
 
 var ccTestFs = map[string][]byte{
-	"Test.cpp":                      nil,
-	"include/Test.h":                nil,
-	"include-android/AndroidTest.h": nil,
-	"include-host/HostTest.h":       nil,
-	"arm64/include/Arm64Test.h":     nil,
-	"libfoo.so":                     nil,
-	"aidl/foo/bar/Test.aidl":        nil,
-	"some/where/stubslib.map.txt":   nil,
+	"Test.cpp":                        nil,
+	"myinclude/Test.h":                nil,
+	"myinclude-android/AndroidTest.h": nil,
+	"myinclude-host/HostTest.h":       nil,
+	"arm64/include/Arm64Test.h":       nil,
+	"libfoo.so":                       nil,
+	"aidl/foo/bar/Test.aidl":          nil,
+	"some/where/stubslib.map.txt":     nil,
 }
 
 func testSdkWithCc(t *testing.T, bp string) *testSdkResult {
@@ -102,16 +102,15 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_sdkmember@current",
-    sdk_member_name: "sdkmember",
+    name: "sdkmember",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
-    installable: false,
     stl: "none",
     compile_multilib: "64",
     target: {
@@ -127,13 +126,17 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "sdkmember",
-    prefer: false,
+    name: "mysdk_sdkmember@current",
+    sdk_member_name: "sdkmember",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
+    installable: false,
     stl: "none",
     compile_multilib: "64",
     target: {
@@ -248,6 +251,7 @@
 			uses_sdks: ["mysdk@1"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 
 		apex {
@@ -256,6 +260,7 @@
 			uses_sdks: ["mysdk@2"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 
 		apex {
@@ -263,6 +268,7 @@
 			native_shared_libs: ["sdkmember"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 	`)
 
@@ -348,12 +354,12 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_object {
-    name: "mysdk_crtobj@current",
-    sdk_member_name: "crtobj",
+    name: "crtobj",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     stl: "none",
@@ -370,10 +376,14 @@
         },
     },
 }
+`),
+		// Make sure that the generated sdk_snapshot uses the native_objects property.
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_object {
-    name: "crtobj",
-    prefer: false,
+    name: "mysdk_crtobj@current",
+    sdk_member_name: "crtobj",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     stl: "none",
@@ -416,7 +426,7 @@
 			srcs: [
 				"Test.cpp",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			stl: "none",
 		}
 
@@ -425,14 +435,14 @@
 			srcs: [
 				"Test.cpp",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			stl: "none",
 		}
 	`)
 
 	result.CheckSnapshot("mysdk", "",
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
 .intermediates/mynativelib1/android_arm_armv7-a-neon_shared/mynativelib1.so -> arm/lib/mynativelib1.so
 .intermediates/mynativelib2/android_arm64_armv8-a_shared/mynativelib2.so -> arm64/lib/mynativelib2.so
@@ -441,6 +451,76 @@
 	)
 }
 
+func TestSnapshotWithCcExportGeneratedHeaders(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["mynativelib"],
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			srcs: [
+				"Test.cpp",
+			],
+			generated_headers: [
+				"generated_foo",
+			],
+			export_generated_headers: [
+				"generated_foo",
+			],
+			export_include_dirs: ["myinclude"],
+			stl: "none",
+		}
+
+		genrule {
+			name: "generated_foo",
+			cmd: "generate-foo",
+			out: [
+				"generated_foo/protos/foo/bar.h",
+			],
+			export_include_dirs: [
+				".",
+				"protos",
+			],
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    stl: "none",
+    compile_multilib: "both",
+    export_include_dirs: [
+        "include/myinclude",
+        "include_gen/generated_foo/gen",
+        "include_gen/generated_foo/gen/protos",
+    ],
+    arch: {
+        arm64: {
+            srcs: ["arm64/lib/mynativelib.so"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+        },
+    },
+}
+`),
+		checkAllCopyRules(`
+myinclude/Test.h -> include/myinclude/Test.h
+.intermediates/generated_foo/gen/generated_foo/protos/foo/bar.h -> include_gen/generated_foo/gen/generated_foo/protos/foo/bar.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+	)
+}
+
 // Verify that when the shared library has some common and some arch specific
 // properties that the generated snapshot is optimized properly. Substruct
 // handling is tested with the sanitize clauses (but note there's a lot of
@@ -458,7 +538,7 @@
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			sanitize: {
 				fuzzer: false,
 				integer_overflow: true,
@@ -477,49 +557,17 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-    sanitize: {
-        fuzzer: false,
-        diag: {
-            undefined: false,
-        },
-    },
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mynativelib.so"],
-            export_system_include_dirs: ["arm64/include/arm64/include"],
-            sanitize: {
-                integer_overflow: false,
-            },
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.so"],
-            sanitize: {
-                integer_overflow: true,
-            },
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     sanitize: {
         fuzzer: false,
         diag: {
@@ -542,15 +590,9 @@
         },
     },
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_shared_libs: ["mysdk_mynativelib@current"],
-}
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
@@ -574,15 +616,14 @@
 	`)
 
 	result.CheckSnapshot("mymodule_exports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
-    name: "mymodule_exports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
+    name: "mynativebinary",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    installable: false,
     compile_multilib: "both",
     arch: {
         arm64: {
@@ -593,12 +634,17 @@
         },
     },
 }
+`),
+		// Make sure that the generated sdk_snapshot uses the native_binaries property.
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
-    name: "mynativebinary",
-    prefer: false,
+    name: "mymodule_exports_mynativebinary@current",
+    sdk_member_name: "mynativebinary",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
+    installable: false,
     compile_multilib: "both",
     arch: {
         arm64: {
@@ -655,17 +701,16 @@
 	`)
 
 	result.CheckSnapshot("myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
-    name: "myexports_mynativebinary@current",
-    sdk_member_name: "mynativebinary",
+    name: "mynativebinary",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
-    installable: false,
     stl: "none",
     target: {
         host: {
@@ -691,14 +736,18 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
-    name: "mynativebinary",
-    prefer: false,
+    name: "myexports_mynativebinary@current",
+    sdk_member_name: "mynativebinary",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
+    installable: false,
     stl: "none",
     target: {
         host: {
@@ -805,7 +854,50 @@
 	result := runTests(t, ctx, config)
 
 	result.CheckSnapshot("myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "mynativebinary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic_x86_64: {
+            enabled: true,
+            srcs: ["x86_64/bin/mynativebinary"],
+        },
+    },
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "64",
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_bionic_x86_64: {
+            enabled: true,
+            srcs: ["x86_64/lib/mynativelib.so"],
+        },
+    },
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
@@ -829,26 +921,6 @@
     },
 }
 
-cc_prebuilt_binary {
-    name: "mynativebinary",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/bin/mynativebinary"],
-        },
-    },
-}
-
 cc_prebuilt_library_shared {
     name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
@@ -870,26 +942,6 @@
     },
 }
 
-cc_prebuilt_library_shared {
-    name: "mynativelib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "64",
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_bionic_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.so"],
-        },
-    },
-}
-
 module_exports_snapshot {
     name: "myexports@current",
     visibility: ["//visibility:public"],
@@ -940,17 +992,16 @@
 	`)
 
 	result.CheckSnapshot("mymodule_exports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
-    name: "mymodule_exports_linker@current",
-    sdk_member_name: "linker",
+    name: "linker",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
-    installable: false,
     stl: "none",
     compile_multilib: "both",
     static_executable: true,
@@ -969,14 +1020,18 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_binary {
-    name: "linker",
-    prefer: false,
+    name: "mymodule_exports_linker@current",
+    sdk_member_name: "linker",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
+    installable: false,
     stl: "none",
     compile_multilib: "both",
     static_executable: true,
@@ -1036,7 +1091,7 @@
 				"aidl/foo/bar/Test.aidl",
 			],
 			apex_available: ["apex1", "apex2"],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			aidl: {
 				export_aidl_headers: true,
 			},
@@ -1045,34 +1100,10 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: [
-        "apex1",
-        "apex2",
-    ],
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mynativelib.so"],
-            export_include_dirs: ["arm64/include_gen/mynativelib"],
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.so"],
-            export_include_dirs: ["arm/include_gen/mynativelib"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -1082,35 +1113,29 @@
     ],
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     arch: {
         arm64: {
             srcs: ["arm64/lib/mynativelib.so"],
-            export_include_dirs: ["arm64/include_gen/mynativelib"],
+            export_include_dirs: ["arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl"],
         },
         arm: {
             srcs: ["arm/lib/mynativelib.so"],
-            export_include_dirs: ["arm/include_gen/mynativelib"],
+            export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl"],
         },
     },
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_shared_libs: ["mysdk_mynativelib@current"],
-}
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
-.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h
 `),
 	)
 }
@@ -1176,32 +1201,10 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    shared_libs: [
-        "mysdk_myothernativelib@current",
-        "libc",
-    ],
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mynativelib.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -1223,25 +1226,6 @@
 }
 
 cc_prebuilt_library_shared {
-    name: "mysdk_myothernativelib@current",
-    sdk_member_name: "myothernativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    system_shared_libs: ["libm"],
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/myothernativelib.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/myothernativelib.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "myothernativelib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -1260,24 +1244,6 @@
 }
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mysystemnativelib@current",
-    sdk_member_name: "mysystemnativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mysystemnativelib.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/mysystemnativelib.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "mysystemnativelib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -1293,16 +1259,6 @@
         },
     },
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_shared_libs: [
-        "mysdk_mynativelib@current",
-        "mysdk_myothernativelib@current",
-        "mysdk_mysystemnativelib@current",
-    ],
-}
 `),
 		checkAllCopyRules(`
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
@@ -1332,7 +1288,7 @@
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			aidl: {
 				export_aidl_headers: true,
 			},
@@ -1342,7 +1298,38 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    device_supported: false,
+    host_supported: true,
+    sdk_version: "minimum",
+    stl: "none",
+    compile_multilib: "both",
+    export_include_dirs: ["include/myinclude"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            srcs: ["x86_64/lib/mynativelib.so"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
+        },
+        linux_glibc_x86: {
+            enabled: true,
+            srcs: ["x86/lib/mynativelib.so"],
+            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
+        },
+    },
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
@@ -1356,7 +1343,7 @@
     sdk_version: "minimum",
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     target: {
         host: {
             enabled: false,
@@ -1364,40 +1351,12 @@
         linux_glibc_x86_64: {
             enabled: true,
             srcs: ["x86_64/lib/mynativelib.so"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
         },
         linux_glibc_x86: {
             enabled: true,
             srcs: ["x86/lib/mynativelib.so"],
-            export_include_dirs: ["x86/include_gen/mynativelib"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
-    name: "mynativelib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    sdk_version: "minimum",
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.so"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/lib/mynativelib.so"],
-            export_include_dirs: ["x86/include_gen/mynativelib"],
+            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
         },
     },
 }
@@ -1422,15 +1381,15 @@
 }
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
-.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h
 .intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> x86/lib/mynativelib.so
-.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h
 `),
 	)
 }
@@ -1466,17 +1425,16 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
+    name: "mynativelib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
-    installable: false,
     stl: "none",
     target: {
         host: {
@@ -1502,14 +1460,18 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mynativelib",
-    prefer: false,
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
+    installable: false,
     stl: "none",
     target: {
         host: {
@@ -1582,7 +1544,7 @@
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			aidl: {
 				export_aidl_headers: true,
 			},
@@ -1591,66 +1553,39 @@
 	`)
 
 	result.CheckSnapshot("myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/mynativelib.a"],
-            export_include_dirs: ["arm64/include_gen/mynativelib"],
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.a"],
-            export_include_dirs: ["arm/include_gen/mynativelib"],
-        },
-    },
-}
-
-cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     arch: {
         arm64: {
             srcs: ["arm64/lib/mynativelib.a"],
-            export_include_dirs: ["arm64/include_gen/mynativelib"],
+            export_include_dirs: ["arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl"],
         },
         arm: {
             srcs: ["arm/lib/mynativelib.a"],
-            export_include_dirs: ["arm/include_gen/mynativelib"],
+            export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl"],
         },
     },
 }
-
-module_exports_snapshot {
-    name: "myexports@current",
-    visibility: ["//visibility:public"],
-    native_static_libs: ["myexports_mynativelib@current"],
-}
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
-.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h
 .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
-.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h
 `),
 	)
 }
@@ -1672,7 +1607,7 @@
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			aidl: {
 				export_aidl_headers: true,
 			},
@@ -1681,7 +1616,37 @@
 	`)
 
 	result.CheckSnapshot("myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "both",
+    export_include_dirs: ["include/myinclude"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            srcs: ["x86_64/lib/mynativelib.a"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
+        },
+        linux_glibc_x86: {
+            enabled: true,
+            srcs: ["x86/lib/mynativelib.a"],
+            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
+        },
+    },
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1694,7 +1659,7 @@
     installable: false,
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     target: {
         host: {
             enabled: false,
@@ -1702,39 +1667,12 @@
         linux_glibc_x86_64: {
             enabled: true,
             srcs: ["x86_64/lib/mynativelib.a"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib"],
+            export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
         },
         linux_glibc_x86: {
             enabled: true,
             srcs: ["x86/lib/mynativelib.a"],
-            export_include_dirs: ["x86/include_gen/mynativelib"],
-        },
-    },
-}
-
-cc_prebuilt_library_static {
-    name: "mynativelib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.a"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib"],
-        },
-        linux_glibc_x86: {
-            enabled: true,
-            srcs: ["x86/lib/mynativelib.a"],
-            export_include_dirs: ["x86/include_gen/mynativelib"],
+            export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
         },
     },
 }
@@ -1759,15 +1697,15 @@
 }
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h
 .intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> x86/lib/mynativelib.a
-.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h
 `),
 	)
 }
@@ -1784,7 +1722,7 @@
 			srcs: [
 				"Test.cpp",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			stl: "none",
 			recovery_available: true,
 			vendor_available: true,
@@ -1792,20 +1730,19 @@
 	`)
 
 	result.CheckSnapshot("myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library {
-    name: "myexports_mynativelib@current",
-    sdk_member_name: "mynativelib",
+    name: "mynativelib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    installable: false,
     recovery_available: true,
     vendor_available: true,
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     arch: {
         arm64: {
             static: {
@@ -1825,17 +1762,22 @@
         },
     },
 }
+`),
+		// Make sure that the generated sdk_snapshot uses the native_libs property.
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library {
-    name: "mynativelib",
-    prefer: false,
+    name: "myexports_mynativelib@current",
+    sdk_member_name: "mynativelib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
+    installable: false,
     recovery_available: true,
     vendor_available: true,
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     arch: {
         arm64: {
             static: {
@@ -1863,7 +1805,7 @@
 }
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
@@ -1893,7 +1835,7 @@
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			aidl: {
 				export_aidl_headers: true,
 			},
@@ -1902,7 +1844,34 @@
 	`)
 
 	result.CheckSnapshot("myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "64",
+    export_include_dirs: [
+        "include/myinclude",
+        "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
+    ],
+    target: {
+        host: {
+            enabled: false,
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+            srcs: ["x86_64/lib/mynativelib.a"],
+        },
+    },
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
@@ -1915,7 +1884,10 @@
     installable: false,
     stl: "none",
     compile_multilib: "64",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: [
+        "include/myinclude",
+        "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
+    ],
     target: {
         host: {
             enabled: false,
@@ -1923,29 +1895,6 @@
         linux_glibc_x86_64: {
             enabled: true,
             srcs: ["x86_64/lib/mynativelib.a"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib"],
-        },
-    },
-}
-
-cc_prebuilt_library_static {
-    name: "mynativelib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    device_supported: false,
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "64",
-    export_include_dirs: ["include/include"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-            srcs: ["x86_64/lib/mynativelib.a"],
-            export_include_dirs: ["x86_64/include_gen/mynativelib"],
         },
     },
 }
@@ -1965,13 +1914,14 @@
             enabled: true,
         },
     },
-}`),
+}
+`),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h
 .intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
 `),
 	)
 }
@@ -1985,43 +1935,27 @@
 
 		cc_library_headers {
 			name: "mynativeheaders",
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			stl: "none",
 		}
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
-    name: "mysdk_mynativeheaders@current",
-    sdk_member_name: "mynativeheaders",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    stl: "none",
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-}
-
-cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_header_libs: ["mysdk_mynativeheaders@current"],
+    export_include_dirs: ["include/myinclude"],
 }
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 `),
 	)
 }
@@ -2039,25 +1973,25 @@
 			name: "mynativeheaders",
 			device_supported: false,
 			host_supported: true,
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			stl: "none",
 		}
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
-    name: "mysdk_mynativeheaders@current",
-    sdk_member_name: "mynativeheaders",
+    name: "mynativeheaders",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     target: {
         host: {
             enabled: false,
@@ -2070,17 +2004,20 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
-    name: "mynativeheaders",
-    prefer: false,
+    name: "mysdk_mynativeheaders@current",
+    sdk_member_name: "mynativeheaders",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
     stl: "none",
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     target: {
         host: {
             enabled: false,
@@ -2114,7 +2051,7 @@
 }
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 `),
 	)
 }
@@ -2131,20 +2068,52 @@
 			name: "mynativeheaders",
 			host_supported: true,
 			stl: "none",
-			export_system_include_dirs: ["include"],
+			export_system_include_dirs: ["myinclude"],
 			target: {
 				android: {
-					export_include_dirs: ["include-android"],
+					export_include_dirs: ["myinclude-android"],
 				},
 				host: {
-					export_include_dirs: ["include-host"],
+					export_include_dirs: ["myinclude-host"],
 				},
 			},
 		}
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+    name: "mynativeheaders",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    host_supported: true,
+    stl: "none",
+    compile_multilib: "both",
+    export_system_include_dirs: ["common_os/include/myinclude"],
+    target: {
+        host: {
+            enabled: false,
+        },
+        android: {
+            export_include_dirs: ["android/include/myinclude-android"],
+        },
+        linux_glibc: {
+            export_include_dirs: ["linux_glibc/include/myinclude-host"],
+        },
+        linux_glibc_x86_64: {
+            enabled: true,
+        },
+        linux_glibc_x86: {
+            enabled: true,
+        },
+    },
+}
+`),
+		// Verifi
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_headers {
@@ -2155,44 +2124,16 @@
     host_supported: true,
     stl: "none",
     compile_multilib: "both",
-    export_system_include_dirs: ["common_os/include/include"],
+    export_system_include_dirs: ["common_os/include/myinclude"],
     target: {
         host: {
             enabled: false,
         },
         android: {
-            export_include_dirs: ["android/include/include-android"],
+            export_include_dirs: ["android/include/myinclude-android"],
         },
         linux_glibc: {
-            export_include_dirs: ["linux_glibc/include/include-host"],
-        },
-        linux_glibc_x86_64: {
-            enabled: true,
-        },
-        linux_glibc_x86: {
-            enabled: true,
-        },
-    },
-}
-
-cc_prebuilt_library_headers {
-    name: "mynativeheaders",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    stl: "none",
-    compile_multilib: "both",
-    export_system_include_dirs: ["common_os/include/include"],
-    target: {
-        host: {
-            enabled: false,
-        },
-        android: {
-            export_include_dirs: ["android/include/include-android"],
-        },
-        linux_glibc: {
-            export_include_dirs: ["linux_glibc/include/include-host"],
+            export_include_dirs: ["linux_glibc/include/myinclude-host"],
         },
         linux_glibc_x86_64: {
             enabled: true,
@@ -2222,9 +2163,9 @@
 }
 `),
 		checkAllCopyRules(`
-include/Test.h -> common_os/include/include/Test.h
-include-android/AndroidTest.h -> android/include/include-android/AndroidTest.h
-include-host/HostTest.h -> linux_glibc/include/include-host/HostTest.h
+myinclude/Test.h -> common_os/include/myinclude/Test.h
+myinclude-android/AndroidTest.h -> android/include/myinclude-android/AndroidTest.h
+myinclude-host/HostTest.h -> linux_glibc/include/myinclude-host/HostTest.h
 `),
 	)
 }
@@ -2253,27 +2194,10 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_sslnil@current",
-    sdk_member_name: "sslnil",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/sslnil.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/sslnil.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "sslnil",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -2290,24 +2214,6 @@
 }
 
 cc_prebuilt_library_shared {
-    name: "mysdk_sslempty@current",
-    sdk_member_name: "sslempty",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    system_shared_libs: [],
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/sslempty.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/sslempty.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "sslempty",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -2325,24 +2231,6 @@
 }
 
 cc_prebuilt_library_shared {
-    name: "mysdk_sslnonempty@current",
-    sdk_member_name: "sslnonempty",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    system_shared_libs: ["mysdk_sslnil@current"],
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/sslnonempty.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/sslnonempty.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "sslnonempty",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -2358,16 +2246,6 @@
         },
     },
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_shared_libs: [
-        "mysdk_sslnil@current",
-        "mysdk_sslempty@current",
-        "mysdk_sslnonempty@current",
-    ],
-}
 `))
 
 	result = testSdkWithCc(t, `
@@ -2389,16 +2267,15 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_sslvariants@current",
-    sdk_member_name: "sslvariants",
+    name: "sslvariants",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
-    installable: false,
     compile_multilib: "both",
     target: {
         host: {
@@ -2423,13 +2300,17 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "sslvariants",
-    prefer: false,
+    name: "mysdk_sslvariants@current",
+    sdk_member_name: "sslvariants",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
+    installable: false,
     compile_multilib: "both",
     target: {
         host: {
@@ -2497,34 +2378,10 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_stubslib@current",
-    sdk_member_name: "stubslib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    stubs: {
-        versions: [
-            "1",
-            "2",
-            "3",
-        ],
-    },
-    arch: {
-        arm64: {
-            srcs: ["arm64/lib/stubslib.so"],
-        },
-        arm: {
-            srcs: ["arm/lib/stubslib.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "stubslib",
     prefer: false,
     visibility: ["//visibility:public"],
@@ -2546,12 +2403,6 @@
         },
     },
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_shared_libs: ["mysdk_stubslib@current"],
-}
 `))
 }
 
@@ -2580,16 +2431,15 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_stubslib@current",
-    sdk_member_name: "stubslib",
+    name: "stubslib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
-    installable: false,
     compile_multilib: "both",
     stubs: {
         versions: [
@@ -2618,13 +2468,17 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "stubslib",
-    prefer: false,
+    name: "mysdk_stubslib@current",
+    sdk_member_name: "stubslib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
+    installable: false,
     compile_multilib: "both",
     stubs: {
         versions: [
@@ -2690,16 +2544,15 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mylib@current",
-    sdk_member_name: "mylib",
+    name: "mylib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
-    installable: false,
     unique_host_soname: true,
     compile_multilib: "both",
     target: {
@@ -2722,13 +2575,17 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mylib",
-    prefer: false,
+    name: "mysdk_mylib@current",
+    sdk_member_name: "mylib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
+    installable: false,
     unique_host_soname: true,
     compile_multilib: "both",
     target: {
@@ -2789,7 +2646,7 @@
 		cc_library_shared {
 			name: "mynativelib",
 			srcs: ["Test.cpp"],
-			export_include_dirs: ["include"],
+			export_include_dirs: ["myinclude"],
 			arch: {
 				arm64: {
 					export_system_include_dirs: ["arm64/include"],
@@ -2802,34 +2659,16 @@
 	`)
 
 	result.CheckSnapshot("mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_shared {
-    name: "mysdk_mynativelib@current",
-    sdk_member_name: "mynativelib",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    installable: false,
-    compile_multilib: "both",
-    export_include_dirs: ["include/include"],
-    arch: {
-        arm64: {
-            export_system_include_dirs: ["arm64/include/arm64/include"],
-        },
-        arm: {
-            srcs: ["arm/lib/mynativelib.so"],
-        },
-    },
-}
-
-cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     compile_multilib: "both",
-    export_include_dirs: ["include/include"],
+    export_include_dirs: ["include/myinclude"],
     arch: {
         arm64: {
             export_system_include_dirs: ["arm64/include/arm64/include"],
@@ -2839,15 +2678,9 @@
         },
     },
 }
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    native_shared_libs: ["mysdk_mynativelib@current"],
-}
 `),
 		checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
 arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
 	)
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 17a6ca9..111b22c 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -211,6 +211,7 @@
 			uses_sdks: ["mysdk@1"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 
 		apex {
@@ -219,6 +220,7 @@
 			uses_sdks: ["mysdk@2"],
 			key: "myapex.key",
 			certificate: ":myapex.cert",
+			updatable: false,
 		}
 	`)
 
diff --git a/sdk/testing.go b/sdk/testing.go
index 1ac873b..7a2540a 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -26,6 +26,7 @@
 	"android/soong/android"
 	"android/soong/apex"
 	"android/soong/cc"
+	"android/soong/genrule"
 	"android/soong/java"
 )
 
@@ -109,6 +110,9 @@
 	// from java package
 	java.RegisterRequiredBuildComponentsForTest(ctx)
 
+	// from genrule package
+	genrule.RegisterGenruleBuildComponents(ctx)
+
 	// from cc package
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
 
@@ -204,7 +208,11 @@
 
 func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
 	h.t.Helper()
-	h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
+	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{}) {
@@ -243,11 +251,11 @@
 // e.g. find the src/dest pairs from each cp command, the various zip files
 // generated, etc.
 func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
-	androidBpContents := sdk.GetAndroidBpContentsForTests()
-
 	info := &snapshotBuildInfo{
-		r:                 r,
-		androidBpContents: androidBpContents,
+		r:                            r,
+		androidBpContents:            sdk.GetAndroidBpContentsForTests(),
+		androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
+		androidVersionedBpContents:   sdk.GetVersionedAndroidBpContentsForTests(),
 	}
 
 	buildParams := sdk.BuildParamsForTests()
@@ -365,6 +373,33 @@
 	}
 }
 
+// Check that the snapshot's unversioned generated Android.bp is correct.
+//
+// This func should be used to check the general snapshot generation code.
+//
+// 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.AssertTrimmedStringEquals("unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents)
+	}
+}
+
+// Check that the snapshot's versioned generated Android.bp is correct.
+//
+// This func should only be used to check the version specific snapshot generation code,
+// i.e. the encoding of version into module names and the generation of the _snapshot module. The
+// general snapshot generation code should be checked using the checkUnversionedAndroidBpContents()
+// func.
+//
+// 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.AssertTrimmedStringEquals("versioned Android.bp contents do not match", expected, info.androidVersionedBpContents)
+	}
+}
+
 // Check that the snapshot's copy rules are correct.
 //
 // The copy rules are formatted as <src> -> <dest>, one per line and then compared
@@ -407,6 +442,12 @@
 	// The contents of the generated Android.bp file
 	androidBpContents string
 
+	// The contents of the unversioned Android.bp file
+	androidUnversionedBpContents string
+
+	// The contents of the versioned Android.bp file
+	androidVersionedBpContents string
+
 	// The paths, relative to the snapshot root, of all files and directories copied into the
 	// snapshot.
 	snapshotContents []string
diff --git a/sdk/update.go b/sdk/update.go
index 377aaae..828c7b6 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -572,12 +572,20 @@
 }
 
 func generateBpContents(contents *generatedContents, bpFile *bpFile) {
+	generateFilteredBpContents(contents, bpFile, func(*bpModule) bool {
+		return true
+	})
+}
+
+func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
 	contents.Printfln("// This is auto-generated. DO NOT EDIT.")
 	for _, bpModule := range bpFile.order {
-		contents.Printfln("")
-		contents.Printfln("%s {", bpModule.moduleType)
-		outputPropertySet(contents, bpModule.bpPropertySet)
-		contents.Printfln("}")
+		if moduleFilter(bpModule) {
+			contents.Printfln("")
+			contents.Printfln("%s {", bpModule.moduleType)
+			outputPropertySet(contents, bpModule.bpPropertySet)
+			contents.Printfln("}")
+		}
 	}
 }
 
@@ -639,6 +647,22 @@
 	return contents.content.String()
 }
 
+func (s *sdk) GetUnversionedAndroidBpContentsForTests() string {
+	contents := &generatedContents{}
+	generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
+		return !strings.Contains(module.properties["name"].(string), "@")
+	})
+	return contents.content.String()
+}
+
+func (s *sdk) GetVersionedAndroidBpContentsForTests() string {
+	contents := &generatedContents{}
+	generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
+		return strings.Contains(module.properties["name"].(string), "@")
+	})
+	return contents.content.String()
+}
+
 type snapshotBuilder struct {
 	ctx         android.ModuleContext
 	sdk         *sdk
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 54dfc24..031cd47 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -84,9 +84,6 @@
 
 	// Make this module available when building for recovery.
 	Recovery_available *bool
-
-	// Properties for Bazel migration purposes.
-	bazel.Properties
 }
 
 type TestProperties struct {
@@ -132,6 +129,7 @@
 
 type ShBinary struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties shBinaryProperties
 
@@ -246,7 +244,7 @@
 		OutputFile: android.OptionalPathForPath(s.outputFilePath),
 		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
 				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
 			},
@@ -395,7 +393,7 @@
 		OutputFile: android.OptionalPathForPath(s.outputFilePath),
 		Include:    "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
 				entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
 				entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
@@ -427,6 +425,7 @@
 
 func InitShBinaryModule(s *ShBinary) {
 	s.AddProperties(&s.properties)
+	android.InitBazelModule(s)
 }
 
 // sh_binary is for a shell script or batch file to be installed as an
@@ -504,7 +503,7 @@
 
 func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*ShBinary)
-	if !ok || !m.properties.Bazel_module.Bp2build_available {
+	if !ok || !m.ConvertWithBp2build() {
 		return
 	}
 
@@ -514,9 +513,11 @@
 		Srcs: srcs,
 	}
 
-	props := bazel.NewBazelTargetModuleProperties(m.Name(), "sh_binary", "")
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class: "sh_binary",
+	}
 
-	ctx.CreateBazelTargetModule(BazelShBinaryFactory, props, attrs)
+	ctx.CreateBazelTargetModule(BazelShBinaryFactory, m.Name(), props, attrs)
 }
 
 func (m *bazelShBinary) Name() string {
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index c664461..f48f7fb 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"
@@ -61,7 +62,7 @@
 }
 
 func TestShTestSubDir(t *testing.T) {
-	ctx, config := testShBinary(t, `
+	ctx, _ := testShBinary(t, `
 		sh_test {
 			name: "foo",
 			src: "test.sh",
@@ -71,9 +72,10 @@
 
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
 
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	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)
@@ -81,7 +83,7 @@
 }
 
 func TestShTest(t *testing.T) {
-	ctx, config := testShBinary(t, `
+	ctx, _ := testShBinary(t, `
 		sh_test {
 			name: "foo",
 			src: "test.sh",
@@ -95,9 +97,10 @@
 
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
 
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	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)
@@ -111,7 +114,7 @@
 }
 
 func TestShTest_dataModules(t *testing.T) {
-	ctx, config := testShBinary(t, `
+	ctx, _ := testShBinary(t, `
 		sh_test {
 			name: "foo",
 			src: "test.sh",
@@ -157,7 +160,7 @@
 		}
 
 		mod := variant.Module().(*ShTest)
-		entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+		entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 		expectedData := []string{
 			filepath.Join(buildDir, ".intermediates/bar", arch, ":bar"),
 			filepath.Join(buildDir, ".intermediates/foo", arch, "relocated/:lib64/libbar"+libExt),
@@ -190,7 +193,7 @@
 }
 
 func TestShTestHost_dataDeviceModules(t *testing.T) {
-	ctx, config := testShBinary(t, `
+	ctx, _ := testShBinary(t, `
 		sh_test_host {
 			name: "foo",
 			src: "test.sh",
@@ -227,7 +230,7 @@
 	}
 
 	mod := variant.Module().(*ShTest)
-	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 	expectedData := []string{
 		filepath.Join(buildDir, ".intermediates/bar/android_arm64_armv8-a/:bar"),
 		// libbar has been relocated, and so has a variant that matches the host arch.
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 2ccce15..4ad68ea 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -37,9 +37,10 @@
 }
 
 type syspropGenProperties struct {
-	Srcs  []string `android:"path"`
-	Scope string
-	Name  *string
+	Srcs      []string `android:"path"`
+	Scope     string
+	Name      *string
+	Check_api *string
 }
 
 type syspropJavaGenRule struct {
@@ -68,10 +69,6 @@
 func init() {
 	pctx.HostBinToolVariable("soongZipCmd", "soong_zip")
 	pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java")
-
-	android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
-	})
 }
 
 // syspropJavaGenRule module generates srcjar containing generated java APIs.
@@ -103,6 +100,12 @@
 	}
 }
 
+func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Add a dependency from the stubs to sysprop library so that the generator rule can depend on
+	// the check API rule of the sysprop library.
+	ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api))
+}
+
 func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
@@ -157,9 +160,6 @@
 	// If set to true, build a variant of the module for the host.  Defaults to false.
 	Host_supported *bool
 
-	// Whether public stub exists or not.
-	Public_stub *bool `blueprint:"mutated"`
-
 	Cpp struct {
 		// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
 		// Forwarded to cc_library.min_sdk_version
@@ -202,11 +202,8 @@
 	return "lib" + m.BaseModuleName()
 }
 
-func (m *syspropLibrary) JavaPublicStubName() string {
-	if proptools.Bool(m.properties.Public_stub) {
-		return m.BaseModuleName() + "_public"
-	}
-	return ""
+func (m *syspropLibrary) javaPublicStubName() string {
+	return m.BaseModuleName() + "_public"
 }
 
 func (m *syspropLibrary) javaGenModuleName() string {
@@ -221,10 +218,6 @@
 	return m.ModuleBase.Name()
 }
 
-func (m *syspropLibrary) HasPublicStub() bool {
-	return proptools.Bool(m.properties.Public_stub)
-}
-
 func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath {
 	return m.currentApiFile
 }
@@ -399,16 +392,17 @@
 }
 
 type javaLibraryProperties struct {
-	Name             *string
-	Srcs             []string
-	Soc_specific     *bool
-	Device_specific  *bool
-	Product_specific *bool
-	Required         []string
-	Sdk_version      *string
-	Installable      *bool
-	Libs             []string
-	Stem             *string
+	Name              *string
+	Srcs              []string
+	Soc_specific      *bool
+	Device_specific   *bool
+	Product_specific  *bool
+	Required          []string
+	Sdk_version       *string
+	Installable       *bool
+	Libs              []string
+	Stem              *string
+	SyspropPublicStub string
 }
 
 func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
@@ -490,35 +484,42 @@
 	// Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed
 	// to Java implementation library.
 	ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
-		Srcs:  m.properties.Srcs,
-		Scope: scope,
-		Name:  proptools.StringPtr(m.javaGenModuleName()),
-	})
-
-	ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
-		Name:             proptools.StringPtr(m.BaseModuleName()),
-		Srcs:             []string{":" + m.javaGenModuleName()},
-		Soc_specific:     proptools.BoolPtr(ctx.SocSpecific()),
-		Device_specific:  proptools.BoolPtr(ctx.DeviceSpecific()),
-		Product_specific: proptools.BoolPtr(ctx.ProductSpecific()),
-		Installable:      m.properties.Installable,
-		Sdk_version:      proptools.StringPtr("core_current"),
-		Libs:             []string{javaSyspropStub},
+		Srcs:      m.properties.Srcs,
+		Scope:     scope,
+		Name:      proptools.StringPtr(m.javaGenModuleName()),
+		Check_api: proptools.StringPtr(ctx.ModuleName()),
 	})
 
 	// if platform sysprop_library is installed in /system or /system-ext, we regard it as an API
 	// and allow any modules (even from different partition) to link against the sysprop_library.
 	// To do that, we create a public stub and expose it to modules with sdk_version: system_*.
+	var publicStub string
 	if isOwnerPlatform && installedInSystem {
-		m.properties.Public_stub = proptools.BoolPtr(true)
+		publicStub = m.javaPublicStubName()
+	}
+
+	ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
+		Name:              proptools.StringPtr(m.BaseModuleName()),
+		Srcs:              []string{":" + m.javaGenModuleName()},
+		Soc_specific:      proptools.BoolPtr(ctx.SocSpecific()),
+		Device_specific:   proptools.BoolPtr(ctx.DeviceSpecific()),
+		Product_specific:  proptools.BoolPtr(ctx.ProductSpecific()),
+		Installable:       m.properties.Installable,
+		Sdk_version:       proptools.StringPtr("core_current"),
+		Libs:              []string{javaSyspropStub},
+		SyspropPublicStub: publicStub,
+	})
+
+	if publicStub != "" {
 		ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
-			Srcs:  m.properties.Srcs,
-			Scope: "public",
-			Name:  proptools.StringPtr(m.javaGenPublicStubName()),
+			Srcs:      m.properties.Srcs,
+			Scope:     "public",
+			Name:      proptools.StringPtr(m.javaGenPublicStubName()),
+			Check_api: proptools.StringPtr(ctx.ModuleName()),
 		})
 
 		ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
-			Name:        proptools.StringPtr(m.JavaPublicStubName()),
+			Name:        proptools.StringPtr(publicStub),
 			Srcs:        []string{":" + m.javaGenPublicStubName()},
 			Installable: proptools.BoolPtr(false),
 			Sdk_version: proptools.StringPtr("core_current"),
@@ -537,15 +538,3 @@
 		*libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName())
 	}
 }
-
-// syspropDepsMutator adds dependencies from java implementation library to sysprop library.
-// java implementation library then depends on check API rule of sysprop library.
-func syspropDepsMutator(ctx android.BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(*syspropLibrary); ok {
-		ctx.AddReverseDependency(m, nil, m.javaGenModuleName())
-
-		if proptools.Bool(m.properties.Public_stub) {
-			ctx.AddReverseDependency(m, nil, m.javaGenPublicStubName())
-		}
-	}
-}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 5cb9e64..9d914e3 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -26,7 +26,6 @@
 	"strings"
 	"testing"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -61,16 +60,10 @@
 	java.RegisterRequiredBuildComponentsForTest(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
-	})
 
 	android.RegisterPrebuiltMutators(ctx)
 
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("sysprop_java", java.SyspropMutator).Parallel()
-	})
 
 	ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory)
 
@@ -392,15 +385,9 @@
 	}
 
 	// Java modules linking against system API should use public stub
-	javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common")
-	publicStubFound := false
-	ctx.VisitDirectDeps(javaSystemApiClient.Module(), func(dep blueprint.Module) {
-		if dep.Name() == "sysprop-platform_public" {
-			publicStubFound = true
-		}
-	})
-	if !publicStubFound {
-		t.Errorf("system api client should use public stub")
+	javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common").Rule("javac")
+	syspropPlatformPublic := ctx.ModuleForTests("sysprop-platform_public", "android_common").Description("for turbine")
+	if g, w := javaSystemApiClient.Implicits.Strings(), syspropPlatformPublic.Output.String(); !android.InList(w, g) {
+		t.Errorf("system api client should use public stub %q, got %q", w, g)
 	}
-
 }
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/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/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..c2fa427 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, _ := config.Environment().Get(k)
+			return v
+		}
+		if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
+			os.Remove(envFile)
 		}
 	}()
 
@@ -151,14 +170,12 @@
 			"--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())
+		cmd.Environment.Set("SOONG_OUTDIR", config.SoongOutDir())
+		if os.Getenv("SOONG_DELVE") != "" {
+			// SOONG_DELVE is already in cmd.Environment
+			cmd.Environment.Set("SOONG_DELVE_PATH", shared.ResolveDelveBinary())
+		}
 
-		cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}
diff --git a/vnames.go.json b/vnames.go.json
index 7ce2d4b..f8c6b7f 100644
--- a/vnames.go.json
+++ b/vnames.go.json
@@ -2,7 +2,6 @@
     {
         "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@"
     }
   }
 ]
-