Merge "ApiLevel of "" and "core_platform" is FutureApiLevel"
diff --git a/android/Android.bp b/android/Android.bp
index f5e5606..62d5e20 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -26,6 +26,7 @@
         "arch_list.go",
         "bazel.go",
         "bazel_handler.go",
+        "bazel_paths.go",
         "config.go",
         "csuite_config.go",
         "deapexer.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 66a1036..618e4be 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -505,6 +505,8 @@
 	// TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ?
 	if amod.commonProperties.Effective_package_name != nil {
 		a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *amod.commonProperties.Effective_package_name)
+	} else if len(amod.commonProperties.Effective_licenses) > 0 {
+		a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(amod.commonProperties.Effective_licenses, " "))
 	}
 	a.SetString("LOCAL_MODULE_CLASS", a.Class)
 	a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
diff --git a/android/arch.go b/android/arch.go
index 6826f3b..9f93752 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -19,7 +19,6 @@
 	"fmt"
 	"reflect"
 	"runtime"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -176,7 +175,7 @@
 // MarshalText allows an ArchType to be serialized through any encoder that supports
 // encoding.TextMarshaler.
 func (a ArchType) MarshalText() ([]byte, error) {
-	return []byte(strconv.Quote(a.String())), nil
+	return []byte(a.String()), nil
 }
 
 var _ encoding.TextMarshaler = ArchType{}
@@ -618,7 +617,7 @@
 	}
 
 	// only the primary arch in the ramdisk / vendor_ramdisk / recovery partition
-	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk()) {
+	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk() || module.InstallInDebugRamdisk()) {
 		osTargets = []Target{osTargets[0]}
 	}
 
diff --git a/android/bazel.go b/android/bazel.go
index b3f9d88..260f1e5 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -129,6 +129,7 @@
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	bp2buildDefaultConfig = Bp2BuildConfig{
 		"bionic":                Bp2BuildDefaultTrueRecursively,
+		"external/gwp_asan":     Bp2BuildDefaultTrueRecursively,
 		"system/core/libcutils": Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
 	}
@@ -138,32 +139,25 @@
 		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static, 'map' file not found
 		"libbionic_spawn_benchmark",     // ruperts@, cc_library_static, depends on //system/libbase
 		"libc_jemalloc_wrapper",         // ruperts@, cc_library_static, depends on //external/jemalloc_new
-		"libc_bootstrap",                // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_init_static",              // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_init_dynamic",             // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_tzcode",                   // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_freebsd",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_freebsd_large_stack",      // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_netbsd",                   // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_openbsd_ndk",              // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_openbsd_large_stack",      // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_openbsd",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_gdtoa",                    // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_fortify",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_bionic",                   // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_bootstrap",                // ruperts@, cc_library_static, 'private/bionic_auxv.h' file not found
+		"libc_init_static",              // ruperts@, cc_library_static, 'private/bionic_elf_tls.h' file not found
+		"libc_init_dynamic",             // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
+		"libc_tzcode",                   // ruperts@, cc_library_static, error: expected expression
+		"libc_netbsd",                   // ruperts@, cc_library_static, 'engine.c' file not found
+		"libc_openbsd_large_stack",      // ruperts@, cc_library_static, 'android/log.h' file not found
+		"libc_openbsd",                  // ruperts@, cc_library_static, 'android/log.h' file not found
+		"libc_fortify",                  // ruperts@, cc_library_static, 'private/bionic_fortify.h' file not found
+		"libc_bionic",                   // ruperts@, cc_library_static, 'private/bionic_asm.h' file not found
 		"libc_bionic_ndk",               // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
-		"libc_bionic_systrace",          // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_pthread",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_bionic_systrace",          // ruperts@, cc_library_static, 'private/bionic_systrace.h' file not found
+		"libc_pthread",                  // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
 		"libc_syscalls",                 // ruperts@, cc_library_static, mutator panic cannot get direct dep syscalls-arm64.S of libc_syscalls
-		"libc_aeabi",                    // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 		"libc_ndk",                      // ruperts@, cc_library_static, depends on //bionic/libm:libm
 		"libc_nopthread",                // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
 		"libc_common",                   // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
-		"libc_static_dispatch",          // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-		"libc_dynamic_dispatch",         // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 		"libc_common_static",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
 		"libc_common_shared",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libc_unwind_static",            // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_unwind_static",            // ruperts@, cc_library_static, 'private/bionic_elf_tls.h' file not found
 		"libc_nomalloc",                 // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
 		"libasync_safe",                 // ruperts@, cc_library_static, 'private/CachedProperty.h' file not found
 		"libc_malloc_debug_backtrace",   // ruperts@, cc_library_static, depends on //system/libbase
@@ -173,10 +167,9 @@
 		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
 		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
 		"libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'complex.h' file not found
-		"libc_dns",                      // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
-
-		"note_memtag_heap_async", // jingwen@, b/185079815, features.h includes not found
-		"note_memtag_heap_sync",  // jingwen@, b/185079815, features.h includes not found
+		"libc_dns",                      // ruperts@, cc_library_static, 'android/log.h' file not found
+		"libc_static_dispatch",          // eakammer@, cc_library_static, 'private/bionic_asm.h' file not found
+		"libc_dynamic_dispatch",         // eakammer@, cc_library_static, 'private/bionic_ifuncs.h' file not found
 
 		// List of all full_cc_libraries in //bionic, with their immediate failures
 		"libc",              // jingwen@, cc_library, depends on //external/gwp_asan
@@ -186,6 +179,11 @@
 		"libm",              // jingwen@, cc_library, fatal error: 'freebsd-compat.h' file not found
 		"libseccomp_policy", // jingwen@, cc_library, fatal error: 'seccomp_policy.h' file not found
 		"libstdc++",         // jingwen@, cc_library, depends on //external/gwp_asan
+
+		// For mixed builds specifically
+		"note_memtag_heap_async", // jingwen@, cc_library_static, OK for bp2build but features.h includes not found for mixed builds (b/185079815)
+		"note_memtag_heap_sync",  // jingwen@, cc_library_static, OK for bp2build but features.h includes not found for mixed builds (b/185079815)
+		"libc_gdtoa",             // ruperts@, cc_library_static, OK for bp2build but undefined symbol: __strtorQ for mixed builds
 	}
 
 	// Used for quicker lookups
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 2697007..8d561d2 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -737,7 +737,7 @@
 
 	// Add ninja file dependencies for files which all bazel invocations require.
 	bazelBuildList := absolutePath(filepath.Join(
-		filepath.Dir(bootstrap.CmdlineModuleListFile()), "bazel.list"))
+		filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list"))
 	ctx.AddNinjaFileDeps(bazelBuildList)
 
 	data, err := ioutil.ReadFile(bazelBuildList)
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
new file mode 100644
index 0000000..13f4949
--- /dev/null
+++ b/android/bazel_paths.go
@@ -0,0 +1,353 @@
+// 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 android
+
+import (
+	"android/soong/bazel"
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
+)
+
+// bazel_paths contains methods to:
+//   * resolve Soong path and module references into bazel.LabelList
+//   * resolve Bazel path references into Soong-compatible paths
+//
+// There is often a similar method for Bazel as there is for Soong path handling and should be used
+// in similar circumstances
+//
+// Bazel                                Soong
+//
+// BazelLabelForModuleSrc               PathForModuleSrc
+// BazelLabelForModuleSrcExcludes       PathForModuleSrcExcludes
+// BazelLabelForModuleDeps              n/a
+// tbd                                  PathForSource
+// tbd                                  ExistentPathsForSources
+// PathForBazelOut                      PathForModuleOut
+//
+// Use cases:
+//  * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
+//    module directory*:
+//     * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_<propname> property
+//     * BazelLabelForModuleSrc, otherwise
+//  * Converting references to other modules to Bazel Labels:
+//     BazelLabelForModuleDeps
+//  * Converting a path obtained from bazel_handler cquery results:
+//     PathForBazelOut
+//
+// NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob
+//       syntax. This occurs because Soong does not have a concept of crossing package boundaries,
+//       so the glob as computed by Soong may contain paths that cross package-boundaries. These
+//       would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within
+//       Soong, we support identification and detection (within Bazel) use of paths that cross
+//       package boundaries.
+//
+// Path resolution:
+// * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g.
+//   //path/to/dir:<filepath>) if path exists in a separate package or subpackage.
+// * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label
+//   for a target. If the Bazel target is in the local module directory, it will be returned
+//   relative to the current package (e.g.  ":<target>"). Otherwise, it will be returned as an
+//   absolute Bazel label (e.g.  "//path/to/dir:<target>"). If the reference to another module
+//   cannot be resolved,the function will panic. This is often due to the dependency not being added
+//   via an AddDependency* method.
+
+// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
+// order to form a Bazel-compatible label for conversion.
+type BazelConversionPathContext interface {
+	EarlyModulePathContext
+
+	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+	Module() Module
+	ModuleType() string
+	OtherModuleName(m blueprint.Module) string
+	OtherModuleDir(m blueprint.Module) string
+}
+
+// BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
+// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
+// module within the given ctx.
+func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+	var labels bazel.LabelList
+	for _, module := range modules {
+		bpText := module
+		if m := SrcIsModule(module); m == "" {
+			module = ":" + module
+		}
+		if m, t := SrcIsModuleWithTag(module); m != "" {
+			l := getOtherModuleLabel(ctx, m, t)
+			l.Bp_text = bpText
+			labels.Includes = append(labels.Includes, l)
+		} else {
+			ctx.ModuleErrorf("%q, is not a module reference", module)
+		}
+	}
+	return labels
+}
+
+// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
+// references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
+// paths, relative to the local module, or Bazel-labels (absolute if in a different package or
+// relative if within the same package).
+// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
+// will have already been handled by the path_deps mutator.
+func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
+	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
+}
+
+// BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory)
+// and module references (":<module>") and returns a bazel.LabelList{} containing the resolved
+// references in paths, minus those in excludes, relative to the local module, or Bazel-labels
+// (absolute if in a different package or relative if within the same package).
+// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
+// will have already been handled by the path_deps mutator.
+func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
+	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
+	excluded := make([]string, 0, len(excludeLabels.Includes))
+	for _, e := range excludeLabels.Includes {
+		excluded = append(excluded, e.Label)
+	}
+	labels := expandSrcsForBazel(ctx, paths, excluded)
+	labels.Excludes = excludeLabels.Includes
+	labels = transformSubpackagePaths(ctx, labels)
+	return labels
+}
+
+// Returns true if a prefix + components[:i] + /Android.bp exists
+// TODO(b/185358476) Could check for BUILD file instead of checking for Android.bp file, or ensure BUILD is always generated?
+func directoryHasBlueprint(fs pathtools.FileSystem, prefix string, components []string, componentIndex int) bool {
+	blueprintPath := prefix
+	if blueprintPath != "" {
+		blueprintPath = blueprintPath + "/"
+	}
+	blueprintPath = blueprintPath + strings.Join(components[:componentIndex+1], "/")
+	blueprintPath = blueprintPath + "/Android.bp"
+	if exists, _, _ := fs.Exists(blueprintPath); exists {
+		return true
+	} else {
+		return false
+	}
+}
+
+// Transform a path (if necessary) to acknowledge package boundaries
+//
+// e.g. something like
+//   async_safe/include/async_safe/CHECK.h
+// might become
+//   //bionic/libc/async_safe:include/async_safe/CHECK.h
+// if the "async_safe" directory is actually a package and not just a directory.
+//
+// In particular, paths that extend into packages are transformed into absolute labels beginning with //.
+func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label {
+	var newPath bazel.Label
+
+	// Don't transform Bp_text
+	newPath.Bp_text = path.Bp_text
+
+	if strings.HasPrefix(path.Label, "//") {
+		// Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h)
+		newPath.Label = path.Label
+		return newPath
+	}
+
+	newLabel := ""
+	pathComponents := strings.Split(path.Label, "/")
+	foundBlueprint := false
+	// Check the deepest subdirectory first and work upwards
+	for i := len(pathComponents) - 1; i >= 0; i-- {
+		pathComponent := pathComponents[i]
+		var sep string
+		if !foundBlueprint && directoryHasBlueprint(ctx.Config().fs, ctx.ModuleDir(), pathComponents, i) {
+			sep = ":"
+			foundBlueprint = true
+		} else {
+			sep = "/"
+		}
+		if newLabel == "" {
+			newLabel = pathComponent
+		} else {
+			newLabel = pathComponent + sep + newLabel
+		}
+	}
+	if foundBlueprint {
+		// Ensure paths end up looking like //bionic/... instead of //./bionic/...
+		moduleDir := ctx.ModuleDir()
+		if strings.HasPrefix(moduleDir, ".") {
+			moduleDir = moduleDir[1:]
+		}
+		// Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h)
+		if moduleDir == "" {
+			newLabel = "//" + newLabel
+		} else {
+			newLabel = "//" + moduleDir + "/" + newLabel
+		}
+	}
+	newPath.Label = newLabel
+
+	return newPath
+}
+
+// Transform paths to acknowledge package boundaries
+// See transformSubpackagePath() for more information
+func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList {
+	var newPaths bazel.LabelList
+	for _, include := range paths.Includes {
+		newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include))
+	}
+	for _, exclude := range paths.Excludes {
+		newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude))
+	}
+	return newPaths
+}
+
+// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
+// directory and Bazel target labels, excluding those included in the excludes argument (which
+// should already be expanded to resolve references to Soong-modules). Valid elements of paths
+// include:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+//   source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//    module directory. Because Soong does not have a concept of crossing package boundaries, the
+//    glob as computed by Soong may contain paths that cross package-boundaries that would be
+//    unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
+//    (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
+//    than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//    or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
+//    the local module directory, it will be returned relative to the current package (e.g.
+//    ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
+//    "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
+//    will panic.
+// Properties passed as the paths or excludes argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on other modules will have already been handled by the
+// path_deps mutator.
+func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
+	if paths == nil {
+		return bazel.LabelList{}
+	}
+	labels := bazel.LabelList{
+		Includes: []bazel.Label{},
+	}
+	for _, p := range paths {
+		if m, tag := SrcIsModuleWithTag(p); m != "" {
+			l := getOtherModuleLabel(ctx, m, tag)
+			if !InList(l.Label, expandedExcludes) {
+				l.Bp_text = fmt.Sprintf(":%s", m)
+				labels.Includes = append(labels.Includes, l)
+			}
+		} else {
+			var expandedPaths []bazel.Label
+			if pathtools.IsGlob(p) {
+				globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes)
+				globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
+				for _, path := range globbedPaths {
+					s := path.Rel()
+					expandedPaths = append(expandedPaths, bazel.Label{Label: s})
+				}
+			} else {
+				if !InList(p, expandedExcludes) {
+					expandedPaths = append(expandedPaths, bazel.Label{Label: p})
+				}
+			}
+			labels.Includes = append(labels.Includes, expandedPaths...)
+		}
+	}
+	return labels
+}
+
+// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
+// module. The label will be relative to the current directory if appropriate. The dependency must
+// already be resolved by either deps mutator or path deps mutator.
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
+	m, _ := ctx.GetDirectDep(dep)
+	if m == nil {
+		panic(fmt.Errorf(`Cannot get direct dep %q of %q.
+		This is likely because it was not added via AddDependency().
+		This may be due a mutator skipped during bp2build.`, dep, ctx.Module().Name()))
+	}
+	otherLabel := bazelModuleLabel(ctx, m, tag)
+	label := bazelModuleLabel(ctx, ctx.Module(), "")
+	if samePackage(label, otherLabel) {
+		otherLabel = bazelShortLabel(otherLabel)
+	}
+
+	return bazel.Label{
+		Label: otherLabel,
+	}
+}
+
+func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
+	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
+	b, ok := module.(Bazelable)
+	// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
+	if !ok || !b.ConvertedToBazel(ctx) {
+		return bp2buildModuleLabel(ctx, module)
+	}
+	return b.GetBazelLabel(ctx, module)
+}
+
+func bazelShortLabel(label string) string {
+	i := strings.Index(label, ":")
+	return label[i:]
+}
+
+func bazelPackage(label string) string {
+	i := strings.Index(label, ":")
+	return label[0:i]
+}
+
+func samePackage(label1, label2 string) bool {
+	return bazelPackage(label1) == bazelPackage(label2)
+}
+
+func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
+	moduleName := ctx.OtherModuleName(module)
+	moduleDir := ctx.OtherModuleDir(module)
+	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
+}
+
+// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
+type BazelOutPath struct {
+	OutputPath
+}
+
+var _ Path = BazelOutPath{}
+var _ objPathProvider = BazelOutPath{}
+
+func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
+	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
+	execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
+	execRootPath := filepath.Join(execRootPathComponents...)
+	validatedExecRootPath, err := validatePath(execRootPath)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+
+	outputPath := OutputPath{basePath{"", ""},
+		ctx.Config().buildDir,
+		ctx.Config().BazelContext.OutputBase()}
+
+	return BazelOutPath{
+		OutputPath: outputPath.withRel(validatedExecRootPath),
+	}
+}
diff --git a/android/config.go b/android/config.go
index c170f1e..3db7980 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1295,10 +1295,6 @@
 	return Bool(c.productVariables.Aml_abis)
 }
 
-func (c *config) ExcludeDraftNdkApis() bool {
-	return Bool(c.productVariables.Exclude_draft_ndk_apis)
-}
-
 func (c *config) FlattenApex() bool {
 	return Bool(c.productVariables.Flatten_apex)
 }
diff --git a/android/image.go b/android/image.go
index 1a1a423..bdb9be0 100644
--- a/android/image.go
+++ b/android/image.go
@@ -30,6 +30,11 @@
 	// vendor ramdisk partition).
 	VendorRamdiskVariantNeeded(ctx BaseModuleContext) bool
 
+	// DebugRamdiskVariantNeeded should return true if the module needs a debug ramdisk variant (installed on the
+	// debug ramdisk partition: $(PRODUCT_OUT)/debug_ramdisk/first_stage_ramdisk if BOARD_USES_RECOVERY_AS_ROOT is
+	// true, $(PRODUCT_OUT)/debug_ramdisk otherise).
+	DebugRamdiskVariantNeeded(ctx BaseModuleContext) bool
+
 	// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
 	// recovery partition).
 	RecoveryVariantNeeded(ctx BaseModuleContext) bool
@@ -60,6 +65,9 @@
 
 	// VendorRamdiskVariation means a module to be installed to vendor ramdisk image.
 	VendorRamdiskVariation string = "vendor_ramdisk"
+
+	// DebugRamdiskVariation means a module to be installed to debug ramdisk image.
+	DebugRamdiskVariation string = "debug_ramdisk"
 )
 
 // imageMutator creates variants for modules that implement the ImageInterface that
@@ -83,6 +91,9 @@
 		if m.VendorRamdiskVariantNeeded(ctx) {
 			variations = append(variations, VendorRamdiskVariation)
 		}
+		if m.DebugRamdiskVariantNeeded(ctx) {
+			variations = append(variations, DebugRamdiskVariation)
+		}
 		if m.RecoveryVariantNeeded(ctx) {
 			variations = append(variations, RecoveryVariation)
 		}
diff --git a/android/module.go b/android/module.go
index 9f923e2..942e071 100644
--- a/android/module.go
+++ b/android/module.go
@@ -393,6 +393,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -450,6 +451,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -753,6 +755,9 @@
 	// Whether this module is installed to vendor ramdisk
 	Vendor_ramdisk *bool
 
+	// Whether this module is installed to debug ramdisk
+	Debug_ramdisk *bool
+
 	// Whether this module is built for non-native architectures (also known as native bridge binary)
 	Native_bridge_supported *bool `android:"arch_variant"`
 
@@ -1540,6 +1545,10 @@
 	return Bool(m.commonProperties.Vendor_ramdisk)
 }
 
+func (m *ModuleBase) InstallInDebugRamdisk() bool {
+	return Bool(m.commonProperties.Debug_ramdisk)
+}
+
 func (m *ModuleBase) InstallInRecovery() bool {
 	return Bool(m.commonProperties.Recovery)
 }
@@ -1593,6 +1602,10 @@
 	return m.base().commonProperties.ImageVariation == VendorRamdiskVariation
 }
 
+func (m *ModuleBase) InDebugRamdisk() bool {
+	return m.base().commonProperties.ImageVariation == DebugRamdiskVariation
+}
+
 func (m *ModuleBase) InRecovery() bool {
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
@@ -2548,6 +2561,10 @@
 	return m.module.InstallInVendorRamdisk()
 }
 
+func (m *moduleContext) InstallInDebugRamdisk() bool {
+	return m.module.InstallInDebugRamdisk()
+}
+
 func (m *moduleContext) InstallInRecovery() bool {
 	return m.module.InstallInRecovery()
 }
diff --git a/android/paths.go b/android/paths.go
index df12228..026cb87 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -107,6 +106,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -355,23 +355,42 @@
 	return ret
 }
 
-// PathsForModuleSrc returns Paths rooted from the module's local source directory.  It expands globs, references to
-// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
-// ":name{.tag}" syntax.  Properties passed as the paths argument must have been annotated with struct tag
+// PathsForModuleSrc returns a Paths{} containing the resolved references in paths:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+//   source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//  source directory.
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//    or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+//    filepath.
+// Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_properties mutator.  If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
-// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies.
+// path_deps mutator.
+// If a requested module is not found as a dependency:
+//   * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
+//     missing dependencies
+//   * otherwise, a ModuleError is thrown.
 func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths {
 	return PathsForModuleSrcExcludes(ctx, paths, nil)
 }
 
-// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in
-// the excludes arguments.  It expands globs, references to SourceFileProducer modules using the ":name" syntax, and
-// references to OutputFileProducer modules using the ":name{.tag}" syntax.  Properties passed as the paths or excludes
-// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
-// will have already been handled by the path_properties mutator.  If ctx.Config().AllowMissingDependencies() is
-// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
-// having missing dependencies.
+// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
+// those listed in excludes. Elements of paths and excludes are resolved as:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+//   source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//  source directory. Not valid in excludes.
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//    or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+//    filepath.
+// excluding the items (similarly resolved
+// Properties passed as the paths argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
+// path_deps mutator.
+// If a requested module is not found as a dependency:
+//   * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
+//     missing dependencies
+//   * otherwise, a ModuleError is thrown.
 func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
 	ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
 	if ctx.Config().AllowMissingDependencies() {
@@ -384,159 +403,6 @@
 	return ret
 }
 
-// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
-// order to form a Bazel-compatible label for conversion.
-type BazelConversionPathContext interface {
-	EarlyModulePathContext
-
-	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
-	Module() Module
-	ModuleType() string
-	OtherModuleName(m blueprint.Module) string
-	OtherModuleDir(m blueprint.Module) string
-}
-
-// BazelLabelForModuleDeps returns a Bazel-compatible label for the requested modules which
-// correspond to dependencies on the module within the given ctx.
-func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
-	var labels bazel.LabelList
-	for _, module := range modules {
-		bpText := module
-		if m := SrcIsModule(module); m == "" {
-			module = ":" + module
-		}
-		if m, t := SrcIsModuleWithTag(module); m != "" {
-			l := getOtherModuleLabel(ctx, m, t)
-			l.Bp_text = bpText
-			labels.Includes = append(labels.Includes, l)
-		} else {
-			ctx.ModuleErrorf("%q, is not a module reference", module)
-		}
-	}
-	return labels
-}
-
-// BazelLabelForModuleSrc returns bazel.LabelList with paths rooted from the module's local source
-// directory. It expands globs, and resolves references to modules using the ":name" syntax to
-// bazel-compatible labels.  Properties passed as the paths or excludes argument must have been
-// annotated with struct tag `android:"path"` so that dependencies on other modules will have
-// already been handled by the path_properties mutator.
-//
-// With expanded globs, we can catch package boundaries problem instead of
-// silently failing to potentially missing files from Bazel's globs.
-func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
-	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
-}
-
-// BazelLabelForModuleSrcExcludes returns bazel.LabelList with paths rooted from the module's local
-// source directory, excluding labels included in the excludes argument. It expands globs, and
-// resolves references to modules using the ":name" syntax to bazel-compatible labels. Properties
-// passed as the paths or excludes argument must have been annotated with struct tag
-// `android:"path"` so that dependencies on other modules will have already been handled by the
-// path_properties mutator.
-//
-// With expanded globs, we can catch package boundaries problem instead of
-// silently failing to potentially missing files from Bazel's globs.
-func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
-	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
-	excluded := make([]string, 0, len(excludeLabels.Includes))
-	for _, e := range excludeLabels.Includes {
-		excluded = append(excluded, e.Label)
-	}
-	labels := expandSrcsForBazel(ctx, paths, excluded)
-	labels.Excludes = excludeLabels.Includes
-	return labels
-}
-
-// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local
-// source directory, excluding labels included in the excludes argument. It expands globs, and
-// resolves references to modules using the ":name" syntax to bazel-compatible labels.  Properties
-// passed as the paths or excludes argument must have been annotated with struct tag
-// `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{},
-	}
-	for _, p := range paths {
-		if m, tag := SrcIsModuleWithTag(p); m != "" {
-			l := getOtherModuleLabel(ctx, m, tag)
-			if !InList(l.Label, expandedExcludes) {
-				l.Bp_text = fmt.Sprintf(":%s", m)
-				labels.Includes = append(labels.Includes, l)
-			}
-		} else {
-			var expandedPaths []bazel.Label
-			if pathtools.IsGlob(p) {
-				globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes)
-				globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
-				for _, path := range globbedPaths {
-					s := path.Rel()
-					expandedPaths = append(expandedPaths, bazel.Label{Label: s})
-				}
-			} else {
-				if !InList(p, expandedExcludes) {
-					expandedPaths = append(expandedPaths, bazel.Label{Label: p})
-				}
-			}
-			labels.Includes = append(labels.Includes, expandedPaths...)
-		}
-	}
-	return labels
-}
-
-// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
-// module. The label will be relative to the current directory if appropriate. The dependency must
-// already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
-	m, _ := ctx.GetDirectDep(dep)
-	if m == nil {
-		panic(fmt.Errorf("cannot get direct dep %s of %s", dep, ctx.Module().Name()))
-	}
-	otherLabel := bazelModuleLabel(ctx, m, tag)
-	label := bazelModuleLabel(ctx, ctx.Module(), "")
-	if samePackage(label, otherLabel) {
-		otherLabel = bazelShortLabel(otherLabel)
-	}
-
-	return bazel.Label{
-		Label: otherLabel,
-	}
-}
-
-func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
-	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
-	b, ok := module.(Bazelable)
-	// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
-	if !ok || !b.ConvertedToBazel(ctx) {
-		return bp2buildModuleLabel(ctx, module)
-	}
-	return b.GetBazelLabel(ctx, module)
-}
-
-func bazelShortLabel(label string) string {
-	i := strings.Index(label, ":")
-	return label[i:]
-}
-
-func bazelPackage(label string) string {
-	i := strings.Index(label, ":")
-	return label[0:i]
-}
-
-func samePackage(label1, label2 string) bool {
-	return bazelPackage(label1) == bazelPackage(label2)
-}
-
-func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
-	moduleName := ctx.OtherModuleName(module)
-	moduleDir := ctx.OtherModuleDir(module)
-	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
-}
-
 // OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
 type OutputPaths []OutputPath
 
@@ -590,14 +456,19 @@
 	}
 }
 
-// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
-// paths listed in the excludes arguments, and a list of missing dependencies.  It expands globs, references to
-// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
-// ":name{.tag}" syntax.  Properties passed as the paths or excludes argument must have been annotated with struct tag
+// PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in
+// paths, minus those listed in excludes. Elements of paths and excludes are resolved as:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+//   source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+//  source directory. Not valid in excludes.
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+//    or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+//    filepath.
+// and a list of the module names of missing module dependencies are returned as the second return.
+// Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_properties mutator.  If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
-// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing
-// dependencies.
+// path_deps mutator.
 func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) {
 	prefix := pathForModuleSrc(ctx).String()
 
@@ -1477,17 +1348,6 @@
 	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
 }
 
-type BazelOutPath struct {
-	OutputPath
-}
-
-var _ Path = BazelOutPath{}
-var _ objPathProvider = BazelOutPath{}
-
-func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
-	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
-}
-
 // PathForVndkRefAbiDump returns an OptionalPath representing the path of the
 // reference abi dump for the given module. This is not guaranteed to be valid.
 func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
@@ -1526,25 +1386,6 @@
 		fileName+ext)
 }
 
-// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
-// bazel-owned outputs.
-func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
-	execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
-	execRootPath := filepath.Join(execRootPathComponents...)
-	validatedExecRootPath, err := validatePath(execRootPath)
-	if err != nil {
-		reportPathError(ctx, err)
-	}
-
-	outputPath := OutputPath{basePath{"", ""},
-		ctx.Config().buildDir,
-		ctx.Config().BazelContext.OutputBase()}
-
-	return BazelOutPath{
-		OutputPath: outputPath.withRel(validatedExecRootPath),
-	}
-}
-
 // PathForModuleOut returns a Path representing the paths... under the module's
 // output directory.
 func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath {
@@ -1849,6 +1690,16 @@
 			if !ctx.InstallInRoot() {
 				partition += "/system"
 			}
+		} else if ctx.InstallInDebugRamdisk() {
+			// The module is only available after switching root into
+			// /first_stage_ramdisk. To expose the module before switching root
+			// on a device without a dedicated recovery partition, install the
+			// recovery variant.
+			if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
+				partition = "debug_ramdisk/first_stage_ramdisk"
+			} else {
+				partition = "debug_ramdisk"
+			}
 		} else if ctx.InstallInRecovery() {
 			if ctx.InstallInRoot() {
 				partition = "recovery/root"
@@ -2019,6 +1870,7 @@
 	inSanitizerDir  bool
 	inRamdisk       bool
 	inVendorRamdisk bool
+	inDebugRamdisk  bool
 	inRecovery      bool
 	inRoot          bool
 	forceOS         *OsType
@@ -2051,6 +1903,10 @@
 	return m.inVendorRamdisk
 }
 
+func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool {
+	return m.inDebugRamdisk
+}
+
 func (m testModuleInstallPathContext) InstallInRecovery() bool {
 	return m.inRecovery
 }
diff --git a/android/paths_test.go b/android/paths_test.go
index 465ea3b..cb9138b 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -21,6 +21,8 @@
 	"strconv"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type strsTestCase struct {
@@ -339,6 +341,73 @@
 		},
 
 		{
+			name: "ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/ramdisk/system/my_test",
+			partitionDir: "target/product/test_device/ramdisk/system",
+		},
+		{
+			name: "ramdisk root binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inRamdisk: true,
+				inRoot:    true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/ramdisk/my_test",
+			partitionDir: "target/product/test_device/ramdisk",
+		},
+		{
+			name: "vendor_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inVendorRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/vendor_ramdisk/system/my_test",
+			partitionDir: "target/product/test_device/vendor_ramdisk/system",
+		},
+		{
+			name: "vendor_ramdisk root binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inVendorRamdisk: true,
+				inRoot:          true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/vendor_ramdisk/my_test",
+			partitionDir: "target/product/test_device/vendor_ramdisk",
+		},
+		{
+			name: "debug_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inDebugRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/debug_ramdisk/my_test",
+			partitionDir: "target/product/test_device/debug_ramdisk",
+		},
+		{
 			name: "system native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
@@ -635,6 +704,80 @@
 	}
 }
 
+func TestPathForModuleInstallRecoveryAsBoot(t *testing.T) {
+	testConfig := pathTestConfig("")
+	testConfig.TestProductVariables.BoardUsesRecoveryAsBoot = proptools.BoolPtr(true)
+	testConfig.TestProductVariables.BoardMoveRecoveryResourcesToVendorBoot = proptools.BoolPtr(true)
+	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
+
+	testCases := []struct {
+		name         string
+		ctx          *testModuleInstallPathContext
+		in           []string
+		out          string
+		partitionDir string
+	}{
+		{
+			name: "ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inRamdisk: true,
+				inRoot:    true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/recovery/root/first_stage_ramdisk/my_test",
+			partitionDir: "target/product/test_device/recovery/root/first_stage_ramdisk",
+		},
+
+		{
+			name: "vendor_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inVendorRamdisk: true,
+				inRoot:          true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/vendor_ramdisk/first_stage_ramdisk/my_test",
+			partitionDir: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk",
+		},
+		{
+			name: "debug_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inDebugRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/debug_ramdisk/first_stage_ramdisk/my_test",
+			partitionDir: "target/product/test_device/debug_ramdisk/first_stage_ramdisk",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			tc.ctx.baseModuleContext.config = testConfig
+			output := PathForModuleInstall(tc.ctx, tc.in...)
+			if output.basePath.path != tc.out {
+				t.Errorf("unexpected path:\n got: %q\nwant: %q\n",
+					output.basePath.path,
+					tc.out)
+			}
+			if output.partitionDir != tc.partitionDir {
+				t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n",
+					output.partitionDir, tc.partitionDir)
+			}
+		})
+	}
+}
+
 func TestBaseDirForInstallPath(t *testing.T) {
 	testConfig := pathTestConfig("")
 	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
diff --git a/android/variable.go b/android/variable.go
index f25143d..e830845 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -331,8 +331,7 @@
 
 	VendorVars map[string]map[string]string `json:",omitempty"`
 
-	Ndk_abis               *bool `json:",omitempty"`
-	Exclude_draft_ndk_apis *bool `json:",omitempty"`
+	Ndk_abis *bool `json:",omitempty"`
 
 	Flatten_apex                 *bool `json:",omitempty"`
 	ForceApexSymlinkOptimization *bool `json:",omitempty"`
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f39c7e3..977a954 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4566,8 +4566,8 @@
 
 	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
 		t.Helper()
-		hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index")
-		indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
+		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index")
 		java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
 	}
 
diff --git a/apex/builder.go b/apex/builder.go
index bbb4666..e59dc96 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -517,6 +517,9 @@
 	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
+	// Figure out if need to compress apex.
+	compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, false) && !a.testApex
+
 	if apexType == imageApex {
 		////////////////////////////////////////////////////////////////////////////////////
 		// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
@@ -631,7 +634,7 @@
 			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
 			return
 		}
-		if moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration() {
+		if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration()) && !compressionEnabled {
 			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
 			// don't need hashtree for activation. Therefore, by removing hashtree from
 			// apex bundle (filesystem image in it, to be specific), we can save storage.
@@ -780,12 +783,11 @@
 	})
 	a.outputFile = signedOutputFile
 
-	// Process APEX compression if enabled or forced
 	if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
 		ctx.PropertyErrorf("test_only_force_compression", "not available")
 		return
 	}
-	compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, false)
+
 	if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
 		a.isCompressed = true
 		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 56e03e2..48edb90 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -76,6 +76,12 @@
 			expectedOutput:       CcInfo{},
 			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 3, []string{"", ""}),
 		},
+		{
+			description:          "too many result splits",
+			input:                "|||",
+			expectedOutput:       CcInfo{},
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 3, []string{"", "", "", ""}),
+		},
 	}
 	for _, tc := range testCases {
 		actualOutput, err := GetCcInfo.ParseResult(tc.input)
diff --git a/bloaty/Android.bp b/bloaty/Android.bp
index 96cc1a5..7e722a7 100644
--- a/bloaty/Android.bp
+++ b/bloaty/Android.bp
@@ -11,6 +11,7 @@
     ],
     srcs: [
         "bloaty.go",
+        "testing.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 764cded..50f241f 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -52,12 +52,28 @@
 	pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
 	pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
 	android.RegisterSingletonType("file_metrics", fileSizesSingleton)
-	fileSizeMeasurerKey = blueprint.NewProvider(android.ModuleOutPath{})
+	fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{})
 }
 
-// MeasureSizeForPath should be called by binary producers (e.g. in builder.go).
-func MeasureSizeForPath(ctx android.ModuleContext, filePath android.WritablePath) {
-	ctx.SetProvider(fileSizeMeasurerKey, filePath)
+// measuredFiles contains the paths of the files measured by a module.
+type measuredFiles struct {
+	paths []android.WritablePath
+}
+
+// MeasureSizeForPaths should be called by binary producers to measure the
+// sizes of artifacts. It must only be called once per module; it will panic
+// otherwise.
+func MeasureSizeForPaths(ctx android.ModuleContext, paths ...android.OptionalPath) {
+	mf := measuredFiles{}
+	for _, p := range paths {
+		if !p.Valid() {
+			continue
+		}
+		if p, ok := p.Path().(android.WritablePath); ok {
+			mf.paths = append(mf.paths, p)
+		}
+	}
+	ctx.SetProvider(fileSizeMeasurerKey, mf)
 }
 
 type sizesSingleton struct{}
@@ -68,21 +84,22 @@
 
 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.InSameDir(ctx, filePath.Base()+bloatyDescriptorExt)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        bloaty,
-			Description: "bloaty " + filePath.Rel(),
-			Input:       filePath,
-			Output:      sizeFile,
-		})
-		deps = append(deps, sizeFile)
+		filePaths := ctx.ModuleProvider(m, fileSizeMeasurerKey).(measuredFiles)
+		for _, path := range filePaths.paths {
+			filePath := path.(android.ModuleOutPath)
+			sizeFile := filePath.InSameDir(ctx, filePath.Base()+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{
diff --git a/bloaty/testing.go b/bloaty/testing.go
new file mode 100644
index 0000000..5f5ec84
--- /dev/null
+++ b/bloaty/testing.go
@@ -0,0 +1,25 @@
+// 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
+
+import (
+	"android/soong/android"
+)
+
+// Preparer that will define the default bloaty singleton.
+var PrepareForTestWithBloatyDefaultModules = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		ctx.RegisterSingletonType("file_metrics", fileSizesSingleton)
+	}))
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 427aed3..7e72a8b 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -279,6 +279,53 @@
     ],
 )`},
 		},
+		{
+			description:                        "cc_library_static subpackage test",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// subpackage with subdirectory
+				"subpackage/Android.bp":                         "",
+				"subpackage/subpackage_header.h":                "",
+				"subpackage/subdirectory/subdirectory_header.h": "",
+				// subsubpackage with subdirectory
+				"subpackage/subsubpackage/Android.bp":                         "",
+				"subpackage/subsubpackage/subsubpackage_header.h":             "",
+				"subpackage/subsubpackage/subdirectory/subdirectory_header.h": "",
+				// subsubsubpackage with subdirectory
+				"subpackage/subsubpackage/subsubsubpackage/Android.bp":                         "",
+				"subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h":          "",
+				"subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: [
+    ],
+    include_dirs: [
+	"subpackage",
+    ],
+
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    includes = [
+        "subpackage",
+        ".",
+    ],
+    linkstatic = True,
+    srcs = [
+        "//subpackage:subpackage_header.h",
+        "//subpackage:subdirectory/subdirectory_header.h",
+        "//subpackage/subsubpackage:subsubpackage_header.h",
+        "//subpackage/subsubpackage:subdirectory/subdirectory_header.h",
+        "//subpackage/subsubpackage/subsubsubpackage:subsubsubpackage_header.h",
+        "//subpackage/subsubpackage/subsubsubpackage:subdirectory/subdirectory_header.h",
+    ],
+)`},
+		},
 	}
 
 	dir := "."
diff --git a/cc/config/global.go b/cc/config/global.go
index ed18300..23106ec 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -145,8 +145,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r416183"
-	ClangDefaultShortVersion = "12.0.4"
+	ClangDefaultVersion      = "clang-r416183b"
+	ClangDefaultShortVersion = "12.0.5"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/genrule.go b/cc/genrule.go
index ca4fda7..82d7205 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -75,6 +75,10 @@
 	return Bool(g.Vendor_ramdisk_available)
 }
 
+func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	// If the build is using a snapshot, the recovery variant under AOSP directories
 	// is not needed.
diff --git a/cc/image.go b/cc/image.go
index c1e5dfe..bf662c6 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -601,6 +601,10 @@
 	return c.Properties.VendorRamdiskVariantNeeded
 }
 
+func (c *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return c.Properties.RecoveryVariantNeeded
 }
diff --git a/cc/library.go b/cc/library.go
index 53be3a5..42115cc 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -60,6 +60,7 @@
 
 	Static_ndk_lib *bool
 
+	// Generate stubs to make this library accessible to APEXes.
 	Stubs struct {
 		// Relative path to the symbol map. The symbol map provides the list of
 		// symbols that are exported for stubs variant of this library.
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 60f931d..56fd5fc 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -75,11 +75,6 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string `android:"path"`
-
-	// True if this API is not yet ready to be shipped in the NDK. It will be
-	// available in the platform for testing, but will be excluded from the
-	// sysroot provided to the NDK proper.
-	Draft bool
 }
 
 type headerModule struct {
@@ -184,11 +179,6 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string
-
-	// True if this API is not yet ready to be shipped in the NDK. It will be
-	// available in the platform for testing, but will be excluded from the
-	// sysroot provided to the NDK proper.
-	Draft bool
 }
 
 // Like ndk_headers, but preprocesses the headers with the bionic versioner:
@@ -311,11 +301,6 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string
-
-	// True if this API is not yet ready to be shipped in the NDK. It will be
-	// available in the platform for testing, but will be excluded from the
-	// sysroot provided to the NDK proper.
-	Draft bool
 }
 
 type preprocessedHeadersModule struct {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index a32ef1f..95d8477 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -79,11 +79,6 @@
 	// used. This is only needed to work around platform bugs like
 	// https://github.com/android-ndk/ndk/issues/265.
 	Unversioned_until *string
-
-	// True if this API is not yet ready to be shipped in the NDK. It will be
-	// available in the platform for testing, but will be excluded from the
-	// sysroot provided to the NDK proper.
-	Draft bool
 }
 
 type stubDecorator struct {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 70b15c1..d8c500e 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -104,38 +104,22 @@
 		}
 
 		if m, ok := module.(*headerModule); ok {
-			if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
-				return
-			}
-
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*versionedHeaderModule); ok {
-			if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
-				return
-			}
-
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*preprocessedHeadersModule); ok {
-			if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
-				return
-			}
-
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*Module); ok {
 			if installer, ok := m.installer.(*stubDecorator); ok && m.library.buildStubs() {
-				if ctx.Config().ExcludeDraftNdkApis() &&
-					installer.properties.Draft {
-					return
-				}
 				installPaths = append(installPaths, installer.installPath)
 			}
 
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index aa70768..6d48aed 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -308,6 +308,10 @@
 	return false
 }
 
+func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index a4554fc..1e796ec 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -23,29 +23,43 @@
 	"strings"
 	"time"
 
+	"android/soong/bp2build"
 	"android/soong/shared"
 	"github.com/google/blueprint/bootstrap"
+	"github.com/google/blueprint/deptools"
 
 	"android/soong/android"
-	"android/soong/bp2build"
 )
 
 var (
-	topDir            string
-	outDir            string
+	topDir           string
+	outDir           string
+	availableEnvFile string
+	usedEnvFile      string
+
+	delveListen string
+	delvePath   string
+
 	docFile           string
 	bazelQueryViewDir string
-	delveListen       string
-	delvePath         string
+	bp2buildMarker    string
 )
 
 func init() {
+	// Flags that make sense in every mode
 	flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
 	flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
+	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
+	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
+
+	// Debug flags
 	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")
+
+	// Flags representing various modes soong_build can run in
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
 	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -76,7 +90,7 @@
 }
 
 func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineModuleListFile(), availableEnv)
+	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineArgs.ModuleListFile, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -90,21 +104,31 @@
 // TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
 // the incorrect results from the first pass, and file I/O is expensive.
 func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
+	var firstArgs, secondArgs bootstrap.Args
+
+	firstArgs = bootstrap.CmdlineArgs
 	configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
-	bootstrap.Main(firstCtx.Context, configuration, false, extraNinjaDeps...)
+	bootstrap.RunBlueprint(firstArgs, firstCtx.Context, configuration, extraNinjaDeps...)
+
 	// Invoke bazel commands and save results for second pass.
 	if err := configuration.BazelContext.InvokeBazel(); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
 	// Second pass: Full analysis, using the bazel command results. Output ninja file.
-	secondPassConfig, err := android.ConfigForAdditionalRun(configuration)
+	secondConfig, err := android.ConfigForAdditionalRun(configuration)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
-	secondCtx := newContext(secondPassConfig, true)
-	bootstrap.Main(secondCtx.Context, secondPassConfig, false, extraNinjaDeps...)
+	secondCtx := newContext(secondConfig, true)
+	secondArgs = bootstrap.CmdlineArgs
+	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig, extraNinjaDeps...)
+	err = deptools.WriteDepFile(shared.JoinPath(topDir, secondArgs.DepFile), secondArgs.OutFile, ninjaDeps)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", secondArgs.DepFile, err)
+		os.Exit(1)
+	}
 }
 
 // Run the code-generation phase to convert BazelTargetModules to BUILD files.
@@ -119,7 +143,8 @@
 
 func runSoongDocs(configuration android.Config, extraNinjaDeps []string) {
 	ctx := newContext(configuration, false)
-	bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...)
+	soongDocsArgs := bootstrap.CmdlineArgs
+	bootstrap.RunBlueprint(soongDocsArgs, ctx.Context, configuration, extraNinjaDeps...)
 	if err := writeDocs(ctx, configuration, docFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -147,40 +172,78 @@
 	writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir())
 }
 
-func doChosenActivity(configuration android.Config, extraNinjaDeps []string) {
-	bazelConversionRequested := configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
+func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
+	bazelConversionRequested := configuration.IsEnvTrue("GENERATE_BAZEL_FILES") || bp2buildMarker != ""
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
 	generateQueryView := bazelQueryViewDir != ""
 	jsonModuleFile := configuration.Getenv("SOONG_DUMP_JSON_MODULE_GRAPH")
 
+	blueprintArgs := bootstrap.CmdlineArgs
 	prepareBuildActions := !generateQueryView && jsonModuleFile == ""
-
 	if bazelConversionRequested {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
 		runBp2Build(configuration, extraNinjaDeps)
-		return
+		if bp2buildMarker != "" {
+			return bp2buildMarker
+		} else {
+			return bootstrap.CmdlineArgs.OutFile
+		}
 	}
 
 	ctx := newContext(configuration, prepareBuildActions)
 	if mixedModeBuild {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
 	} else {
-		bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...)
+		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, ctx.Context, configuration, extraNinjaDeps...)
+		err := deptools.WriteDepFile(shared.JoinPath(topDir, blueprintArgs.DepFile), blueprintArgs.OutFile, ninjaDeps)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", blueprintArgs.DepFile, err)
+			os.Exit(1)
+		}
 	}
 
 	// Convert the Soong module graph into Bazel BUILD files.
 	if generateQueryView {
 		runQueryView(configuration, ctx)
-		return
+		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
 	}
 
 	if jsonModuleFile != "" {
 		writeJsonModuleGraph(configuration, ctx, jsonModuleFile, extraNinjaDeps)
-		return
+		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
 	}
 
 	writeMetrics(configuration)
+	return bootstrap.CmdlineArgs.OutFile
+}
+
+// soong_ui dumps the available environment variables to
+// soong.environment.available . Then soong_build itself is run with an empty
+// environment so that the only way environment variables can be accessed is
+// using Config, which tracks access to them.
+
+// At the end of the build, a file called soong.environment.used is written
+// containing the current value of all used environment variables. The next
+// time soong_ui is run, it checks whether any environment variables that was
+// used had changed and if so, it deletes soong.environment.used to cause a
+// rebuild.
+//
+// The dependency of build.ninja on soong.environment.used is declared in
+// build.ninja.d
+func parseAvailableEnv() map[string]string {
+	if availableEnvFile == "" {
+		fmt.Fprintf(os.Stderr, "--available_env not set\n")
+		os.Exit(1)
+	}
+
+	result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error reading available environment file '%s': %s\n", availableEnvFile, err)
+		os.Exit(1)
+	}
+
+	return result
 }
 
 func main() {
@@ -189,33 +252,14 @@
 	shared.ReexecWithDelveMaybe(delveListen, delvePath)
 	android.InitSandbox(topDir)
 
-	// soong_ui dumps the available environment variables to
-	// soong.environment.available . Then soong_build itself is run with an empty
-	// environment so that the only way environment variables can be accessed is
-	// using Config, which tracks access to them.
-
-	// At the end of the build, a file called soong.environment.used is written
-	// containing the current value of all used environment variables. The next
-	// time soong_ui is run, it checks whether any environment variables that was
-	// used had changed and if so, it deletes soong.environment.used to cause a
-	// rebuild.
-	//
-	// The dependency of build.ninja on soong.environment.used is declared in
-	// build.ninja.d
-	availableEnvFile := shared.JoinPath(topDir, outDir, "soong.environment.available")
-	usedEnvFile := shared.JoinPath(topDir, outDir, "soong.environment.used")
-	availableEnv, err := shared.EnvFromFile(availableEnvFile)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "error reading available environment file %s: %s\n", availableEnvFile, err)
-		os.Exit(1)
-	}
+	availableEnv := parseAvailableEnv()
 
 	// The top-level Blueprints file is passed as the first argument.
 	srcDir := filepath.Dir(flag.Arg(0))
 	configuration := newConfig(srcDir, outDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
-		shared.JoinPath(outDir, "soong.environment.used"),
+		usedEnvFile,
 	}
 
 	if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
@@ -233,37 +277,37 @@
 		// because that is done from within the actual builds as a Ninja action and
 		// thus it would overwrite the actual used variables file so this is
 		// special-cased.
+		// TODO: Fix this by not passing --used_env to the soong_docs invocation
 		runSoongDocs(configuration, extraNinjaDeps)
 		return
 	}
 
-	doChosenActivity(configuration, extraNinjaDeps)
-	writeUsedEnvironmentFile(usedEnvFile, configuration)
+	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
 
-func writeUsedEnvironmentFile(path string, configuration android.Config) {
+func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) {
+	if usedEnvFile == "" {
+		return
+	}
+
+	path := shared.JoinPath(topDir, usedEnvFile)
 	data, err := shared.EnvFileContents(configuration.EnvDeps())
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error writing used environment file %s: %s\n", path, err)
+		fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
 		os.Exit(1)
 	}
 
 	err = ioutil.WriteFile(path, data, 0666)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error writing used environment file %s: %s\n", path, err)
+		fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
 		os.Exit(1)
 	}
 
-	// Touch the output Ninja file so that it's not older than the file we just
+	// Touch the output file so that it's not older than the file we just
 	// wrote. We can't write the environment file earlier because one an access
 	// new environment variables while writing it.
-	outputNinjaFile := shared.JoinPath(topDir, bootstrap.CmdlineOutFile())
-	currentTime := time.Now().Local()
-	err = os.Chtimes(outputNinjaFile, currentTime, currentTime)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "error touching output file %s: %s\n", outputNinjaFile, err)
-		os.Exit(1)
-	}
+	touch(shared.JoinPath(topDir, finalOutputFile))
 }
 
 // Workarounds to support running bp2build in a clean AOSP checkout with no
@@ -289,6 +333,27 @@
 		0666)
 }
 
+func touch(path string) {
+	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
+		os.Exit(1)
+	}
+
+	err = f.Close()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
+		os.Exit(1)
+	}
+
+	currentTime := time.Now().Local()
+	err = os.Chtimes(path, currentTime, currentTime)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error touching '%s': %s\n", path, err)
+		os.Exit(1)
+	}
+}
+
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
@@ -296,17 +361,18 @@
 	// Register an alternate set of singletons and mutators for bazel
 	// conversion for Bazel conversion.
 	bp2buildCtx := android.NewContext(configuration)
-	bp2buildCtx.RegisterForBazelConversion()
 
-	// No need to generate Ninja build rules/statements from Modules and Singletons.
-	configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+	// Propagate "allow misssing dependencies" bit. This is normally set in
+	// newContext(), but we create bp2buildCtx without calling that method.
+	bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
 	bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+	bp2buildCtx.RegisterForBazelConversion()
 
 	// The bp2build process is a purely functional process that only depends on
 	// Android.bp files. It must not depend on the values of per-build product
 	// configurations or variables, since those will generate different BUILD
 	// files based on how the user has configured their tree.
-	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile())
+	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineArgs.ModuleListFile)
 	modulePaths, err := bp2buildCtx.ListModulePaths(configuration.SrcDir())
 	if err != nil {
 		panic(err)
@@ -314,10 +380,25 @@
 
 	extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
 
+	// No need to generate Ninja build rules/statements from Modules and Singletons.
+	configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+
 	// Run the loading and analysis pipeline to prepare the graph of regular
 	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 	// from the regular Modules.
-	bootstrap.Main(bp2buildCtx.Context, configuration, false, extraNinjaDeps...)
+	blueprintArgs := bootstrap.CmdlineArgs
+	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration, extraNinjaDeps...)
+
+	for _, globPath := range bp2buildCtx.Globs() {
+		ninjaDeps = append(ninjaDeps, globPath.FileListFile(configuration.BuildDir()))
+	}
+
+	depFile := bp2buildMarker + ".d"
+	err = deptools.WriteDepFile(shared.JoinPath(topDir, depFile), bp2buildMarker, ninjaDeps)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Cannot write depfile '%s': %s\n", depFile, err)
+		os.Exit(1)
+	}
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files
 	// and print conversion metrics to the user.
@@ -330,5 +411,9 @@
 	metrics.Print()
 
 	extraNinjaDeps = append(extraNinjaDeps, codegenContext.AdditionalNinjaDeps()...)
-	writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
+	if bp2buildMarker != "" {
+		touch(shared.JoinPath(topDir, bp2buildMarker))
+	} else {
+		writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
+	}
 }
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 02f1120..26ff5ba 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -115,7 +115,7 @@
 	DexLocation     string // dex location on device
 	BuildPath       android.OutputPath
 	DexPath         android.Path
-	ManifestPath    android.Path
+	ManifestPath    android.OptionalPath
 	UncompressedDex bool
 	HasApkLibraries bool
 	PreoptFlags     []string
@@ -291,7 +291,7 @@
 	// Construct paths that require a PathContext.
 	config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath)
 	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
-	config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
+	config.ModuleConfig.ManifestPath = android.OptionalPathForPath(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)
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 81a63b0..dc17c0a 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -276,9 +276,9 @@
 		// no time/space is wasted on AOT-compiling modules that will fail CLC check on device.
 
 		var manifestOrApk android.Path
-		if module.ManifestPath != nil {
+		if module.ManifestPath.Valid() {
 			// Ok, there is an XML manifest.
-			manifestOrApk = module.ManifestPath
+			manifestOrApk = module.ManifestPath.Path()
 		} else if filepath.Ext(base) == ".apk" {
 			// Ok, there is is an APK with the manifest inside.
 			manifestOrApk = module.DexPath
diff --git a/docs/map_files.md b/docs/map_files.md
index 9fc0d14..192530f 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -52,7 +52,8 @@
 symbol visibility of the library to expose only the interface named by the map
 file. Without this, APIs that you have not explicitly exposed will still be
 available to users via `dlsym`. Note: All comments are ignored in this case. Any
-symbol named in any `global:` group will be visible.
+symbol named in any `global:` group will be visible in the implementation
+library. Annotations in comments only affect what is exposed by the stubs.
 
 ## Special version names
 
@@ -76,9 +77,13 @@
 ### future
 
 Indicates that the version or symbol is first introduced in the "future" API
-level. This is an abitrarily high API level used to define APIs that have not
+level. This is an arbitrarily high API level used to define APIs that have not
 yet been added to a specific release.
 
+Warning: APIs marked `future` will be usable in any module with `sdk: "current"`
+but **will not be included in the NDK**. `future` should generally not be used,
+but is useful when developing APIs for an unknown future release.
+
 ### introduced
 
 Indicates the version in which an API was first introduced. For example,
@@ -92,13 +97,15 @@
 determine which API level an API was added in. The `first_version` property of
 `ndk_library` will dictate which API levels stubs are generated for. If the
 module sets `first_version: "21"`, no symbols were introduced before API 21.
+**Symbol names for which no other rule applies will implicitly be introduced in
+`first_version`.**
 
-Codenames can (and typically should) be used when defining new APIs. This allows
-the actual number of the API level to remain vague during development of that
-release. For example, `introduced=S` can be used to define APIs added in S. Any
-code name known to the build system can be used. For a list of versions known to
-the build system, see `out/soong/api_levels.json` (if not present, run `m
-out/soong/api_levels.json` to generate it).
+Code names can (and typically should) be used when defining new APIs. This
+allows the actual number of the API level to remain vague during development of
+that release. For example, `introduced=S` can be used to define APIs added in S.
+Any code name known to the build system can be used. For a list of versions
+known to the build system, see `out/soong/api_levels.json` (if not present, run
+`m out/soong/api_levels.json` to generate it).
 
 Architecture-specific variants of this tag exist:
 
@@ -123,6 +130,8 @@
 than the NDK. May be used in combination with `apex` if the symbol is exposed to
 both APEX and the LL-NDK.
 
+Historically this annotation was spelled `vndk`, but it has always meant LL-NDK.
+
 ### platform-only
 
 Indicates that the version or symbol is public in the implementation library but
@@ -131,9 +140,9 @@
 clear to the developer that they are up to no good.
 
 The typical use for this tag is for exposing an API to the platform that is not
-for use by the NDK, LL-NDK, or APEX. It is preferable to keep such APIs in an
-entirely separate library to protect them from access via `dlsym`, but this is
-not always possible.
+for use by the NDK, LL-NDK, or APEX (similar to Java's `@SystemAPI`). It is
+preferable to keep such APIs in an entirely separate library to protect them
+from access via `dlsym`, but this is not always possible.
 
 ### var
 
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 6291325..3204e70 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -29,6 +29,7 @@
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/google/blueprint/proptools"
 
@@ -47,6 +48,7 @@
 func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
 	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+	ctx.RegisterModuleType("prebuilt_root", PrebuiltRootFactory)
 	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
 	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
@@ -60,14 +62,6 @@
 	// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	Src *string `android:"path,arch_variant"`
 
-	// Optional subdirectory under which this file is installed into, cannot be specified with
-	// relative_install_path, prefer relative_install_path.
-	Sub_dir *string `android:"arch_variant"`
-
-	// Optional subdirectory under which this file is installed into, cannot be specified with
-	// sub_dir.
-	Relative_install_path *string `android:"arch_variant"`
-
 	// Optional name for the installed file. If unspecified, name of the module is used as the file
 	// name.
 	Filename *string `android:"arch_variant"`
@@ -90,6 +84,13 @@
 	// the recovery variant instead.
 	Vendor_ramdisk_available *bool
 
+	// Make this module available when building for debug ramdisk.
+	// On device without a dedicated recovery partition, the module is only
+	// available after switching root into
+	// /first_stage_ramdisk. To expose the module before switching root, install
+	// the recovery variant instead.
+	Debug_ramdisk_available *bool
+
 	// Make this module available when building for recovery.
 	Recovery_available *bool
 
@@ -100,6 +101,16 @@
 	Symlinks []string `android:"arch_variant"`
 }
 
+type prebuiltSubdirProperties struct {
+	// Optional subdirectory under which this file is installed into, cannot be specified with
+	// relative_install_path, prefer relative_install_path.
+	Sub_dir *string `android:"arch_variant"`
+
+	// Optional subdirectory under which this file is installed into, cannot be specified with
+	// sub_dir.
+	Relative_install_path *string `android:"arch_variant"`
+}
+
 type PrebuiltEtcModule interface {
 	android.Module
 
@@ -117,7 +128,8 @@
 type PrebuiltEtc struct {
 	android.ModuleBase
 
-	properties prebuiltEtcProperties
+	properties       prebuiltEtcProperties
+	subdirProperties prebuiltSubdirProperties
 
 	sourceFilePath android.Path
 	outputFilePath android.OutputPath
@@ -154,6 +166,18 @@
 	return p.inVendorRamdisk()
 }
 
+func (p *PrebuiltEtc) inDebugRamdisk() bool {
+	return p.ModuleBase.InDebugRamdisk() || p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInDebugRamdisk() bool {
+	return p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInDebugRamdisk() bool {
+	return p.inDebugRamdisk()
+}
+
 func (p *PrebuiltEtc) inRecovery() bool {
 	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
@@ -172,7 +196,7 @@
 
 func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk() &&
-		!p.ModuleBase.InstallInVendorRamdisk()
+		!p.ModuleBase.InstallInVendorRamdisk() && !p.ModuleBase.InstallInDebugRamdisk()
 }
 
 func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -183,6 +207,10 @@
 	return proptools.Bool(p.properties.Vendor_ramdisk_available) || p.ModuleBase.InstallInVendorRamdisk()
 }
 
+func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(p.properties.Debug_ramdisk_available) || p.ModuleBase.InstallInDebugRamdisk()
+}
+
 func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
 }
@@ -224,10 +252,10 @@
 }
 
 func (p *PrebuiltEtc) SubDir() string {
-	if subDir := proptools.String(p.properties.Sub_dir); subDir != "" {
+	if subDir := proptools.String(p.subdirProperties.Sub_dir); subDir != "" {
 		return subDir
 	}
-	return proptools.String(p.properties.Relative_install_path)
+	return proptools.String(p.subdirProperties.Relative_install_path)
 }
 
 func (p *PrebuiltEtc) BaseDir() string {
@@ -263,8 +291,13 @@
 	}
 	p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
 
+	if strings.Contains(filename, "/") {
+		ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
+		return
+	}
+
 	// Check that `sub_dir` and `relative_install_path` are not set at the same time.
-	if p.properties.Sub_dir != nil && p.properties.Relative_install_path != nil {
+	if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
 		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
 	}
 
@@ -303,6 +336,9 @@
 	if p.inVendorRamdisk() && !p.onlyInVendorRamdisk() {
 		nameSuffix = ".vendor_ramdisk"
 	}
+	if p.inDebugRamdisk() && !p.onlyInDebugRamdisk() {
+		nameSuffix = ".debug_ramdisk"
+	}
 	if p.inRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
@@ -330,6 +366,12 @@
 func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
 	p.installDirBase = dirBase
 	p.AddProperties(&p.properties)
+	p.AddProperties(&p.subdirProperties)
+}
+
+func InitPrebuiltRootModule(p *PrebuiltEtc) {
+	p.installDirBase = "."
+	p.AddProperties(&p.properties)
 }
 
 // prebuilt_etc is for a prebuilt artifact that is installed in
@@ -352,6 +394,16 @@
 	return module
 }
 
+// prebuilt_root is for a prebuilt artifact that is installed in
+// <partition>/ directory. Can't have any sub directories.
+func PrebuiltRootFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltRootModule(module)
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
 // prebuilt_usr_share is for a prebuilt artifact that is installed in
 // <partition>/usr/share/<sub_dir> directory.
 func PrebuiltUserShareFactory() android.Module {
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 9c3db3b..fdb5648 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -179,6 +179,30 @@
 	}
 }
 
+func TestPrebuiltRootInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_root {
+			name: "foo.conf",
+			src: "foo.conf",
+			filename: "foo.conf",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
+func TestPrebuiltRootInstallDirPathValidate(t *testing.T) {
+	prepareForPrebuiltEtcTest.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("filename cannot contain separator")).RunTestWithBp(t, `
+		prebuilt_root {
+			name: "foo.conf",
+			src: "foo.conf",
+			filename: "foo/bar.conf",
+		}
+	`)
+}
+
 func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_usr_share {
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 791019d..3cdaa64 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -15,8 +15,10 @@
         "filesystem.go",
         "logical_partition.go",
         "vbmeta.go",
+        "testing.go",
     ],
     testSrcs: [
+        "filesystem_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index b2bd6bd..cf98717 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -26,7 +26,11 @@
 )
 
 func init() {
-	android.RegisterModuleType("android_filesystem", filesystemFactory)
+	registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("android_filesystem", filesystemFactory)
 }
 
 type filesystem struct {
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
new file mode 100644
index 0000000..880b177
--- /dev/null
+++ b/filesystem/filesystem_test.go
@@ -0,0 +1,42 @@
+// 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 filesystem
+
+import (
+	"os"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(m.Run())
+}
+
+var fixture = android.GroupFixturePreparers(
+	android.PrepareForIntegrationTestWithAndroid,
+	PrepareForTestWithFilesystemBuildComponents,
+)
+
+func TestFileSystemDeps(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "myfilesystem",
+		}
+	`)
+
+	// produces "myfilesystem.img"
+	result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+}
diff --git a/filesystem/testing.go b/filesystem/testing.go
new file mode 100644
index 0000000..631f1b1
--- /dev/null
+++ b/filesystem/testing.go
@@ -0,0 +1,19 @@
+// 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 filesystem
+
+import "android/soong/android"
+
+var PrepareForTestWithFilesystemBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index d07b002..e6a5ab9 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -626,6 +626,7 @@
 func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
 func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool         { return false }
 func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool   { return false }
+func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool    { return false }
 func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
 func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
 func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index adbe490..d497460 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -17,6 +17,9 @@
 package java
 
 import (
+	"fmt"
+	"strings"
+
 	"android/soong/android"
 )
 
@@ -47,21 +50,102 @@
 type classpathFragment interface {
 	android.Module
 
-	classpathFragmentBase() *classpathFragmentBase
+	classpathFragmentBase() *ClasspathFragmentBase
 }
 
-// classpathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
+// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
 // such modules are expected to call initClasspathFragment().
-type classpathFragmentBase struct {
+type ClasspathFragmentBase struct {
 	properties classpathFragmentProperties
 
-	classpathType classpathType
-
 	outputFilepath android.OutputPath
+	installDirPath android.InstallPath
 }
 
-// Initializes classpathFragmentBase struct. Must be called by all modules that include classpathFragmentBase.
+func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase {
+	return c
+}
+
+// Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase.
 func initClasspathFragment(c classpathFragment) {
 	base := c.classpathFragmentBase()
 	c.AddProperties(&base.properties)
 }
+
+// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto
+type classpathJar struct {
+	path      string
+	classpath classpathType
+	// TODO(satayev): propagate min/max sdk versions for the jars
+	minSdkVersion int32
+	maxSdkVersion int32
+}
+
+func (c *ClasspathFragmentBase) generateAndroidBuildActions(ctx android.ModuleContext) {
+	outputFilename := ctx.ModuleName() + ".pb"
+	c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
+	c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
+
+	var jars []classpathJar
+	jars = appendClasspathJar(jars, BOOTCLASSPATH, defaultBootclasspath(ctx)...)
+	jars = appendClasspathJar(jars, DEX2OATBOOTCLASSPATH, defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps...)
+	jars = appendClasspathJar(jars, SYSTEMSERVERCLASSPATH, systemServerClasspath(ctx)...)
+
+	generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
+	writeClasspathsJson(ctx, generatedJson, jars)
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("conv_classpaths_proto").
+		Flag("encode").
+		Flag("--format=json").
+		FlagWithInput("--input=", generatedJson).
+		FlagWithOutput("--output=", c.outputFilepath)
+
+	rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+}
+
+func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
+	var content strings.Builder
+	fmt.Fprintf(&content, "{\n")
+	fmt.Fprintf(&content, "\"jars\": [\n")
+	for idx, jar := range jars {
+		fmt.Fprintf(&content, "{\n")
+
+		fmt.Fprintf(&content, "\"relativePath\": \"%s\",\n", jar.path)
+		fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath)
+
+		if idx < len(jars)-1 {
+			fmt.Fprintf(&content, "},\n")
+		} else {
+			fmt.Fprintf(&content, "}\n")
+		}
+	}
+	fmt.Fprintf(&content, "]\n")
+	fmt.Fprintf(&content, "}\n")
+	android.WriteFileRule(ctx, output, content.String())
+}
+
+func appendClasspathJar(slice []classpathJar, classpathType classpathType, paths ...string) (result []classpathJar) {
+	result = append(result, slice...)
+	for _, path := range paths {
+		result = append(result, classpathJar{
+			path:      path,
+			classpath: classpathType,
+		})
+	}
+	return
+}
+
+func (c *ClasspathFragmentBase) getAndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(c.outputFilepath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base())
+			},
+		},
+	}}
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index b4cf012..3571590 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -221,7 +221,7 @@
 		DexLocation:     dexLocation,
 		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
 		DexPath:         dexJarFile,
-		ManifestPath:    d.manifestFile,
+		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
 		UncompressedDex: d.uncompressedDex,
 		HasApkLibraries: false,
 		PreoptFlags:     nil,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 7137f33..e57c3e9 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -484,7 +484,7 @@
 
 	// Now match the apex part of the boot image configuration.
 	requiredApex := bootjars.Apex(index)
-	if requiredApex == "platform" {
+	if requiredApex == "platform" || requiredApex == "system_ext" {
 		if len(apexInfo.InApexes) != 0 {
 			// A platform variant is required but this is for an apex so ignore it.
 			return -1, nil, nil
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index d78651d..73f21d1 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -35,6 +35,7 @@
 			name: "bar",
 			srcs: ["b.java"],
 			installable: true,
+			system_ext_specific: true,
 		}
 
 		dex_import {
@@ -47,7 +48,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar", "platform:baz"),
+		dexpreopt.FixtureSetBootJars("platform:foo", "system_ext:bar", "platform:baz"),
 	).RunTestWithBp(t, bp)
 
 	dexpreoptBootJars := result.SingletonForTests("dex_bootjars")
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 64b2656..656f5ef 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -26,7 +26,7 @@
 // systemServerClasspath returns the on-device locations of the modules in the system server classpath.  It is computed
 // once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
 // ctx.Config().
-func systemServerClasspath(ctx android.MakeVarsContext) []string {
+func systemServerClasspath(ctx android.PathContext) []string {
 	return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string {
 		global := dexpreopt.GetGlobalConfig(ctx)
 		var systemServerClasspathLocations []string
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index bed11fe..7cf082b 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -20,9 +20,9 @@
 
 // Contains support for processing hiddenAPI in a modular fashion.
 
-// HiddenAPIAugmentationProperties contains paths to the files that can be used to augment the information
-// obtained from annotations within the source code in order to create the complete set of flags
-// that should be applied to the dex implementation jars on the bootclasspath.
+// HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
+// information obtained from annotations within the source code in order to create the complete set
+// of flags that should be applied to the dex implementation jars on the bootclasspath.
 //
 // Each property contains a list of paths. With the exception of the Unsupported_packages the paths
 // of each property reference a plain text file that contains a java signature per line. The flags
@@ -31,7 +31,7 @@
 // The Unsupported_packages property contains a list of paths, each of which is a plain text file
 // with one Java package per line. All members of all classes within that package (but not nested
 // packages) will be updated in a property specific way.
-type HiddenAPIAugmentationProperties struct {
+type HiddenAPIFlagFileProperties struct {
 	// Marks each signature in the referenced files as being unsupported.
 	Unsupported []string `android:"path"`
 
@@ -60,45 +60,116 @@
 	Unsupported_packages []string `android:"path"`
 }
 
-func (p *HiddenAPIAugmentationProperties) hiddenAPIAugmentationInfo(ctx android.ModuleContext) hiddenAPIAugmentationInfo {
-	paths := func(paths []string) android.Paths { return android.PathsForModuleSrc(ctx, paths) }
-	return hiddenAPIAugmentationInfo{
-		Unsupported:               paths(p.Unsupported),
-		Removed:                   paths(p.Removed),
-		Max_target_r_low_priority: paths(p.Max_target_r_low_priority),
-		Max_target_q:              paths(p.Max_target_q),
-		Max_target_p:              paths(p.Max_target_p),
-		Max_target_o_low_priority: paths(p.Max_target_o_low_priority),
-		Blocked:                   paths(p.Blocked),
-		Unsupported_packages:      paths(p.Unsupported_packages),
+func (p *HiddenAPIFlagFileProperties) hiddenAPIFlagFileInfo(ctx android.ModuleContext) hiddenAPIFlagFileInfo {
+	info := hiddenAPIFlagFileInfo{categoryToPaths: map[*hiddenAPIFlagFileCategory]android.Paths{}}
+	for _, category := range hiddenAPIFlagFileCategories {
+		paths := android.PathsForModuleSrc(ctx, category.propertyAccessor(p))
+		info.categoryToPaths[category] = paths
 	}
+	return info
 }
 
-// hiddenAPIAugmentationInfo contains paths resolved from HiddenAPIAugmentationProperties
-type hiddenAPIAugmentationInfo struct {
-	// See HiddenAPIAugmentationProperties.Unsupported
-	Unsupported android.Paths
+type hiddenAPIFlagFileCategory struct {
+	// propertyName is the name of the property for this category.
+	propertyName string
 
-	// See HiddenAPIAugmentationProperties.Removed
-	Removed android.Paths
+	// propertyAccessor retrieves the value of the property for this category from the set of
+	// properties.
+	propertyAccessor func(properties *HiddenAPIFlagFileProperties) []string
 
-	// See HiddenAPIAugmentationProperties.Max_target_r_low_priority
-	Max_target_r_low_priority android.Paths
+	// commandMutator adds the appropriate command line options for this category to the supplied
+	// command
+	commandMutator func(command *android.RuleBuilderCommand, path android.Path)
+}
 
-	// See HiddenAPIAugmentationProperties.Max_target_q
-	Max_target_q android.Paths
+var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
+	// See HiddenAPIFlagFileProperties.Unsupported
+	{
+		propertyName: "unsupported",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Unsupported
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--unsupported ", path)
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Removed
+	{
+		propertyName: "removed",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Removed
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
+	{
+		propertyName: "max_target_r_low_priority",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Max_target_r_low_priority
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Max_target_q
+	{
+		propertyName: "max_target_q",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Max_target_q
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--max-target-q ", path)
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Max_target_p
+	{
+		propertyName: "max_target_p",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Max_target_p
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--max-target-p ", path)
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
+	{
+		propertyName: "max_target_o_low_priority",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Max_target_o_low_priority
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Blocked
+	{
+		propertyName: "blocked",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Blocked
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--blocked ", path)
+		},
+	},
+	// See HiddenAPIFlagFileProperties.Unsupported_packages
+	{
+		propertyName: "unsupported_packages",
+		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+			return properties.Unsupported_packages
+		},
+		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+			command.FlagWithInput("--unsupported ", path).Flag("--packages ")
+		},
+	},
+}
 
-	// See HiddenAPIAugmentationProperties.Max_target_p
-	Max_target_p android.Paths
-
-	// See HiddenAPIAugmentationProperties.Max_target_o_low_priority
-	Max_target_o_low_priority android.Paths
-
-	// See HiddenAPIAugmentationProperties.Blocked
-	Blocked android.Paths
-
-	// See HiddenAPIAugmentationProperties.Unsupported_packages
-	Unsupported_packages android.Paths
+// hiddenAPIFlagFileInfo contains paths resolved from HiddenAPIFlagFileProperties
+type hiddenAPIFlagFileInfo struct {
+	// categoryToPaths maps from the flag file category to the paths containing information for that
+	// category.
+	categoryToPaths map[*hiddenAPIFlagFileCategory]android.Paths
 }
 
 // ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the
@@ -125,7 +196,7 @@
 //
 // augmentationInfo is a struct containing paths to files that augment the information provided by
 // the moduleSpecificFlagsPaths.
-func ruleToGenerateHiddenApiFlags(ctx android.BuilderContext, outputPath android.WritablePath, baseFlagsPath android.Path, moduleSpecificFlagsPaths android.Paths, augmentationInfo hiddenAPIAugmentationInfo) {
+func ruleToGenerateHiddenApiFlags(ctx android.BuilderContext, outputPath android.WritablePath, baseFlagsPath android.Path, moduleSpecificFlagsPaths android.Paths, augmentationInfo hiddenAPIFlagFileInfo) {
 	tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
 	rule := android.NewRuleBuilder(pctx, ctx)
 	command := rule.Command().
@@ -134,36 +205,12 @@
 		Inputs(moduleSpecificFlagsPaths).
 		FlagWithOutput("--output ", tempPath)
 
-	for _, path := range augmentationInfo.Unsupported {
-		command.FlagWithInput("--unsupported ", path)
-	}
-
-	for _, path := range augmentationInfo.Removed {
-		command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
-	}
-
-	for _, path := range augmentationInfo.Max_target_r_low_priority {
-		command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
-	}
-
-	for _, path := range augmentationInfo.Max_target_q {
-		command.FlagWithInput("--max-target-q ", path)
-	}
-
-	for _, path := range augmentationInfo.Max_target_p {
-		command.FlagWithInput("--max-target-p ", path)
-	}
-
-	for _, path := range augmentationInfo.Max_target_o_low_priority {
-		command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
-	}
-
-	for _, path := range augmentationInfo.Blocked {
-		command.FlagWithInput("--blocked ", path)
-	}
-
-	for _, path := range augmentationInfo.Unsupported_packages {
-		command.FlagWithInput("--unsupported ", path).Flag("--packages ")
+	// Add the options for the different categories of flag files.
+	for _, category := range hiddenAPIFlagFileCategories {
+		paths := augmentationInfo.categoryToPaths[category]
+		for _, path := range paths {
+			category.commandMutator(command, path)
+		}
 	}
 
 	commitChangeForRestat(rule, tempPath, outputPath)
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 6ba5f35..ed0b722 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -24,7 +24,6 @@
 
 func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
 	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
-	ctx.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory)
 }
 
 var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
@@ -116,7 +115,7 @@
 }
 
 type hiddenAPISingleton struct {
-	flags, metadata android.Path
+	flags android.Path
 }
 
 // hiddenAPI singleton rules
@@ -138,30 +137,25 @@
 
 	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
 		h.flags = prebuiltFlagsRule(ctx)
+		prebuiltIndexRule(ctx)
 		return
 	}
 
 	// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
 	if ctx.Config().FrameworksBaseDirExists(ctx) {
 		h.flags = flagsRule(ctx)
-		h.metadata = metadataRule(ctx)
 	} else {
 		h.flags = emptyFlagsRule(ctx)
 	}
 }
 
 // Export paths to Make.  INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-// Both paths are used to call dist-for-goals.
 func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) {
 	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
 		return
 	}
 
 	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
-
-	if h.metadata != nil {
-		ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", h.metadata.String())
-	}
 }
 
 // stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
@@ -321,6 +315,17 @@
 	return outputPath
 }
 
+func prebuiltIndexRule(ctx android.SingletonContext) {
+	outputPath := hiddenAPISingletonPaths(ctx).index
+	inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: outputPath,
+		Input:  inputPath,
+	})
+}
+
 // flagsRule is a placeholder that simply returns the location of the file, the generation of the
 // ninja rules is done in generateHiddenAPIBuildActions.
 func flagsRule(ctx android.SingletonContext) android.Path {
@@ -343,34 +348,6 @@
 	return outputPath
 }
 
-// metadataRule creates a rule to build hiddenapi-unsupported.csv out of the metadata.csv files generated for boot image
-// modules.
-func metadataRule(ctx android.SingletonContext) android.Path {
-	var metadataCSV android.Paths
-
-	ctx.VisitAllModules(func(module android.Module) {
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if csv := h.metadataCSV(); csv != nil {
-				metadataCSV = append(metadataCSV, csv)
-			}
-		}
-	})
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	outputPath := hiddenAPISingletonPaths(ctx).metadata
-
-	rule.Command().
-		BuiltTool("merge_csv").
-		Flag("--key_field signature").
-		FlagWithOutput("--output=", outputPath).
-		Inputs(metadataCSV)
-
-	rule.Build("hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
-
-	return outputPath
-}
-
 // commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different.  It
 // also marks the rule as restat and marks the tempPath as a temporary file that should not be considered an output of
 // the rule.
@@ -388,60 +365,3 @@
 		Text("fi").
 		Text(")")
 }
-
-func hiddenAPIIndexSingletonFactory() android.Singleton {
-	return &hiddenAPIIndexSingleton{}
-}
-
-type hiddenAPIIndexSingleton struct {
-	index android.Path
-}
-
-func (h *hiddenAPIIndexSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-
-	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
-		outputPath := hiddenAPISingletonPaths(ctx).index
-		inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
-
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Output: outputPath,
-			Input:  inputPath,
-		})
-
-		h.index = outputPath
-		return
-	}
-
-	indexes := android.Paths{}
-	ctx.VisitAllModules(func(module android.Module) {
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if h.indexCSV() != nil {
-				indexes = append(indexes, h.indexCSV())
-			}
-		}
-	})
-
-	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)
-	rule.Build("singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
-
-	h.index = hiddenAPISingletonPaths(ctx).index
-}
-
-func (h *hiddenAPIIndexSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-
-	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_INDEX", h.index.String())
-}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index e5e1c25..5ea9a5b 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -50,61 +50,6 @@
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
-func TestHiddenAPIIndexSingleton(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		hiddenApiFixtureFactory,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("bar"),
-		FixtureConfigureBootJars("platform:foo", "platform:bar"),
-	).RunTestWithBp(t, `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-			compile_dex: true,
-
-			hiddenapi_additional_annotations: [
-				"foo-hiddenapi-annotations",
-			],
-		}
-
-		java_library {
-			name: "foo-hiddenapi-annotations",
-			srcs: ["a.java"],
-			compile_dex: true,
-		}
-
-		java_import {
-			name: "foo",
-			jars: ["a.jar"],
-			compile_dex: true,
-			prefer: false,
-		}
-
-		java_sdk_library {
-			name: "bar",
-			srcs: ["a.java"],
-			compile_dex: true,
-		}
-	`)
-
-	hiddenAPIIndex := result.SingletonForTests("hiddenapi_index")
-	indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index")
-	CheckHiddenAPIRuleInputs(t, `
-.intermediates/bar/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 := result.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) {
 	expectedErrorMessage :=
 		"hiddenapi has determined that the source module \"foo\" should be ignored as it has been" +
diff --git a/java/java_test.go b/java/java_test.go
index fdf7579..0523458 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1261,6 +1261,14 @@
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
 		t.Error("did not use the correct file for baseline")
 	}
+
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
+		t.Error("should check NewApi errors")
+	}
+
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
+		t.Error("should combine NewApi errors with SomeCheck errors")
+	}
 }
 
 func TestGeneratedSources(t *testing.T) {
diff --git a/java/lint.go b/java/lint.go
index 30843dc..aa308e6 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -57,24 +57,25 @@
 }
 
 type linter struct {
-	name                string
-	manifest            android.Path
-	mergedManifest      android.Path
-	srcs                android.Paths
-	srcJars             android.Paths
-	resources           android.Paths
-	classpath           android.Paths
-	classes             android.Path
-	extraLintCheckJars  android.Paths
-	test                bool
-	library             bool
-	minSdkVersion       string
-	targetSdkVersion    string
-	compileSdkVersion   string
-	javaLanguageLevel   string
-	kotlinLanguageLevel string
-	outputs             lintOutputs
-	properties          LintProperties
+	name                    string
+	manifest                android.Path
+	mergedManifest          android.Path
+	srcs                    android.Paths
+	srcJars                 android.Paths
+	resources               android.Paths
+	classpath               android.Paths
+	classes                 android.Path
+	extraLintCheckJars      android.Paths
+	test                    bool
+	library                 bool
+	minSdkVersion           string
+	targetSdkVersion        string
+	compileSdkVersion       string
+	javaLanguageLevel       string
+	kotlinLanguageLevel     string
+	outputs                 lintOutputs
+	properties              LintProperties
+	extraMainlineLintErrors []string
 
 	reports android.Paths
 
@@ -246,6 +247,7 @@
 	cmd.FlagWithInput("@",
 		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
 
+	cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
 	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
 	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
@@ -282,6 +284,10 @@
 		return
 	}
 
+	if l.minSdkVersion != l.compileSdkVersion {
+		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, "NewApi")
+	}
+
 	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
 	for _, extraLintCheckModule := range extraLintCheckModules {
 		if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 621119e..cb8ad68 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -59,6 +59,7 @@
 
 type platformBootclasspathModule struct {
 	android.ModuleBase
+	ClasspathFragmentBase
 
 	properties platformBootclasspathProperties
 
@@ -99,28 +100,29 @@
 	// platform_bootclasspath.
 	Fragments []ApexVariantReference
 
-	Hidden_api HiddenAPIAugmentationProperties
+	Hidden_api HiddenAPIFlagFileProperties
 }
 
 func platformBootclasspathFactory() android.Module {
 	m := &platformBootclasspathModule{}
 	m.AddProperties(&m.properties)
+	// TODO(satayev): split systemserver and apex jars into separate configs.
+	initClasspathFragment(m)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
 
 var _ android.OutputFileProducer = (*platformBootclasspathModule)(nil)
 
-// A minimal AndroidMkEntries is needed in order to support the dists property.
-func (b *platformBootclasspathModule) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{
-		{
-			Class: "FAKE",
-			// Need at least one output file in order for this to take effect.
-			OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV),
-			Include:    "$(BUILD_PHONY_PACKAGE)",
-		},
-	}
+func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
+	entries = append(entries, android.AndroidMkEntries{
+		Class: "FAKE",
+		// Need at least one output file in order for this to take effect.
+		OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV),
+		Include:    "$(BUILD_PHONY_PACKAGE)",
+	})
+	entries = append(entries, b.classpathFragmentBase().getAndroidMkEntries()...)
+	return
 }
 
 // Make the hidden API files available from the platform-bootclasspath module.
@@ -245,6 +247,8 @@
 }
 
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	b.classpathFragmentBase().generateAndroidBuildActions(ctx)
+
 	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == platformBootclasspathModuleDepTag {
@@ -334,9 +338,47 @@
 		moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV())
 	}
 
-	augmentationInfo := b.properties.Hidden_api.hiddenAPIAugmentationInfo(ctx)
+	augmentationInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
 
 	outputPath := hiddenAPISingletonPaths(ctx).flags
 	baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags
 	ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, augmentationInfo)
+
+	b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules)
+	b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules)
+}
+
+func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+	indexes := android.Paths{}
+	for _, module := range modules {
+		indexes = append(indexes, module.indexCSV())
+	}
+
+	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)
+	rule.Build("platform-bootclasspath-monolithic-hiddenapi-index", "monolithic hidden API index")
+}
+
+func (b *platformBootclasspathModule) generatedHiddenAPIMetadataRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+	metadataCSVFiles := android.Paths{}
+	for _, module := range modules {
+		metadataCSVFiles = append(metadataCSVFiles, module.metadataCSV())
+	}
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	outputPath := hiddenAPISingletonPaths(ctx).metadata
+
+	rule.Command().
+		BuiltTool("merge_csv").
+		Flag("--key_field signature").
+		FlagWithOutput("--output=", outputPath).
+		Inputs(metadataCSVFiles)
+
+	rule.Build("platform-bootclasspath-monolithic-hiddenapi-metadata", "monolithic hidden API metadata")
 }
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index c740911..e51b049 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -132,6 +132,89 @@
 	})
 }
 
+func TestPlatformBootclasspathVariant(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`),
+	).RunTest(t)
+
+	variants := result.ModuleVariantsForTests("platform-bootclasspath")
+	android.AssertIntEquals(t, "expect 1 variant", 1, len(variants))
+}
+
+func TestPlatformBootclasspath_ClasspathFragmentPaths(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`),
+	).RunTest(t)
+
+	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+	android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base())
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+}
+
+func TestPlatformBootclasspathModule_AndroidMkEntries(t *testing.T) {
+	preparer := android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`),
+	)
+
+	t.Run("AndroidMkEntries", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		android.AssertIntEquals(t, "AndroidMkEntries count", 2, len(entries))
+	})
+
+	t.Run("hiddenapi-flags-entry", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		got := entries[0].OutputFile
+		android.AssertBoolEquals(t, "valid output path", true, got.Valid())
+		android.AssertSame(t, "output filepath", p.hiddenAPIFlagsCSV, got.Path())
+	})
+
+	t.Run("classpath-fragment-entry", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		want := map[string][]string{
+			"LOCAL_MODULE":                {"platform-bootclasspath"},
+			"LOCAL_MODULE_CLASS":          {"ETC"},
+			"LOCAL_INSTALLED_MODULE_STEM": {"platform-bootclasspath.pb"},
+			// Output and Install paths are tested separately in TestPlatformBootclasspath_ClasspathFragmentPaths
+		}
+
+		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		got := entries[1]
+		for k, expectedValue := range want {
+			if value, ok := got.EntryMap[k]; ok {
+				android.AssertDeepEquals(t, k, expectedValue, value)
+			} else {
+				t.Errorf("No %s defined, saw %q", k, got.EntryMap)
+			}
+		}
+	})
+}
+
 func TestPlatformBootclasspath_Dist(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
@@ -172,3 +255,62 @@
 	android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore\n", goals[0])
 	android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[1]))
 }
+
+func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		hiddenApiFixtureFactory,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("bar"),
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
+	).RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+
+			hiddenapi_additional_annotations: [
+				"foo-hiddenapi-annotations",
+			],
+		}
+
+		java_library {
+			name: "foo-hiddenapi-annotations",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: false,
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		platform_bootclasspath {
+			name: "myplatform-bootclasspath",
+		}
+	`)
+
+	platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
+	indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index")
+	CheckHiddenAPIRuleInputs(t, `
+.intermediates/bar/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 := result.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)
+}
diff --git a/licenses/Android.bp b/licenses/Android.bp
index c70d6bd..a983b5b 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -20,6 +20,7 @@
 
 license {
     name: "Android-Apache-2.0",
+    package_name: "Android",
     license_kinds: ["SPDX-license-identifier-Apache-2.0"],
     copyright_notice: "Copyright (C) The Android Open Source Project",
     license_text: ["LICENSE"],
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index da80a47..241cac6 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/etc"
+	"fmt"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -68,6 +69,17 @@
 	return l.outputFilePath
 }
 
+var _ android.OutputFileProducer = (*linkerConfig)(nil)
+
+func (l *linkerConfig) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{l.outputFilePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (l *linkerConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
 	l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
@@ -81,9 +93,10 @@
 	linkerConfigRule.Build("conv_linker_config",
 		"Generate linker config protobuf "+l.outputFilePath.String())
 
-	if proptools.BoolDefault(l.properties.Installable, true) {
-		ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
+	if !proptools.BoolDefault(l.properties.Installable, true) {
+		l.SkipInstall()
 	}
+	ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
 }
 
 // linker_config generates protobuf file from json file. This protobuf file will be used from
diff --git a/rust/builder.go b/rust/builder.go
index 197c703..208b734 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -21,7 +21,6 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
-	"android/soong/bloaty"
 	"android/soong/rust/config"
 )
 
@@ -254,8 +253,6 @@
 		implicits = append(implicits, clippyFile)
 	}
 
-	bloaty.MeasureSizeForPath(ctx, outputFile)
-
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rustc,
 		Description:     "rustc " + main.Rel(),
diff --git a/rust/image.go b/rust/image.go
index 7eb49d9..900842e 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -97,6 +97,10 @@
 	return mod.InRamdisk()
 }
 
+func (mod *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
 	return mod.InRecovery()
 }
diff --git a/rust/library.go b/rust/library.go
index ae130a3..26c104c 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -475,7 +475,7 @@
 		TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
 	}
 
-	if !library.rlib() && library.stripper.NeedsStrip(ctx) {
+	if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
 		strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
 		library.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
 		library.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
diff --git a/rust/rust.go b/rust/rust.go
index d2de1bc..9738b46 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bloaty"
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
 	"android/soong/rust/config"
@@ -751,8 +752,8 @@
 	if mod.compiler != nil && !mod.compiler.Disabled() {
 		mod.compiler.initialize(ctx)
 		unstrippedOutputFile := mod.compiler.compile(ctx, flags, deps)
-
 		mod.unstrippedOutputFile = android.OptionalPathForPath(unstrippedOutputFile)
+		bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), mod.unstrippedOutputFile)
 
 		apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 		if mod.installable(apexInfo) {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 47c64a9..6ae05d9 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -402,3 +402,17 @@
 	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std")
 	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
 }
+
+// Test that library size measurements are generated.
+func TestLibrarySizes(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_dylib {
+			name: "libwaldo",
+			srcs: ["foo.rs"],
+			crate_name: "waldo",
+		}`)
+
+	m := ctx.SingletonForTests("file_metrics")
+	m.Output("libwaldo.dylib.so.bloaty.csv")
+	m.Output("stripped/libwaldo.dylib.so.bloaty.csv")
+}
diff --git a/rust/testing.go b/rust/testing.go
index 2dda9c1..a0f86b2 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bloaty"
 	"android/soong/cc"
 )
 
@@ -34,6 +35,7 @@
 // Preparer that will define default rust modules, e.g. standard prebuilt modules.
 var PrepareForTestWithRustDefaultModules = android.GroupFixturePreparers(
 	cc.PrepareForTestWithCcDefaultModules,
+	bloaty.PrepareForTestWithBloatyDefaultModules,
 	PrepareForTestWithRustBuildComponents,
 	android.FixtureAddTextFile(rustDefaultsDir+"Android.bp", GatherRequiredDepsForTest()),
 )
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index c27f098..a1fa48d 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -54,7 +54,6 @@
     "Safestack": false,
 
     "Ndk_abis": true,
-    "Exclude_draft_ndk_apis": true,
 
     "VendorVars": {
         "art_module": {
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 22fe9f6..92f79da 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -78,6 +78,14 @@
   with open(args.output, 'wb') as f:
     f.write(pb.SerializeToString())
 
+def Merge(args):
+  pb = linker_config_pb2.LinkerConfig()
+  for other in args.input:
+    with open(other, 'rb') as f:
+      pb.MergeFromString(f.read())
+
+  with open(args.out, 'wb') as f:
+    f.write(pb.SerializeToString())
 
 def GetArgParser():
   parser = argparse.ArgumentParser()
@@ -161,6 +169,22 @@
       help='Values of the libraries to append. If there are more than one it should be separated by empty space')
   append.set_defaults(func=Append)
 
+  append = subparsers.add_parser(
+      'merge', help='Merge configurations')
+  append.add_argument(
+      '-o',
+      '--out',
+      required=True,
+      type=str,
+      help='Ouptut linker configuration file to write in protobuf.')
+  append.add_argument(
+      '-i',
+      '--input',
+      nargs='+',
+      type=str,
+      help='Linker configuration files to merge.')
+  append.set_defaults(func=Merge)
+
   return parser
 
 
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index fd7741c..17ddf17 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -43,7 +43,18 @@
 		})
 
 	CheckSnapshot(t, result, "myexports", "package",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -54,18 +65,11 @@
     jars: ["java/myjavalib.jar"],
 }
 
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
 module_exports_snapshot {
     name: "myexports@current",
     visibility: ["//visibility:public"],
     java_libs: ["myexports_myjavalib@current"],
 }
-`))
+`),
+	)
 }
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 208cd58..54916d8 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -179,7 +179,18 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -190,20 +201,11 @@
     jars: ["java/myjavalib.jar"],
 }
 
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
 sdk_snapshot {
     name: "mysdk@current",
     visibility: ["//visibility:public"],
     java_header_libs: ["mysdk_myjavalib@current"],
 }
-
 `),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
@@ -239,22 +241,25 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavalib.jar"],
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -296,12 +301,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
@@ -314,10 +319,13 @@
         },
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     host_supported: true,
@@ -371,7 +379,18 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -382,20 +401,11 @@
     jars: ["java/myjavalib.jar"],
 }
 
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
 module_exports_snapshot {
     name: "myexports@current",
     visibility: ["//visibility:public"],
     java_libs: ["myexports_myjavalib@current"],
 }
-
 `),
 		checkAllCopyRules(`
 .intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
@@ -431,7 +441,18 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+`),
+		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
@@ -442,14 +463,6 @@
     jars: ["java/myjavalib.jar"],
 }
 
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
-}
-
 module_exports_snapshot {
     name: "myexports@current",
     visibility: ["//visibility:public"],
@@ -489,22 +502,25 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "myexports_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
     jars: ["java/myjavalib.jar"],
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -545,21 +561,24 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
-    name: "myexports_myjavatests@current",
-    sdk_member_name: "myjavatests",
+    name: "myjavatests",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavatests.jar"],
     test_config: "java/myjavatests-AndroidTest.xml",
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_test_import {
-    name: "myjavatests",
-    prefer: false,
+    name: "myexports_myjavatests@current",
+    sdk_member_name: "myjavatests",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavatests.jar"],
@@ -600,12 +619,12 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_test_import {
-    name: "myexports_myjavatests@current",
-    sdk_member_name: "myjavatests",
+    name: "myjavatests",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -613,10 +632,13 @@
     jars: ["java/myjavatests.jar"],
     test_config: "java/myjavatests-AndroidTest.xml",
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_test_import {
-    name: "myjavatests",
-    prefer: false,
+    name: "myexports_myjavatests@current",
+    sdk_member_name: "myjavatests",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -669,20 +691,41 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_exported-system-module@current",
-    sdk_member_name: "exported-system-module",
+    name: "exported-system-module",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/exported-system-module.jar"],
 }
 
 java_import {
-    name: "exported-system-module",
+    name: "mysdk_system-module",
     prefer: false,
+    visibility: ["//visibility:private"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/system-module.jar"],
+}
+
+java_system_modules_import {
+    name: "my-system-modules",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    libs: [
+        "mysdk_system-module",
+        "exported-system-module",
+    ],
+}
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_exported-system-module@current",
+    sdk_member_name: "exported-system-module",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/exported-system-module.jar"],
@@ -696,14 +739,6 @@
     jars: ["java/system-module.jar"],
 }
 
-java_import {
-    name: "mysdk_system-module",
-    prefer: false,
-    visibility: ["//visibility:private"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/system-module.jar"],
-}
-
 java_system_modules_import {
     name: "mysdk_my-system-modules@current",
     sdk_member_name: "my-system-modules",
@@ -714,16 +749,6 @@
     ],
 }
 
-java_system_modules_import {
-    name: "my-system-modules",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    libs: [
-        "mysdk_system-module",
-        "exported-system-module",
-    ],
-}
-
 sdk_snapshot {
     name: "mysdk@current",
     visibility: ["//visibility:public"],
@@ -765,12 +790,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_system-module@current",
-    sdk_member_name: "system-module",
+    name: "mysdk_system-module",
+    prefer: false,
     visibility: ["//visibility:private"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -778,9 +803,21 @@
     jars: ["java/system-module.jar"],
 }
 
-java_import {
-    name: "mysdk_system-module",
+java_system_modules_import {
+    name: "my-system-modules",
     prefer: false,
+    visibility: ["//visibility:public"],
+    device_supported: false,
+    host_supported: true,
+    libs: ["mysdk_system-module"],
+}
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_system-module@current",
+    sdk_member_name: "system-module",
     visibility: ["//visibility:private"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -797,15 +834,6 @@
     libs: ["mysdk_system-module@current"],
 }
 
-java_system_modules_import {
-    name: "my-system-modules",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    device_supported: false,
-    host_supported: true,
-    libs: ["mysdk_system-module"],
-}
-
 sdk_snapshot {
     name: "mysdk@current",
     visibility: ["//visibility:public"],
@@ -856,12 +884,12 @@
 	`)
 
 	CheckSnapshot(t, result, "myexports", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "myexports_hostjavalib@current",
-    sdk_member_name: "hostjavalib",
+    name: "hostjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     device_supported: false,
@@ -870,10 +898,37 @@
 }
 
 java_import {
-    name: "hostjavalib",
+    name: "androidjavalib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
+    jars: ["java/androidjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_hostjavalib@current",
+    sdk_member_name: "hostjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
     device_supported: false,
     host_supported: true,
     jars: ["java/hostjavalib.jar"],
@@ -888,14 +943,6 @@
 }
 
 java_import {
-    name: "androidjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/androidjavalib.jar"],
-}
-
-java_import {
     name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
@@ -911,22 +958,6 @@
     },
 }
 
-java_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    host_supported: true,
-    target: {
-        android: {
-            jars: ["java/android/myjavalib.jar"],
-        },
-        linux_glibc: {
-            jars: ["java/linux_glibc/myjavalib.jar"],
-        },
-    },
-}
-
 module_exports_snapshot {
     name: "myexports@current",
     visibility: ["//visibility:public"],
@@ -970,12 +1001,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: false,
@@ -1001,10 +1032,13 @@
         sdk_version: "test_current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: false,
@@ -1071,12 +1105,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     shared_library: true,
@@ -1088,10 +1122,13 @@
         sdk_version: "none",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     shared_library: true,
@@ -1140,12 +1177,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     shared_library: true,
@@ -1157,10 +1194,13 @@
         sdk_version: "module_current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     shared_library: true,
@@ -1212,12 +1252,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
@@ -1236,10 +1276,13 @@
         sdk_version: "system_current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
@@ -1305,12 +1348,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
@@ -1336,10 +1379,13 @@
         sdk_version: "module_current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
@@ -1413,12 +1459,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
@@ -1437,10 +1483,13 @@
         sdk_version: "system_server_current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     shared_library: true,
@@ -1501,12 +1550,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     naming_scheme: "default",
@@ -1519,10 +1568,13 @@
         sdk_version: "current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:anyapex"],
     naming_scheme: "default",
@@ -1580,12 +1632,12 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "mysdk_myjavalib@current",
-    sdk_member_name: "myjavalib",
+    name: "myjavalib",
+    prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     shared_library: true,
@@ -1598,10 +1650,13 @@
         sdk_version: "current",
     },
 }
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     shared_library: true,
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 6623381..42d5680 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -210,6 +210,10 @@
 	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.ModuleBase.InstallInVendorRamdisk()
 }
 
+func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(s.properties.Recovery_available) || s.ModuleBase.InstallInRecovery()
 }
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 5271f8d..a483370 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -114,7 +114,61 @@
   rm a/Android.bp
   run_soong
 
-  grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja && fail "Old module in output"
+  if grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja; then
+    fail "Old module in output"
+  fi
+}
+
+# Test that an incremental build with a glob doesn't rerun soong_build, and
+# only regenerates the globs on the first but not the second incremental build.
+function test_glob_noop_incremental() {
+  setup
+
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["*.py"],
+}
+EOF
+  touch a/my_little_binary_host.py
+  run_soong
+  local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  local glob_deps_file=out/soong/.glob/a/__py.glob.d
+
+  if [ -e "$glob_deps_file" ]; then
+    fail "Glob deps file unexpectedly written on first build"
+  fi
+
+  run_soong
+  local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  # There is an ineffiencency in glob that requires bpglob to rerun once for each glob to update
+  # the entry in the .ninja_log.  It doesn't update the output file, but we can detect the rerun
+  # by checking if the deps file was created.
+  if [ ! -e "$glob_deps_file" ]; then
+    fail "Glob deps file missing after second build"
+  fi
+
+  local glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file")
+
+  if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
+    fail "Ninja file rewritten on null incremental build"
+  fi
+
+  run_soong
+  local ninja_mtime3=$(stat -c "%y" out/soong/build.ninja)
+  local glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file")
+
+  if [[ "$ninja_mtime2" != "$ninja_mtime3" ]]; then
+    fail "Ninja file rewritten on null incremental build"
+  fi
+
+  # The bpglob commands should not rerun after the first incremental build.
+  if [[ "$glob_deps_mtime2" != "$glob_deps_mtime3" ]]; then
+    fail "Glob deps file rewritten on second null incremental build"
+  fi
 }
 
 function test_add_file_to_glob() {
@@ -404,7 +458,9 @@
 
   grep -q "Engage" out/soong/build.ninja || fail "New action not present"
 
-  grep -q "Make it so" out/soong/build.ninja && fail "Original action still present"
+  if grep -q "Make it so" out/soong/build.ninja; then
+    fail "Original action still present"
+  fi
 }
 
 function test_null_build_after_docs {
@@ -421,6 +477,83 @@
   fi
 }
 
+function test_integrated_bp2build_smoke {
+  setup
+  INTEGRATED_BP2BUILD=1 run_soong
+  if [[ ! -e out/soong/.bootstrap/bp2build_workspace_marker ]]; then
+    fail "bp2build marker file not created"
+  fi
+}
+
+function test_integrated_bp2build_add_android_bp {
+  setup
+
+  mkdir -p a
+  touch a/a.txt
+  cat > a/Android.bp <<'EOF'
+filegroup {
+  name: "a",
+  srcs: ["a.txt"],
+  bazel_module: { bp2build_available: true },
+}
+EOF
+
+  INTEGRATED_BP2BUILD=1 run_soong
+  if [[ ! -e out/soong/bp2build/a/BUILD ]]; then
+    fail "a/BUILD not created";
+  fi
+
+  mkdir -p b
+  touch b/b.txt
+  cat > b/Android.bp <<'EOF'
+filegroup {
+  name: "b",
+  srcs: ["b.txt"],
+  bazel_module: { bp2build_available: true },
+}
+EOF
+
+  INTEGRATED_BP2BUILD=1 run_soong
+  if [[ ! -e out/soong/bp2build/b/BUILD ]]; then
+    fail "b/BUILD not created";
+  fi
+}
+
+function test_integrated_bp2build_null_build {
+  setup
+
+  INTEGRATED_BP2BUILD=1 run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  INTEGRATED_BP2BUILD=1 run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$mtime1" != "$mtime2" ]]; then
+    fail "Output Ninja file changed on null build"
+  fi
+}
+
+function test_integrated_bp2build_add_to_glob {
+  setup
+
+  mkdir -p a
+  touch a/a1.txt
+  cat > a/Android.bp <<'EOF'
+filegroup {
+  name: "a",
+  srcs: ["*.txt"],
+  bazel_module: { bp2build_available: true },
+}
+EOF
+
+  INTEGRATED_BP2BUILD=1 run_soong
+  grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file"
+
+  touch a/a2.txt
+  INTEGRATED_BP2BUILD=1 run_soong
+  grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file"
+}
+
 function test_dump_json_module_graph() {
   setup
   SOONG_DUMP_JSON_MODULE_GRAPH="$MOCK_TOP/modules.json" run_soong
@@ -433,6 +566,7 @@
 test_null_build
 test_null_build_after_docs
 test_soong_build_rebuilt_if_blueprint_changes
+test_glob_noop_incremental
 test_add_file_to_glob
 test_add_android_bp
 test_change_android_bp
@@ -441,3 +575,6 @@
 test_glob_during_bootstrapping
 test_soong_build_rerun_iff_environment_changes
 test_dump_json_module_graph
+test_integrated_bp2build_smoke
+test_integrated_bp2build_null_build
+test_integrated_bp2build_add_to_glob
diff --git a/tests/lib.sh b/tests/lib.sh
index b61ca91..3795dfc 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -6,8 +6,39 @@
 
 REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
 
+if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+  MOCK_TOP="$HARDWIRED_MOCK_TOP"
+else
+  MOCK_TOP=$(mktemp -t -d st.XXXXX)
+  trap cleanup_mock_top EXIT
+fi
+
+WARMED_UP_MOCK_TOP=$(mktemp -t soong_integration_tests_warmup.XXXXXX.tar.gz)
+trap 'rm -f "$WARMED_UP_MOCK_TOP"' EXIT
+
+function warmup_mock_top {
+  info "Warming up mock top ..."
+  info "Mock top warmup archive: $WARMED_UP_MOCK_TOP"
+  cleanup_mock_top
+  mkdir -p "$MOCK_TOP"
+  cd "$MOCK_TOP"
+
+  create_mock_soong
+  run_soong
+  tar czf "$WARMED_UP_MOCK_TOP" *
+}
+
+function cleanup_mock_top {
+  cd /
+  rm -fr "$MOCK_TOP"
+}
+
+function info {
+  echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" $*
+}
+
 function fail {
-  echo ERROR: $1
+  echo -e "\e[91;1mFAILED:\e[0m" $*
   exit 1
 }
 
@@ -47,19 +78,7 @@
   done
 }
 
-function setup() {
-  if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
-    MOCK_TOP="$HARDWIRED_MOCK_TOP"
-    rm -fr "$MOCK_TOP"
-    mkdir -p "$MOCK_TOP"
-  else
-    MOCK_TOP=$(mktemp -t -d st.XXXXX)
-    trap 'cd / && rm -fr "$MOCK_TOP"' EXIT
-  fi
-
-  echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
-  cd "$MOCK_TOP"
-
+function create_mock_soong {
   copy_directory build/blueprint
   copy_directory build/soong
 
@@ -68,12 +87,27 @@
   symlink_directory external/golang-protobuf
 
   touch "$MOCK_TOP/Android.bp"
+}
 
-  export ALLOW_MISSING_DEPENDENCIES=true
+function setup() {
+  cleanup_mock_top
+  mkdir -p "$MOCK_TOP"
 
-  mkdir -p out/soong
+  echo
+  echo ----------------------------------------------------------------------------
+  info "Running test case \e[96;1m${FUNCNAME[1]}\e[0m"
+  cd "$MOCK_TOP"
+
+  tar xzf "$WARMED_UP_MOCK_TOP"
 }
 
 function run_soong() {
   build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
 }
+
+info "Starting Soong integration test suite $(basename $0)"
+info "Mock top: $MOCK_TOP"
+
+
+export ALLOW_MISSING_DEPENDENCIES=true
+warmup_mock_top
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 54f0689..7dbafea 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -8,7 +8,7 @@
 
 source "$(dirname "$0")/lib.sh"
 
-function setup_bazel() {
+function create_mock_bazel() {
   copy_directory build/bazel
 
   symlink_directory prebuilts/bazel
@@ -20,7 +20,7 @@
 
 function test_bazel_smoke {
   setup
-  setup_bazel
+  create_mock_bazel
 
   tools/bazel info
 }
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 0089075..7e94b25 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -21,6 +21,7 @@
 	"strconv"
 
 	"android/soong/shared"
+	"github.com/google/blueprint/deptools"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 	"github.com/google/blueprint"
@@ -33,6 +34,11 @@
 	"android/soong/ui/status"
 )
 
+const (
+	availableEnvFile = "soong.environment.available"
+	usedEnvFile      = "soong.environment.used"
+)
+
 func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
 	data, err := shared.EnvFileContents(envDeps)
 	if err != nil {
@@ -87,12 +93,23 @@
 	return c.debugCompilation
 }
 
-func bootstrapBlueprint(ctx Context, config Config) {
+func environmentArgs(config Config, suffix string) []string {
+	return []string{
+		"--available_env", shared.JoinPath(config.SoongOutDir(), availableEnvFile),
+		"--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix),
+	}
+}
+func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
 	var args bootstrap.Args
 
+	mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
+	globFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
+	bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
+	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
+
 	args.RunGoTests = !config.skipSoongTests
 	args.UseValidations = true // Use validations to depend on tests
 	args.BuildDir = config.SoongOutDir()
@@ -100,8 +117,7 @@
 	args.TopFile = "Android.bp"
 	args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
 	args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
-	args.DepFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
-	args.GlobFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
+	args.GlobFile = globFile
 	args.GeneratingPrimaryBuilder = true
 
 	args.DelveListen = os.Getenv("SOONG_DELVE")
@@ -109,6 +125,44 @@
 		args.DelvePath = shared.ResolveDelveBinary()
 	}
 
+	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, bootstrapGlobFile, mainNinjaFile)
+	bp2BuildMarkerFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+	mainSoongBuildInputs := []string{"Android.bp"}
+
+	if integratedBp2Build {
+		mainSoongBuildInputs = append(mainSoongBuildInputs, bp2BuildMarkerFile)
+	}
+
+	soongBuildArgs := make([]string, 0)
+	soongBuildArgs = append(soongBuildArgs, commonArgs...)
+	soongBuildArgs = append(soongBuildArgs, environmentArgs(config, "")...)
+	soongBuildArgs = append(soongBuildArgs, "Android.bp")
+
+	mainSoongBuildInvocation := bootstrap.PrimaryBuilderInvocation{
+		Inputs:  mainSoongBuildInputs,
+		Outputs: []string{mainNinjaFile},
+		Args:    soongBuildArgs,
+	}
+
+	if integratedBp2Build {
+		bp2buildArgs := []string{"--bp2build_marker", bp2BuildMarkerFile}
+		bp2buildArgs = append(bp2buildArgs, commonArgs...)
+		bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
+		bp2buildArgs = append(bp2buildArgs, "Android.bp")
+
+		bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
+			Inputs:  []string{"Android.bp"},
+			Outputs: []string{bp2BuildMarkerFile},
+			Args:    bp2buildArgs,
+		}
+		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
+			bp2buildInvocation,
+			mainSoongBuildInvocation,
+		}
+	} else {
+		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}
+	}
+
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
@@ -118,7 +172,21 @@
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
 	}
 
-	bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
+	bootstrapDeps := bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
+	err := deptools.WriteDepFile(bootstrapDepFile, args.OutFile, bootstrapDeps)
+	if err != nil {
+		ctx.Fatalf("Error writing depfile '%s': %s", bootstrapDepFile, err)
+	}
+}
+
+func checkEnvironmentFile(currentEnv *Environment, envFile string) {
+	getenv := func(k string) string {
+		v, _ := currentEnv.Get(k)
+		return v
+	}
+	if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
+		os.Remove(envFile)
+	}
 }
 
 func runSoong(ctx Context, config Config) {
@@ -129,7 +197,7 @@
 	// .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")
+	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
 
 	for _, n := range []string{".bootstrap", ".minibootstrap"} {
 		dir := filepath.Join(config.SoongOutDir(), n)
@@ -138,8 +206,10 @@
 		}
 	}
 
+	integratedBp2Build := config.Environment().IsEnvTrue("INTEGRATED_BP2BUILD")
+
 	// This is done unconditionally, but does not take a measurable amount of time
-	bootstrapBlueprint(ctx, config)
+	bootstrapBlueprint(ctx, config, integratedBp2Build)
 
 	soongBuildEnv := config.Environment().Copy()
 	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
@@ -164,13 +234,12 @@
 		ctx.BeginTrace(metrics.RunSoong, "environment check")
 		defer ctx.EndTrace()
 
-		envFile := filepath.Join(config.SoongOutDir(), "soong.environment.used")
-		getenv := func(k string) string {
-			v, _ := soongBuildEnv.Get(k)
-			return v
-		}
-		if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
-			os.Remove(envFile)
+		soongBuildEnvFile := filepath.Join(config.SoongOutDir(), usedEnvFile)
+		checkEnvironmentFile(soongBuildEnv, soongBuildEnvFile)
+
+		if integratedBp2Build {
+			bp2buildEnvFile := filepath.Join(config.SoongOutDir(), usedEnvFile+".bp2build")
+			checkEnvironmentFile(soongBuildEnv, bp2buildEnvFile)
 		}
 	}()