Merge "Clean up package names."
diff --git a/android/Android.bp b/android/Android.bp
index a32e8f2..f5e5606 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -78,9 +78,6 @@
         "variable.go",
         "visibility.go",
         "writedocs.go",
-
-        // Lock down environment access last
-        "env.go",
     ],
     testSrcs: [
         "android_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 7947376..618e4be 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -548,7 +548,7 @@
 		}
 
 		if !amod.InRamdisk() && !amod.InVendorRamdisk() {
-			a.AddStrings("LOCAL_INIT_RC", amod.commonProperties.Init_rc...)
+			a.AddPaths("LOCAL_FULL_INIT_RC", amod.initRcPaths)
 		}
 		a.AddStrings("LOCAL_VINTF_FRAGMENTS", amod.commonProperties.Vintf_fragments...)
 		a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
diff --git a/android/apex.go b/android/apex.go
index 7f9f0f5..25a07b8 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -43,10 +43,8 @@
 	// mergeApexVariations.
 	ApexVariationName string
 
-	// Serialized ApiLevel that this module has to support at minimum. Should be accessed via
-	// MinSdkVersion() method. Cannot be stored in its struct form because this is cloned into
-	// properties structs, and ApiLevel has private members.
-	MinSdkVersionStr string
+	// ApiLevel that this module has to support at minimum.
+	MinSdkVersion ApiLevel
 
 	// True if this module comes from an updatable apexBundle.
 	Updatable bool
@@ -82,19 +80,13 @@
 // have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b
 // are configured to have the same alias variation named apex29.
 func (i ApexInfo) mergedName(ctx PathContext) string {
-	name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
+	name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
 	for _, sdk := range i.RequiredSdks {
 		name += "_" + sdk.Name + "_" + sdk.Version
 	}
 	return name
 }
 
-// MinSdkVersion gives the api level that this module has to support at minimum. This is from the
-// min_sdk_version property of the containing apexBundle.
-func (i ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel {
-	return ApiLevelOrPanic(ctx, i.MinSdkVersionStr)
-}
-
 // IsForPlatform tells whether this module is for the platform or not. If false is returned, it
 // means that this apex variant of the module is built for an APEX.
 func (i ApexInfo) IsForPlatform() bool {
@@ -919,7 +911,7 @@
 					"Consider adding 'min_sdk_version: %q' to %q",
 					minSdkVersion, ctx.ModuleName(), err.Error(),
 					ctx.GetPathString(false),
-					minSdkVersion, ctx.ModuleName())
+					minSdkVersion, toName)
 				return false
 			}
 		}
diff --git a/android/apex_test.go b/android/apex_test.go
index b5323e8..109b1c8 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,10 @@
 		{
 			name: "single",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"foo", "apex10000"},
@@ -45,11 +45,11 @@
 		{
 			name: "merge",
 			in: []ApexInfo{
-				{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
+				{"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
 			wantAliases: [][2]string{
 				{"bar", "apex10000_baz_1"},
 				{"foo", "apex10000_baz_1"},
@@ -58,12 +58,12 @@
 		{
 			name: "don't merge version",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex30", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
-				{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex30"},
@@ -73,11 +73,11 @@
 		{
 			name: "merge updatable",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
@@ -87,12 +87,12 @@
 		{
 			name: "don't merge sdks",
 			in: []ApexInfo{
-				{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
-				{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000_baz_2"},
@@ -102,15 +102,15 @@
 		{
 			name: "don't merge when for prebuilt_apex",
 			in: []ApexInfo{
-				{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
 				// This one should not be merged in with the others because it is for
 				// a prebuilt_apex.
-				{"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+				{"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
-				{"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+				{"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
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 b54ae64..9468891 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -129,54 +129,45 @@
 	// 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,
 	}
 
 	// Per-module denylist to always opt modules out.
 	bp2buildModuleDoNotConvertList = []string{
-		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static
+		"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
-		"libc_init_static",              // ruperts@, cc_library_static
-		"libc_init_dynamic",             // ruperts@, cc_library_static
-		"libc_tzcode",                   // ruperts@, cc_library_static
-		"libc_freebsd",                  // ruperts@, cc_library_static
-		"libc_freebsd_large_stack",      // ruperts@, cc_library_static
-		"libc_netbsd",                   // ruperts@, cc_library_static
-		"libc_openbsd_ndk",              // ruperts@, cc_library_static
-		"libc_openbsd_large_stack",      // ruperts@, cc_library_static
-		"libc_openbsd",                  // ruperts@, cc_library_static
-		"libc_gdtoa",                    // ruperts@, cc_library_static
-		"libc_fortify",                  // ruperts@, cc_library_static
-		"libc_bionic",                   // ruperts@, cc_library_static
+		"libc_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
-		"libc_pthread",                  // ruperts@, cc_library_static
-		"libc_syscalls",                 // ruperts@, cc_library_static
-		"libc_aeabi",                    // ruperts@, cc_library_static
+		"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_ndk",                      // ruperts@, cc_library_static, depends on //bionic/libm:libm
 		"libc_nopthread",                // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
 		"libc_common",                   // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
-		"libc_static_dispatch",          // ruperts@, cc_library_static
-		"libc_dynamic_dispatch",         // ruperts@, cc_library_static
 		"libc_common_static",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
 		"libc_common_shared",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libc_unwind_static",            // ruperts@, cc_library_static
+		"libc_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
+		"libasync_safe",                 // ruperts@, cc_library_static, 'private/CachedProperty.h' file not found
 		"libc_malloc_debug_backtrace",   // ruperts@, cc_library_static, depends on //system/libbase
 		"libsystemproperties",           // ruperts@, cc_library_static, depends on //system/core/property_service/libpropertyinfoparser
-		"libdl_static",                  // ruperts@, cc_library_static
+		"libdl_static",                  // ruperts@, cc_library_static, 'private/CFIShadow.h' file not found
 		"liblinker_main",                // ruperts@, cc_library_static, depends on //system/libbase
 		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
 		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
-		"libbionic_tests_headers_posix", // ruperts@, cc_library_static
-		"libc_dns",                      // ruperts@, cc_library_static
-
-		"note_memtag_heap_async", // jingwen@, b/185079815, features.h includes not found
-		"note_memtag_heap_sync",  // jingwen@, b/185079815, features.h includes not found
+		"libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'complex.h' file not found
+		"libc_dns",                      // ruperts@, cc_library_static, 'android/log.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 +177,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 97eec30..8d561d2 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -67,11 +67,7 @@
 
 	// TODO(cparsons): Other cquery-related methods should be added here.
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
-	GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
-
-	// GetPrebuiltCcStaticLibraryFiles returns paths to prebuilt cc static libraries, and whether the
-	// results were available
-	GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool)
+	GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error)
 
 	// ** End cquery methods
 
@@ -89,16 +85,24 @@
 	BuildStatementsToRegister() []bazel.BuildStatement
 }
 
-// A context object which tracks queued requests that need to be made to Bazel,
-// and their results after the requests have been made.
-type bazelContext struct {
+type bazelRunner interface {
+	issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
+}
+
+type bazelPaths struct {
 	homeDir      string
 	bazelPath    string
 	outputBase   string
 	workspaceDir string
 	buildDir     string
 	metricsDir   string
+}
 
+// A context object which tracks queued requests that need to be made to Bazel,
+// and their results after the requests have been made.
+type bazelContext struct {
+	bazelRunner
+	paths        *bazelPaths
 	requests     map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
 	requestMutex sync.Mutex         // requests can be written in parallel
 
@@ -119,9 +123,8 @@
 type MockBazelContext struct {
 	OutputBaseDir string
 
-	LabelToOutputFiles                 map[string][]string
-	LabelToOutputFilesAndCcObjectFiles map[string]cquery.GetOutputFilesAndCcObjectFiles_Result
-	LabelToCcStaticLibraryFiles        map[string][]string
+	LabelToOutputFiles map[string][]string
+	LabelToCcInfo      map[string]cquery.CcInfo
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
@@ -129,14 +132,9 @@
 	return result, ok
 }
 
-func (m MockBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
-	result, ok := m.LabelToOutputFilesAndCcObjectFiles[label]
-	return result.OutputFiles, result.CcObjectFiles, ok
-}
-
-func (m MockBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := m.LabelToCcStaticLibraryFiles[label]
-	return result, ok
+func (m MockBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+	result, ok := m.LabelToCcInfo[label]
+	return result, ok, nil
 }
 
 func (m MockBazelContext) InvokeBazel() error {
@@ -165,39 +163,22 @@
 	return ret, ok
 }
 
-func (bazelCtx *bazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
-	var outputFiles []string
-	var ccObjects []string
-
-	result, ok := bazelCtx.cquery(label, cquery.GetOutputFilesAndCcObjectFiles, archType)
-	if ok {
-		bazelOutput := strings.TrimSpace(result)
-		returnResult := cquery.GetOutputFilesAndCcObjectFiles.ParseResult(bazelOutput)
-		outputFiles = returnResult.OutputFiles
-		ccObjects = returnResult.CcObjectFiles
-	}
-
-	return outputFiles, ccObjects, ok
-}
-
-// GetPrebuiltCcStaticLibraryFiles returns a slice of prebuilt static libraries for the given
-// label/archType if there are query results; otherwise, it enqueues the query and returns false.
-func (bazelCtx *bazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := bazelCtx.cquery(label, cquery.GetPrebuiltCcStaticLibraryFiles, archType)
+func (bazelCtx *bazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+	result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, archType)
 	if !ok {
-		return nil, false
+		return cquery.CcInfo{}, ok, nil
 	}
 
 	bazelOutput := strings.TrimSpace(result)
-	ret := cquery.GetPrebuiltCcStaticLibraryFiles.ParseResult(bazelOutput)
-	return ret, ok
+	ret, err := cquery.GetCcInfo.ParseResult(bazelOutput)
+	return ret, ok, err
 }
 
 func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
+func (n noopBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
 	panic("unimplemented")
 }
 
@@ -228,42 +209,56 @@
 		return noopBazelContext{}, nil
 	}
 
-	bazelCtx := bazelContext{buildDir: c.buildDir, requests: make(map[cqueryKey]bool)}
+	p, err := bazelPathsFromConfig(c)
+	if err != nil {
+		return nil, err
+	}
+	return &bazelContext{
+		bazelRunner: &builtinBazelRunner{},
+		paths:       p,
+		requests:    make(map[cqueryKey]bool),
+	}, nil
+}
+
+func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
+	p := bazelPaths{
+		buildDir: c.buildDir,
+	}
 	missingEnvVars := []string{}
 	if len(c.Getenv("BAZEL_HOME")) > 1 {
-		bazelCtx.homeDir = c.Getenv("BAZEL_HOME")
+		p.homeDir = c.Getenv("BAZEL_HOME")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
 	}
 	if len(c.Getenv("BAZEL_PATH")) > 1 {
-		bazelCtx.bazelPath = c.Getenv("BAZEL_PATH")
+		p.bazelPath = c.Getenv("BAZEL_PATH")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
 	}
 	if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
-		bazelCtx.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
+		p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
 	}
 	if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
-		bazelCtx.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
+		p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
 	}
 	if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
-		bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
+		p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
 	}
 	if len(missingEnvVars) > 0 {
 		return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
 	} else {
-		return &bazelCtx, nil
+		return &p, nil
 	}
 }
 
-func (context *bazelContext) BazelMetricsDir() string {
-	return context.metricsDir
+func (p *bazelPaths) BazelMetricsDir() string {
+	return p.metricsDir
 }
 
 func (context *bazelContext) BazelEnabled() bool {
@@ -296,17 +291,40 @@
 	return ""
 }
 
+type bazelCommand struct {
+	command string
+	// query or label
+	expression string
+}
+
+type mockBazelRunner struct {
+	bazelCommandResults map[bazelCommand]string
+	commands            []bazelCommand
+}
+
+func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
+	runName bazel.RunName,
+	command bazelCommand,
+	extraFlags ...string) (string, string, error) {
+	r.commands = append(r.commands, command)
+	if ret, ok := r.bazelCommandResults[command]; ok {
+		return ret, "", nil
+	}
+	return "", "", nil
+}
+
+type builtinBazelRunner struct{}
+
 // Issues the given bazel command with given build label and additional flags.
 // Returns (stdout, stderr, error). The first and second return values are strings
 // containing the stdout and stderr of the run command, and an error is returned if
 // the invocation returned an error code.
-func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
+func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) (string, string, error) {
-
-	cmdFlags := []string{"--output_base=" + context.outputBase, command}
-	cmdFlags = append(cmdFlags, labels...)
-	cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
-	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
+	cmdFlags := []string{"--output_base=" + paths.outputBase, command.command}
+	cmdFlags = append(cmdFlags, command.expression)
+	cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+paths.intermediatesDir())
+	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
 
 	// Set default platforms to canonicalized values for mixed builds requests.
 	// If these are set in the bazelrc, they will have values that are
@@ -328,9 +346,9 @@
 	cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
 	cmdFlags = append(cmdFlags, extraFlags...)
 
-	bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
-	bazelCmd.Dir = context.workspaceDir
-	bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix(),
+	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
+	bazelCmd.Dir = paths.workspaceDir
+	bazelCmd.Env = append(os.Environ(), "HOME="+paths.homeDir, pwdPrefix(),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
@@ -367,7 +385,7 @@
     path = "%[1]s/build/bazel/bazel_skylib",
 )
 `
-	return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+	return []byte(fmt.Sprintf(formatString, context.paths.workspaceDir))
 }
 
 func (context *bazelContext) mainBzlFileContents() []byte {
@@ -577,8 +595,8 @@
 
 // Returns a workspace-relative path containing build-related metadata required
 // for interfacing with Bazel. Example: out/soong/bazel.
-func (context *bazelContext) intermediatesDir() string {
-	return filepath.Join(context.buildDir, "bazel")
+func (p *bazelPaths) intermediatesDir() string {
+	return filepath.Join(p.buildDir, "bazel")
 }
 
 // Issues commands to Bazel to receive results for all cquery requests
@@ -590,7 +608,7 @@
 	var cqueryErr string
 	var err error
 
-	intermediatesDirPath := absolutePath(context.intermediatesDir())
+	intermediatesDirPath := absolutePath(context.paths.intermediatesDir())
 	if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
 		err = os.Mkdir(intermediatesDirPath, 0777)
 	}
@@ -599,38 +617,38 @@
 		return err
 	}
 	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
+		filepath.Join(intermediatesDirPath, "main.bzl"),
 		context.mainBzlFileContents(), 0666)
 	if err != nil {
 		return err
 	}
 	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
+		filepath.Join(intermediatesDirPath, "BUILD.bazel"),
 		context.mainBuildFileContents(), 0666)
 	if err != nil {
 		return err
 	}
-	cqueryFileRelpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
+	cqueryFileRelpath := filepath.Join(context.paths.intermediatesDir(), "buildroot.cquery")
 	err = ioutil.WriteFile(
 		absolutePath(cqueryFileRelpath),
 		context.cqueryStarlarkFileContents(), 0666)
 	if err != nil {
 		return err
 	}
-	workspaceFileRelpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
 	err = ioutil.WriteFile(
-		absolutePath(workspaceFileRelpath),
+		filepath.Join(intermediatesDirPath, "WORKSPACE.bazel"),
 		context.workspaceFileContents(), 0666)
 	if err != nil {
 		return err
 	}
 	buildrootLabel := "//:buildroot"
-	cqueryOutput, cqueryErr, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
-		[]string{fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
+	cqueryOutput, cqueryErr, err = context.issueBazelCommand(
+		context.paths,
+		bazel.CqueryBuildRootRunName,
+		bazelCommand{"cquery", fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
 		"--output=starlark",
 		"--starlark:file="+cqueryFileRelpath)
-	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.intermediatesDir(), "cquery.out")),
+	err = ioutil.WriteFile(filepath.Join(intermediatesDirPath, "cquery.out"),
 		[]byte(cqueryOutput), 0666)
 	if err != nil {
 		return err
@@ -661,11 +679,13 @@
 	//
 	// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
 	var aqueryOutput string
-	aqueryOutput, _, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
-		[]string{fmt.Sprintf("deps(%s)", buildrootLabel),
-			// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
-			// proto sources, which would add a number of unnecessary dependencies.
-			"--output=jsonproto"})
+	aqueryOutput, _, err = context.issueBazelCommand(
+		context.paths,
+		bazel.AqueryBuildRootRunName,
+		bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
+		// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+		// proto sources, which would add a number of unnecessary dependencies.
+		"--output=jsonproto")
 
 	if err != nil {
 		return err
@@ -679,8 +699,10 @@
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
-		[]string{"//:phonyroot"})
+	_, _, err = context.issueBazelCommand(
+		context.paths,
+		bazel.BazelBuildPhonyRootRunName,
+		bazelCommand{"build", "//:phonyroot"})
 
 	if err != nil {
 		return err
@@ -696,7 +718,7 @@
 }
 
 func (context *bazelContext) OutputBase() string {
-	return context.outputBase
+	return context.paths.outputBase
 }
 
 // Singleton used for registering BUILD file ninja dependencies (needed
@@ -715,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_handler_test.go b/android/bazel_handler_test.go
new file mode 100644
index 0000000..85f701f
--- /dev/null
+++ b/android/bazel_handler_test.go
@@ -0,0 +1,118 @@
+package android
+
+import (
+	"os"
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+func TestRequestResultsAfterInvokeBazel(t *testing.T) {
+	label := "//foo:bar"
+	arch := Arm64
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+		bazelCommand{command: "cquery", expression: "kind(rule, deps(//:buildroot))"}: `@sourceroot//foo:bar|arm64>>out/foo/bar.txt`,
+	})
+	g, ok := bazelContext.GetOutputFiles(label, arch)
+	if ok {
+		t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
+	}
+	err := bazelContext.InvokeBazel()
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+	}
+	g, ok = bazelContext.GetOutputFiles(label, arch)
+	if !ok {
+		t.Errorf("Expected cquery results after running InvokeBazel(), but got none")
+	} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("Expected output %s, got %s", w, g)
+	}
+}
+
+func TestInvokeBazelWritesBazelFiles(t *testing.T) {
+	bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
+	err := bazelContext.InvokeBazel()
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+	}
+	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "main.bzl")); os.IsNotExist(err) {
+		t.Errorf("Expected main.bzl to exist, but it does not")
+	} else if err != nil {
+		t.Errorf("Unexpected error stating main.bzl %s", err)
+	}
+
+	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "BUILD.bazel")); os.IsNotExist(err) {
+		t.Errorf("Expected BUILD.bazel to exist, but it does not")
+	} else if err != nil {
+		t.Errorf("Unexpected error stating BUILD.bazel %s", err)
+	}
+
+	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "WORKSPACE.bazel")); os.IsNotExist(err) {
+		t.Errorf("Expected WORKSPACE.bazel to exist, but it does not")
+	} else if err != nil {
+		t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err)
+	}
+}
+
+func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+		bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}: `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [1],
+    "outputIds": [1],
+    "primaryOutputId": 1
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "two"
+  }]
+}`,
+	})
+	err := bazelContext.InvokeBazel()
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+	}
+
+	got := bazelContext.BuildStatementsToRegister()
+	if want := 1; len(got) != want {
+		t.Errorf("Expected %d registered build statements, got %#v", want, got)
+	}
+}
+
+func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
+	t.Helper()
+	p := bazelPaths{
+		buildDir:     t.TempDir(),
+		outputBase:   "outputbase",
+		workspaceDir: "workspace_dir",
+	}
+	aqueryCommand := bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}
+	if _, exists := bazelCommandResults[aqueryCommand]; !exists {
+		bazelCommandResults[aqueryCommand] = "{}\n"
+	}
+	runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
+	return &bazelContext{
+		bazelRunner: runner,
+		paths:       &p,
+		requests:    map[cqueryKey]bool{},
+	}, p.buildDir
+}
diff --git a/android/config.go b/android/config.go
index 80651bb..3db7980 100644
--- a/android/config.go
+++ b/android/config.go
@@ -345,7 +345,7 @@
 // multiple runs in the same program execution is carried over (such as Bazel
 // context or environment deps).
 func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile)
+	newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -356,12 +356,12 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
+func NewConfig(srcDir, buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
 
-		env: originalEnv,
+		env: availableEnv,
 
 		srcDir:            srcDir,
 		buildDir:          buildDir,
@@ -1259,7 +1259,7 @@
 	if len(c.productVariables.CFIIncludePaths) == 0 {
 		return false
 	}
-	return HasAnyPrefix(path, c.productVariables.CFIIncludePaths)
+	return HasAnyPrefix(path, c.productVariables.CFIIncludePaths) && !c.CFIDisabledForPath(path)
 }
 
 func (c *config) MemtagHeapDisabledForPath(path string) bool {
@@ -1273,14 +1273,14 @@
 	if len(c.productVariables.MemtagHeapAsyncIncludePaths) == 0 {
 		return false
 	}
-	return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths)
+	return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
 }
 
 func (c *config) MemtagHeapSyncEnabledForPath(path string) bool {
 	if len(c.productVariables.MemtagHeapSyncIncludePaths) == 0 {
 		return false
 	}
-	return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths)
+	return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
 }
 
 func (c *config) VendorConfig(name string) VendorConfig {
@@ -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/env.go b/android/env.go
deleted file mode 100644
index 725a145..0000000
--- a/android/env.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2015 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"android/soong/shared"
-)
-
-// This file supports dependencies on environment variables.  During build
-// manifest generation, any dependency on an environment variable is added to a
-// list.  At the end of the build, a JSON file called soong.environment.used is
-// written containing the current value of all used environment variables. The
-// next time the top-level build script is run, soong_ui parses the compare the
-// contents of the used environment variables, then, if they changed, deletes
-// soong.environment.used to cause a rebuild.
-//
-// The dependency of build.ninja on soong.environment.used is declared in
-// build.ninja.d
-
-var originalEnv map[string]string
-
-func InitEnvironment(envFile string) {
-	var err error
-	originalEnv, err = shared.EnvFromFile(envFile)
-	if err != nil {
-		panic(err)
-	}
-}
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 37b04dd..c303c38 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -107,6 +107,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -416,6 +417,93 @@
 	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
+}
+
 // 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
@@ -445,6 +533,7 @@
 	}
 	labels := expandSrcsForBazel(ctx, paths, excluded)
 	labels.Excludes = excludeLabels.Includes
+	labels = transformSubpackagePaths(ctx, labels)
 	return labels
 }
 
@@ -1101,11 +1190,12 @@
 		// a single file.
 		files, err = gctx.GlobWithDeps(path.String(), nil)
 	} else {
-		var deps []string
+		var result pathtools.GlobResult
 		// We cannot add build statements in this context, so we fall back to
 		// AddNinjaFileDeps
-		files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
-		ctx.AddNinjaFileDeps(deps...)
+		result, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
+		ctx.AddNinjaFileDeps(result.Deps...)
+		files = result.Matches
 	}
 
 	if err != nil {
@@ -1848,6 +1938,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"
@@ -2018,6 +2118,7 @@
 	inSanitizerDir  bool
 	inRamdisk       bool
 	inVendorRamdisk bool
+	inDebugRamdisk  bool
 	inRecovery      bool
 	inRoot          bool
 	forceOS         *OsType
@@ -2050,6 +2151,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/prebuilt.go b/android/prebuilt.go
index 2fc4782..40bcdfd 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -339,6 +339,13 @@
 		return false
 	}
 
+	// Skip prebuilt modules under unexported namespaces so that we won't
+	// end up shadowing non-prebuilt module when prebuilt module under same
+	// name happens to have a `Prefer` property set to true.
+	if ctx.Config().KatiEnabled() && !prebuilt.ExportedToMake() {
+		return false
+	}
+
 	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
 	if Bool(p.properties.Prefer) {
 		return true
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 5fdaa91..98db824 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -22,15 +22,15 @@
 
 type SdkContext interface {
 	// SdkVersion returns SdkSpec that corresponds to the sdk_version property of the current module
-	SdkVersion() SdkSpec
+	SdkVersion(ctx EarlyModuleContext) SdkSpec
 	// SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
 	SystemModules() string
 	// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
-	MinSdkVersion() SdkSpec
+	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
 	// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
-	TargetSdkVersion() SdkSpec
+	TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
 }
 
 // SdkKind represents a particular category of an SDK spec like public, system, test, etc.
@@ -201,15 +201,23 @@
 	return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
 }
 
-func SdkSpecFrom(str string) SdkSpec {
+var (
+	SdkSpecNone = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
+	// TODO(b/175678607) ApiLevel of SdkSpecPrivate should be FutureApiLevel
+	SdkSpecPrivate = SdkSpec{SdkPrivate, NoneApiLevel, ""}
+	// TODO(b/175678607) ApiLevel of SdkSpecCorePlatform should be FutureApiLevel
+	SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, NoneApiLevel, "core_platform"}
+)
+
+func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
 	switch str {
 	// special cases first
 	case "":
-		return SdkSpec{SdkPrivate, NoneApiLevel, str}
+		return SdkSpecPrivate
 	case "none":
-		return SdkSpec{SdkNone, NoneApiLevel, str}
+		return SdkSpecNone
 	case "core_platform":
-		return SdkSpec{SdkCorePlatform, NoneApiLevel, str}
+		return SdkSpecCorePlatform
 	default:
 		// the syntax is [kind_]version
 		sep := strings.LastIndex(str, "_")
@@ -242,15 +250,10 @@
 			return SdkSpec{SdkInvalid, NoneApiLevel, str}
 		}
 
-		var apiLevel ApiLevel
-		if versionString == "current" {
-			apiLevel = FutureApiLevel
-		} else if i, err := strconv.Atoi(versionString); err == nil {
-			apiLevel = uncheckedFinalApiLevel(i)
-		} else {
+		apiLevel, err := ApiLevelFromUser(ctx, versionString)
+		if err != nil {
 			return SdkSpec{SdkInvalid, apiLevel, str}
 		}
-
 		return SdkSpec{kind, apiLevel, str}
 	}
 }
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/androidmk.go b/apex/androidmk.go
index 99cd75e..9fc701d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -284,7 +284,7 @@
 					// To install companion files (init_rc, vintf_fragments)
 					// Copy some common properties of apexBundle to apex_manifest
 					commonProperties := []string{
-						"LOCAL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+						"LOCAL_FULL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
 					}
 					for _, name := range commonProperties {
 						if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok {
@@ -394,7 +394,7 @@
 				// Because apex writes .mk with Custom(), we need to write manually some common properties
 				// which are available via data.Entries
 				commonProperties := []string{
-					"LOCAL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+					"LOCAL_FULL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
 					"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
 					"LOCAL_MODULE_OWNER",
 				}
diff --git a/apex/apex.go b/apex/apex.go
index 880028f..f5e6fa9 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -903,7 +903,7 @@
 	// be built for this apexBundle.
 	apexInfo := android.ApexInfo{
 		ApexVariationName: mctx.ModuleName(),
-		MinSdkVersionStr:  minSdkVersion.String(),
+		MinSdkVersion:     minSdkVersion,
 		RequiredSdks:      a.RequiredSdks(),
 		Updatable:         a.Updatable(),
 		InApexes:          []string{mctx.ModuleName()},
@@ -2272,8 +2272,10 @@
 		tag := ctx.OtherModuleDependencyTag(module)
 		switch tag {
 		case javaLibTag, androidAppTag:
-			if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
-				if err := m.CheckStableSdkVersion(); err != nil {
+			if m, ok := module.(interface {
+				CheckStableSdkVersion(ctx android.BaseModuleContext) error
+			}); ok {
+				if err := m.CheckStableSdkVersion(ctx); err != nil {
 					ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
 				}
 			}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5439265..977a954 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2756,7 +2756,7 @@
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_VINTF_FRAGMENTS := fragment.xml\n")
-	ensureContains(t, androidMk, "LOCAL_INIT_RC := init.rc\n")
+	ensureContains(t, androidMk, "LOCAL_FULL_INIT_RC := init.rc\n")
 }
 
 func TestStaticLinking(t *testing.T) {
@@ -4375,9 +4375,7 @@
 // These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
 // propagation of paths to dex implementation jars from the former to the latter.
 func TestPrebuiltExportDexImplementationJars(t *testing.T) {
-	transform := func(config *dexpreopt.GlobalConfig) {
-		// Empty transformation.
-	}
+	transform := android.NullFixturePreparer
 
 	checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
 		// Make sure the import has been given the correct path to the dex jar.
@@ -4547,9 +4545,7 @@
 }
 
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
-	transform := func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo", "myapex:libbar"})
-	}
+	preparer := java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar")
 
 	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
 		t.Helper()
@@ -4570,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)
 	}
 
@@ -4605,7 +4601,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -4639,7 +4635,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -4698,7 +4694,7 @@
 		// prebuilt_apex module always depends on the prebuilt, and so it doesn't
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
-		testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", transform)
+		testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", preparer)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -4746,7 +4742,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -4813,7 +4809,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
@@ -4830,7 +4826,7 @@
 			name: "myapex",
 			enabled: false,
 			key: "myapex.key",
-			java_libs: ["libfoo"],
+			java_libs: ["libfoo", "libbar"],
 		}
 
 		apex_key {
@@ -4882,14 +4878,14 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/prebuilt_libbar/android_common_prebuilt_myapex/hiddenapi/index.csv
-.intermediates/prebuilt_libfoo/android_common_prebuilt_myapex/hiddenapi/index.csv
+.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv
 `)
 	})
 }
@@ -6440,7 +6436,7 @@
 	android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
 }
 
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer) {
 	t.Helper()
 
 	bp := `
@@ -6528,10 +6524,10 @@
 		}
 	`
 
-	testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
+	testDexpreoptWithApexes(t, bp, errmsg, preparer)
 }
 
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer) *android.TestContext {
 	t.Helper()
 
 	fs := android.MockFS{
@@ -6557,18 +6553,13 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		PrepareForTestWithApexBuildComponents,
-		android.FixtureModifyConfig(func(config android.Config) {
-			pathCtx := android.PathContextForTesting(config)
-			dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-			transformDexpreoptConfig(dexpreoptConfig)
-			dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
-			// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
-			// product variables.
-			config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
-			config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
-		}),
+		preparer,
 		fs.AddToFixture(),
+		android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`),
 	).
 		ExtendWithErrorHandler(errorHandler).
 		RunTestWithBp(t, bp)
@@ -6608,92 +6599,95 @@
 }
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
-	var err string
-	var transform func(*dexpreopt.GlobalConfig)
+	// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
+	// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
+	// modules to be included in the BootJars.
+	prepareSetBootJars := func(bootJars ...string) android.FixturePreparer {
+		return android.GroupFixturePreparers(
+			dexpreopt.FixtureSetBootJars(bootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+			}),
+		)
+	}
+
+	// Set the ArtApexJars and BootJars in dexpreopt.GlobalConfig and productVariables all to the
+	// same value. This can result in an invalid configuration as it allows non art apex jars to be
+	// specified in the ArtApexJars configuration.
+	prepareSetArtJars := func(bootJars ...string) android.FixturePreparer {
+		return android.GroupFixturePreparers(
+			dexpreopt.FixtureSetArtBootJars(bootJars...),
+			dexpreopt.FixtureSetBootJars(bootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+			}),
+		)
+	}
 
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"com.android.art.debug:some-art-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, "", transform)
+		preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
+		testNoUpdatableJarsInBootImage(t, "", preparer)
 	})
 
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
-		err = `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"com.android.art.debug:some-art-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
+		// Update the dexpreopt BootJars directly.
+		preparer := prepareSetBootJars("com.android.art.debug:some-art-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"some-updatable-apex:some-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
+		// Update the dexpreopt ArtApexJars directly.
+		preparer := prepareSetArtJars("some-updatable-apex:some-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err = `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
+		// Update the dexpreopt ArtApexJars directly.
+		preparer := prepareSetArtJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
-		err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"some-updatable-apex:some-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
+		preparer := java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, "", transform)
+		preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, "", preparer)
 	})
 
 	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
-		err = "failed to find a dex jar path for module 'nonexistent'"
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"platform:nonexistent"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
+		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
-		err = "failed to find a dex jar path for module 'nonexistent'"
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"platform:nonexistent"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
+		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
-		err = `module "some-platform-lib" is not allowed in the ART boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"platform:some-platform-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-platform-lib" is not allowed in the ART boot image`
+		// Update the dexpreopt ArtApexJars directly.
+		preparer := prepareSetArtJars("platform:some-platform-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"platform:some-platform-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, "", transform)
+		preparer := java.FixtureConfigureBootJars("platform:some-platform-lib")
+		testNoUpdatableJarsInBootImage(t, "", preparer)
 	})
-
 }
 
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
-	transform := func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
-	}
+	preparer := java.FixtureConfigureBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
 		testDexpreoptWithApexes(t, `
 			prebuilt_apex {
@@ -6713,7 +6707,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 		}
-`, "", transform)
+`, "", preparer)
 	})
 
 	t.Run("prebuilt no source", func(t *testing.T) {
@@ -6735,7 +6729,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 		}
-`, "", transform)
+`, "", preparer)
 	})
 }
 
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index aa0d9c4..d447d70 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -19,7 +19,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/dexpreopt"
 	"android/soong/java"
 )
 
@@ -42,8 +41,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootImage,
 		// Configure some libraries in the art and framework boot images.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"),
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"),
 		prepareForTestWithArtApex,
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -169,7 +167,7 @@
 		prepareForTestWithArtApex,
 
 		// Configure some libraries in the art boot image.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -264,7 +262,7 @@
 		}),
 
 		// Configure some libraries in the art boot image.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 	).RunTestWithBp(t, `
 		prebuilt_apex {
 			name: "com.android.art",
diff --git a/apex/builder.go b/apex/builder.go
index 2df380b..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")
@@ -946,16 +948,19 @@
 			depInfos[to.Name()] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+			if m, ok := to.(interface {
+				MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+			}); ok {
+				if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
+					toMinSdkVersion = v.ApiLevel.String()
+				}
+			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
+				// string
 				if v := m.MinSdkVersion(); v != "" {
 					toMinSdkVersion = v
 				}
-			} else if m, ok := to.(interface{ MinSdkVersionString() string }); ok {
-				if v := m.MinSdkVersionString(); v != "" {
-					toMinSdkVersion = v
-				}
 			}
-
 			depInfos[to.Name()] = android.ApexModuleDepInfo{
 				To:            to.Name(),
 				From:          []string{from.Name()},
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 2ea6401..74830d3 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -18,7 +18,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/dexpreopt"
 	"android/soong/java"
 	"github.com/google/blueprint"
 )
@@ -37,9 +36,8 @@
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure some libraries in the art and framework boot images.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"),
-		dexpreopt.FixtureSetBootJars("platform:foo"),
-		dexpreopt.FixtureSetUpdatableBootJars("myapex:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo"),
+		java.FixtureConfigureUpdatableBootJars("myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 10a70a3..a9d24a7 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -172,7 +172,7 @@
 
 	// Create an ApexInfo for the prebuilt_apex.
 	apexInfo := android.ApexInfo{
-		ApexVariationName: mctx.ModuleName(),
+		ApexVariationName: android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()),
 		InApexes:          []string{mctx.ModuleName()},
 		ApexContents:      []*android.ApexContents{apexContents},
 		ForPrebuiltApex:   true,
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index b88da2f..8049108 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -1,18 +1,19 @@
 package cquery
 
 import (
+	"fmt"
 	"strings"
 )
 
 var (
-	GetOutputFiles                  = &getOutputFilesRequestType{}
-	GetOutputFilesAndCcObjectFiles  = &getOutputFilesAndCcObjectFilesType{}
-	GetPrebuiltCcStaticLibraryFiles = &getPrebuiltCcStaticLibraryFiles{}
+	GetOutputFiles = &getOutputFilesRequestType{}
+	GetCcInfo      = &getCcInfoType{}
 )
 
-type GetOutputFilesAndCcObjectFiles_Result struct {
-	OutputFiles   []string
-	CcObjectFiles []string
+type CcInfo struct {
+	OutputFiles          []string
+	CcObjectFiles        []string
+	CcStaticLibraryFiles []string
 }
 
 type getOutputFilesRequestType struct{}
@@ -39,15 +40,15 @@
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
 func (g getOutputFilesRequestType) ParseResult(rawString string) []string {
-	return strings.Split(rawString, ", ")
+	return splitOrEmpty(rawString, ", ")
 }
 
-type getOutputFilesAndCcObjectFilesType struct{}
+type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
 // and must only consist of alphanumeric characters.
-func (g getOutputFilesAndCcObjectFilesType) Name() string {
-	return "getOutputFilesAndCcObjectFiles"
+func (g getCcInfoType) Name() string {
+	return "getCcInfo"
 }
 
 // StarlarkFunctionBody returns a starlark function body to process this request type.
@@ -58,61 +59,52 @@
 //   - `target` is the only parameter to this function (a configured target).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
-func (g getOutputFilesAndCcObjectFilesType) StarlarkFunctionBody() string {
+func (g getCcInfoType) StarlarkFunctionBody() string {
 	return `
 outputFiles = [f.path for f in target.files.to_list()]
 
 ccObjectFiles = []
+staticLibraries = []
 linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
   for library in linker_input.libraries:
     for object in library.objects:
       ccObjectFiles += [object.path]
-return ', '.join(outputFiles) + "|" + ', '.join(ccObjectFiles)`
+    if library.static_library:
+      staticLibraries.append(library.static_library.path)
+
+returns = [
+  outputFiles,
+  staticLibraries,
+  ccObjectFiles,
+]
+
+return "|".join([", ".join(r) for r in returns])`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
-func (g getOutputFilesAndCcObjectFilesType) ParseResult(rawString string) GetOutputFilesAndCcObjectFiles_Result {
+func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
 	var outputFiles []string
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
+	if expectedLen := 3; len(splitString) != expectedLen {
+		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+	}
 	outputFilesString := splitString[0]
-	ccObjectsString := splitString[1]
+	ccStaticLibrariesString := splitString[1]
+	ccObjectsString := splitString[2]
 	outputFiles = splitOrEmpty(outputFilesString, ", ")
+	ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
 	ccObjects = splitOrEmpty(ccObjectsString, ", ")
-	return GetOutputFilesAndCcObjectFiles_Result{outputFiles, ccObjects}
-}
-
-type getPrebuiltCcStaticLibraryFiles struct{}
-
-// Name returns the name of the starlark function to get prebuilt cc static library files
-func (g getPrebuiltCcStaticLibraryFiles) Name() string {
-	return "getPrebuiltCcStaticLibraryFiles"
-}
-
-// StarlarkFunctionBody returns the unindented body of a starlark function for extracting the static
-// library paths from a cc_import module.
-func (g getPrebuiltCcStaticLibraryFiles) StarlarkFunctionBody() string {
-	return `
-linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
-
-static_libraries = []
-for linker_input in linker_inputs:
-  for library in linker_input.libraries:
-    static_libraries.append(library.static_library.path)
-
-return ', '.join(static_libraries)`
-}
-
-// ParseResult returns a slice of bazel output paths to static libraries if any exist for the given
-// rawString corresponding to the string output which was created by evaluating the
-// StarlarkFunctionBody.
-func (g getPrebuiltCcStaticLibraryFiles) ParseResult(rawString string) []string {
-	return strings.Split(rawString, ", ")
+	return CcInfo{
+		OutputFiles:          outputFiles,
+		CcObjectFiles:        ccObjects,
+		CcStaticLibraryFiles: ccStaticLibraries,
+	}, nil
 }
 
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
new file mode 100644
index 0000000..48edb90
--- /dev/null
+++ b/bazel/cquery/request_type_test.go
@@ -0,0 +1,95 @@
+package cquery
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+)
+
+func TestGetOutputFilesParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput []string
+	}{
+		{
+			description:    "no result",
+			input:          "",
+			expectedOutput: []string{},
+		},
+		{
+			description:    "one result",
+			input:          "test",
+			expectedOutput: []string{"test"},
+		},
+		{
+			description:    "splits on comma with space",
+			input:          "foo, bar",
+			expectedOutput: []string{"foo", "bar"},
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetOutputFiles.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
+
+func TestGetCcInfoParseResults(t *testing.T) {
+	testCases := []struct {
+		description          string
+		input                string
+		expectedOutput       CcInfo
+		expectedErrorMessage string
+	}{
+		{
+			description: "no result",
+			input:       "||",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{},
+				CcObjectFiles:        []string{},
+				CcStaticLibraryFiles: []string{},
+			},
+		},
+		{
+			description: "only output",
+			input:       "test||",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{"test"},
+				CcObjectFiles:        []string{},
+				CcStaticLibraryFiles: []string{},
+			},
+		},
+		{
+			description: "all items set",
+			input:       "out1, out2|static_lib1, static_lib2|object1, object2",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{"out1", "out2"},
+				CcObjectFiles:        []string{"object1", "object2"},
+				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
+			},
+		},
+		{
+			description:          "too few result splits",
+			input:                "|",
+			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)
+		if (err == nil && tc.expectedErrorMessage != "") ||
+			(err != nil && err.Error() != tc.expectedErrorMessage) {
+			t.Errorf("%q: expected Error %s, got %s", tc.description, tc.expectedErrorMessage, err)
+		} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
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/api_level.go b/cc/api_level.go
index c93d6ed..fd145a9 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -53,14 +53,6 @@
 	return value, nil
 }
 
-func nativeApiLevelFromUserWithDefault(ctx android.BaseModuleContext,
-	raw string, defaultValue string) (android.ApiLevel, error) {
-	if raw == "" {
-		raw = defaultValue
-	}
-	return nativeApiLevelFromUser(ctx, raw)
-}
-
 func nativeApiLevelOrPanic(ctx android.BaseModuleContext,
 	raw string) android.ApiLevel {
 	value, err := nativeApiLevelFromUser(ctx, raw)
diff --git a/cc/cc.go b/cc/cc.go
index 9176bc3..7f69d56 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1113,16 +1113,33 @@
 	return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
 }
 
-// isLLndk returns true for both LLNDK (public) and LLNDK-private libs.
 func (c *Module) IsLlndk() bool {
 	return c.VendorProperties.IsLLNDK
 }
 
-// IsLlndkPublic returns true only for LLNDK (public) libs.
 func (c *Module) IsLlndkPublic() bool {
 	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
 }
 
+func (c *Module) IsLlndkHeaders() bool {
+	if _, ok := c.linker.(*llndkHeadersDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) IsLlndkLibrary() bool {
+	if _, ok := c.linker.(*llndkStubDecorator); ok {
+		return true
+	}
+	return false
+}
+
+func (m *Module) HasLlndkStubs() bool {
+	lib := moduleLibraryInterface(m)
+	return lib != nil && lib.hasLLNDKStubs()
+}
+
 // isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
 // and does not set llndk.vendor_available: false.
 func (c *Module) isImplementationForLLNDKPublic() bool {
@@ -1186,6 +1203,10 @@
 	return false
 }
 
+func (c *Module) SubName() string {
+	return c.Properties.SubName
+}
+
 func (c *Module) MustUseVendorVariant() bool {
 	return c.isVndkSp() || c.Properties.MustUseVendorVariant
 }
@@ -1246,7 +1267,7 @@
 	return c.linker != nil && c.linker.nativeCoverage()
 }
 
-func (c *Module) isSnapshotPrebuilt() bool {
+func (c *Module) IsSnapshotPrebuilt() bool {
 	if p, ok := c.linker.(snapshotInterface); ok {
 		return p.isSnapshotPrebuilt()
 	}
@@ -2289,12 +2310,7 @@
 			if ccFrom.vndkdep != nil {
 				ccFrom.vndkdep.vndkCheckLinkType(ctx, ccTo, tag)
 			}
-		} else if linkableMod, ok := to.(LinkableInterface); ok {
-			// Static libraries from other languages can be linked
-			if !linkableMod.Static() {
-				ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
-			}
-		} else {
+		} else if _, ok := to.(LinkableInterface); !ok {
 			ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
 		}
 		return
@@ -2483,7 +2499,7 @@
 	c.apexSdkVersion = android.FutureApiLevel
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
-		c.apexSdkVersion = apexInfo.MinSdkVersion(ctx)
+		c.apexSdkVersion = apexInfo.MinSdkVersion
 	}
 
 	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
@@ -2807,7 +2823,7 @@
 					c.sabi.Properties.ReexportedIncludes, depExporterInfo.IncludeDirs.Strings()...)
 			}
 
-			makeLibName := c.makeLibName(ctx, ccDep, depName) + libDepTag.makeSuffix
+			makeLibName := MakeLibName(ctx, c, ccDep, depName) + libDepTag.makeSuffix
 			switch {
 			case libDepTag.header():
 				c.Properties.AndroidMkHeaderLibs = append(
@@ -2846,7 +2862,7 @@
 			switch depTag {
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
-					c.Properties.AndroidMkRuntimeLibs, c.makeLibName(ctx, ccDep, depName)+libDepTag.makeSuffix)
+					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, depName)+libDepTag.makeSuffix)
 				// Record baseLibName for snapshots.
 				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
 			case objDepTag:
@@ -2924,7 +2940,8 @@
 	return libName
 }
 
-func (c *Module) makeLibName(ctx android.ModuleContext, ccDep LinkableInterface, depName string) string {
+func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
+
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
 	libName := baseLibName(depName)
@@ -2934,6 +2951,7 @@
 	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
 
 	if ccDepModule != nil {
+		// TODO(ivanlozano) Support snapshots for Rust-produced C library variants.
 		// Use base module name for snapshots when exporting to Makefile.
 		if snapshotPrebuilt, ok := ccDepModule.linker.(snapshotInterface); ok {
 			baseName := ccDepModule.BaseModuleName()
@@ -2947,10 +2965,10 @@
 		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
 		// core module instead.
 		return libName
-	} else if ccDep.UseVndk() && nonSystemVariantsExist && ccDepModule != nil {
+	} else if ccDep.UseVndk() && nonSystemVariantsExist {
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
-		return libName + ccDepModule.Properties.SubName
+		return libName + ccDep.SubName()
 	} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 		return libName + vendorPublicLibrarySuffix
 	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 465283d..07dcc95 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -244,6 +244,120 @@
 	}
 }
 
+func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
+	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+	partitionDefined := false
+	checkPartition := func(specific bool, partition string) {
+		if specific {
+			if expected != partition && !partitionDefined {
+				// The variant is installed to the 'partition'
+				t.Errorf("%s variant of %q must not be installed to %s partition", variant, name, partition)
+			}
+			partitionDefined = true
+		} else {
+			// The variant is not installed to the 'partition'
+			if expected == partition {
+				t.Errorf("%s variant of %q must be installed to %s partition", variant, name, partition)
+			}
+		}
+	}
+	socSpecific := func(m *Module) bool {
+		return m.SocSpecific() || m.socSpecificModuleContext()
+	}
+	deviceSpecific := func(m *Module) bool {
+		return m.DeviceSpecific() || m.deviceSpecificModuleContext()
+	}
+	productSpecific := func(m *Module) bool {
+		return m.ProductSpecific() || m.productSpecificModuleContext()
+	}
+	systemExtSpecific := func(m *Module) bool {
+		return m.SystemExtSpecific()
+	}
+	checkPartition(socSpecific(mod), "vendor")
+	checkPartition(deviceSpecific(mod), "odm")
+	checkPartition(productSpecific(mod), "product")
+	checkPartition(systemExtSpecific(mod), "system_ext")
+	if !partitionDefined && expected != "system" {
+		t.Errorf("%s variant of %q is expected to be installed to %s partition,"+
+			" but installed to system partition", variant, name, expected)
+	}
+}
+
+func TestInstallPartition(t *testing.T) {
+	t.Helper()
+	ctx := prepareForCcTest.RunTestWithBp(t, `
+		cc_library {
+			name: "libsystem",
+		}
+		cc_library {
+			name: "libsystem_ext",
+			system_ext_specific: true,
+		}
+		cc_library {
+			name: "libproduct",
+			product_specific: true,
+		}
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+		}
+		cc_library {
+			name: "libodm",
+			device_specific: true,
+		}
+		cc_library {
+			name: "liball_available",
+			vendor_available: true,
+			product_available: true,
+		}
+		cc_library {
+			name: "libsystem_ext_all_available",
+			system_ext_specific: true,
+			vendor_available: true,
+			product_available: true,
+		}
+		cc_library {
+			name: "liball_available_odm",
+			odm_available: true,
+			product_available: true,
+		}
+		cc_library {
+			name: "libproduct_vendoravailable",
+			product_specific: true,
+			vendor_available: true,
+		}
+		cc_library {
+			name: "libproduct_odmavailable",
+			product_specific: true,
+			odm_available: true,
+		}
+	`).TestContext
+
+	checkInstallPartition(t, ctx, "libsystem", coreVariant, "system")
+	checkInstallPartition(t, ctx, "libsystem_ext", coreVariant, "system_ext")
+	checkInstallPartition(t, ctx, "libproduct", productVariant, "product")
+	checkInstallPartition(t, ctx, "libvendor", vendorVariant, "vendor")
+	checkInstallPartition(t, ctx, "libodm", vendorVariant, "odm")
+
+	checkInstallPartition(t, ctx, "liball_available", coreVariant, "system")
+	checkInstallPartition(t, ctx, "liball_available", productVariant, "product")
+	checkInstallPartition(t, ctx, "liball_available", vendorVariant, "vendor")
+
+	checkInstallPartition(t, ctx, "libsystem_ext_all_available", coreVariant, "system_ext")
+	checkInstallPartition(t, ctx, "libsystem_ext_all_available", productVariant, "product")
+	checkInstallPartition(t, ctx, "libsystem_ext_all_available", vendorVariant, "vendor")
+
+	checkInstallPartition(t, ctx, "liball_available_odm", coreVariant, "system")
+	checkInstallPartition(t, ctx, "liball_available_odm", productVariant, "product")
+	checkInstallPartition(t, ctx, "liball_available_odm", vendorVariant, "odm")
+
+	checkInstallPartition(t, ctx, "libproduct_vendoravailable", productVariant, "product")
+	checkInstallPartition(t, ctx, "libproduct_vendoravailable", vendorVariant, "vendor")
+
+	checkInstallPartition(t, ctx, "libproduct_odmavailable", productVariant, "product")
+	checkInstallPartition(t, ctx, "libproduct_odmavailable", vendorVariant, "odm")
+}
+
 func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string,
 	isVndkSp bool, extends string, variant string) {
 
@@ -3807,8 +3921,9 @@
 	}),
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
-		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
-		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+		// "subdir_exclude" is covered by both include and exclude paths. Exclude wins.
+		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"}
+		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"}
 	}),
 )
 
diff --git a/cc/gen.go b/cc/gen.go
index 83c019c..b152e02 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -111,9 +111,7 @@
 	return ret
 }
 
-func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path,
-	outFile, depFile android.ModuleGenPath, aidlFlags string) android.Paths {
-
+func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path, aidlFlags string) (cppFile android.OutputPath, headerFiles android.Paths) {
 	aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
 	baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
 	shortName := baseName
@@ -126,6 +124,8 @@
 	}
 
 	outDir := android.PathForModuleGen(ctx, "aidl")
+	cppFile = outDir.Join(ctx, aidlPackage, baseName+".cpp")
+	depFile := outDir.Join(ctx, aidlPackage, baseName+".cpp.d")
 	headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
 	headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
 	headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
@@ -142,14 +142,14 @@
 		Flag(aidlFlags).
 		Input(aidlFile).
 		OutputDir().
-		Output(outFile).
+		Output(cppFile).
 		ImplicitOutputs(android.WritablePaths{
 			headerI,
 			headerBn,
 			headerBp,
 		})
 
-	return android.Paths{
+	return cppFile, android.Paths{
 		headerI,
 		headerBn,
 		headerBp,
@@ -293,10 +293,9 @@
 				aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
 					android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
 			}
-			cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp")
-			depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
+			cppFile, aidlHeaders := genAidl(ctx, aidlRule, srcFile, buildFlags.aidlFlags)
 			srcFiles[i] = cppFile
-			aidlHeaders := genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)
+
 			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
 			// Use the generated headers as order only deps to ensure that they are up to date when
 			// needed.
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 afe6a0e..bf662c6 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -49,21 +49,15 @@
 )
 
 func (ctx *moduleContext) ProductSpecific() bool {
-	// Additionally check if this module is inProduct() that means it is a "product" variant of a
-	// module. As well as product specific modules, product variants must be installed to /product.
-	return ctx.ModuleContext.ProductSpecific() || ctx.mod.InProduct()
+	return ctx.ModuleContext.ProductSpecific() || ctx.mod.productSpecificModuleContext()
 }
 
 func (ctx *moduleContext) SocSpecific() bool {
-	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
-	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
-	// unless they have "odm_available: true".
-	return ctx.ModuleContext.SocSpecific() || (ctx.mod.InVendor() && !ctx.mod.VendorVariantToOdm())
+	return ctx.ModuleContext.SocSpecific() || ctx.mod.socSpecificModuleContext()
 }
 
 func (ctx *moduleContext) DeviceSpecific() bool {
-	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
-	return ctx.ModuleContext.DeviceSpecific() || (ctx.mod.InVendor() && ctx.mod.VendorVariantToOdm())
+	return ctx.ModuleContext.DeviceSpecific() || ctx.mod.deviceSpecificModuleContext()
 }
 
 func (ctx *moduleContextImpl) inProduct() bool {
@@ -86,6 +80,24 @@
 	return ctx.mod.InRecovery()
 }
 
+func (c *Module) productSpecificModuleContext() bool {
+	// Additionally check if this module is inProduct() that means it is a "product" variant of a
+	// module. As well as product specific modules, product variants must be installed to /product.
+	return c.InProduct()
+}
+
+func (c *Module) socSpecificModuleContext() bool {
+	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
+	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
+	// unless they have "odm_available: true".
+	return c.HasVendorVariant() && c.InVendor() && !c.VendorVariantToOdm()
+}
+
+func (c *Module) deviceSpecificModuleContext() bool {
+	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
+	return c.InVendor() && c.VendorVariantToOdm()
+}
+
 // Returns true when this module is configured to have core and vendor variants.
 func (c *Module) HasVendorVariant() bool {
 	return Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Odm_available)
@@ -187,40 +199,73 @@
 	return true
 }
 
+// ImageMutatableModule provides a common image mutation interface for  LinkableInterface modules.
+type ImageMutatableModule interface {
+	android.Module
+	LinkableInterface
+
+	// AndroidModuleBase returns the android.ModuleBase for this module
+	AndroidModuleBase() *android.ModuleBase
+
+	// VendorAvailable returns true if this module is available on the vendor image.
+	VendorAvailable() bool
+
+	// OdmAvailable returns true if this module is available on the odm image.
+	OdmAvailable() bool
+
+	// ProductAvailable returns true if this module is available on the product image.
+	ProductAvailable() bool
+
+	// RamdiskAvailable returns true if this module is available on the ramdisk image.
+	RamdiskAvailable() bool
+
+	// RecoveryAvailable returns true if this module is available on the recovery image.
+	RecoveryAvailable() bool
+
+	// VendorRamdiskAvailable returns true if this module is available on the vendor ramdisk image.
+	VendorRamdiskAvailable() bool
+
+	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
+	IsSnapshotPrebuilt() bool
+
+	// SnapshotVersion returns the snapshot version for this module.
+	SnapshotVersion(mctx android.BaseModuleContext) string
+
+	// SdkVersion returns the SDK version for this module.
+	SdkVersion() string
+
+	// ExtraVariants returns the list of extra variants this module requires.
+	ExtraVariants() []string
+
+	// AppendExtraVariant returns an extra variant to the list of extra variants this module requires.
+	AppendExtraVariant(extraVariant string)
+
+	// SetRamdiskVariantNeeded sets whether the Ramdisk Variant is needed.
+	SetRamdiskVariantNeeded(b bool)
+
+	// SetVendorRamdiskVariantNeeded sets whether the Vendor Ramdisk Variant is needed.
+	SetVendorRamdiskVariantNeeded(b bool)
+
+	// SetRecoveryVariantNeeded sets whether the Recovery Variant is needed.
+	SetRecoveryVariantNeeded(b bool)
+
+	// SetCoreVariantNeeded sets whether the Core Variant is needed.
+	SetCoreVariantNeeded(b bool)
+}
+
+var _ ImageMutatableModule = (*Module)(nil)
+
 func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	// Validation check
+	m.CheckVndkProperties(mctx)
+	MutateImage(mctx, m)
+}
+
+// CheckVndkProperties checks whether the VNDK-related properties are set correctly.
+// If properties are not set correctly, results in a module context property error.
+func (m *Module) CheckVndkProperties(mctx android.BaseModuleContext) {
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 	productSpecific := mctx.ProductSpecific()
 
-	if Bool(m.VendorProperties.Vendor_available) {
-		if vendorSpecific {
-			mctx.PropertyErrorf("vendor_available",
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
-		}
-		if Bool(m.VendorProperties.Odm_available) {
-			mctx.PropertyErrorf("vendor_available",
-				"doesn't make sense at the same time as `odm_available: true`")
-		}
-	}
-
-	if Bool(m.VendorProperties.Odm_available) {
-		if vendorSpecific {
-			mctx.PropertyErrorf("odm_available",
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
-		}
-	}
-
-	if Bool(m.VendorProperties.Product_available) {
-		if productSpecific {
-			mctx.PropertyErrorf("product_available",
-				"doesn't make sense at the same time as `product_specific: true`")
-		}
-		if vendorSpecific {
-			mctx.PropertyErrorf("product_available",
-				"cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
-		}
-	}
-
 	if vndkdep := m.vndkdep; vndkdep != nil {
 		if vndkdep.isVndk() {
 			if vendorSpecific || productSpecific {
@@ -265,6 +310,111 @@
 			}
 		}
 	}
+}
+
+func (m *Module) VendorAvailable() bool {
+	return Bool(m.VendorProperties.Vendor_available)
+}
+
+func (m *Module) OdmAvailable() bool {
+	return Bool(m.VendorProperties.Odm_available)
+}
+
+func (m *Module) ProductAvailable() bool {
+	return Bool(m.VendorProperties.Product_available)
+}
+
+func (m *Module) RamdiskAvailable() bool {
+	return Bool(m.Properties.Ramdisk_available)
+}
+
+func (m *Module) VendorRamdiskAvailable() bool {
+	return Bool(m.Properties.Vendor_ramdisk_available)
+}
+
+func (m *Module) AndroidModuleBase() *android.ModuleBase {
+	return &m.ModuleBase
+}
+
+func (m *Module) RecoveryAvailable() bool {
+	return Bool(m.Properties.Recovery_available)
+}
+
+func (m *Module) ExtraVariants() []string {
+	return m.Properties.ExtraVariants
+}
+
+func (m *Module) AppendExtraVariant(extraVariant string) {
+	m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant)
+}
+
+func (m *Module) SetRamdiskVariantNeeded(b bool) {
+	m.Properties.RamdiskVariantNeeded = b
+}
+
+func (m *Module) SetVendorRamdiskVariantNeeded(b bool) {
+	m.Properties.VendorRamdiskVariantNeeded = b
+}
+
+func (m *Module) SetRecoveryVariantNeeded(b bool) {
+	m.Properties.RecoveryVariantNeeded = b
+}
+
+func (m *Module) SetCoreVariantNeeded(b bool) {
+	m.Properties.CoreVariantNeeded = b
+}
+
+func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+	if snapshot, ok := m.linker.(snapshotInterface); ok {
+		return snapshot.version()
+	} else {
+		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+		// Should we be panicking here instead?
+		return ""
+	}
+}
+
+func (m *Module) KernelHeadersDecorator() bool {
+	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+		return true
+	}
+	return false
+}
+
+// MutateImage handles common image mutations for ImageMutatableModule interfaces.
+func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) {
+	// Validation check
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+	productSpecific := mctx.ProductSpecific()
+
+	if m.VendorAvailable() {
+		if vendorSpecific {
+			mctx.PropertyErrorf("vendor_available",
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+		}
+		if m.OdmAvailable() {
+			mctx.PropertyErrorf("vendor_available",
+				"doesn't make sense at the same time as `odm_available: true`")
+		}
+	}
+
+	if m.OdmAvailable() {
+		if vendorSpecific {
+			mctx.PropertyErrorf("odm_available",
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+		}
+	}
+
+	if m.ProductAvailable() {
+		if productSpecific {
+			mctx.PropertyErrorf("product_available",
+				"doesn't make sense at the same time as `product_specific: true`")
+		}
+		if vendorSpecific {
+			mctx.PropertyErrorf("product_available",
+				"cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
+		}
+	}
 
 	var coreVariantNeeded bool = false
 	var ramdiskVariantNeeded bool = false
@@ -287,18 +437,13 @@
 		productVndkVersion = platformVndkVersion
 	}
 
-	_, isLLNDKLibrary := m.linker.(*llndkStubDecorator)
-	_, isLLNDKHeaders := m.linker.(*llndkHeadersDecorator)
-	lib := moduleLibraryInterface(m)
-	hasLLNDKStubs := lib != nil && lib.hasLLNDKStubs()
-
-	if isLLNDKLibrary || isLLNDKHeaders || hasLLNDKStubs {
+	if m.IsLlndkLibrary() || m.IsLlndkHeaders() || m.HasLlndkStubs() {
 		// This is an LLNDK library.  The implementation of the library will be on /system,
 		// and vendor and product variants will be created with LLNDK stubs.
 		// The LLNDK libraries need vendor variants even if there is no VNDK.
 		// The obsolete llndk_library and llndk_headers modules also need the vendor variants
 		// so the cc_library LLNDK stubs can depend on them.
-		if hasLLNDKStubs {
+		if m.HasLlndkStubs() {
 			coreVariantNeeded = true
 		}
 		if platformVndkVersion != "" {
@@ -315,17 +460,13 @@
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
 		coreVariantNeeded = true
-	} else if m.isSnapshotPrebuilt() {
+	} else if m.IsSnapshotPrebuilt() {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		if snapshot, ok := m.linker.(snapshotInterface); ok {
-			if m.InstallInRecovery() {
-				recoveryVariantNeeded = true
-			} else {
-				vendorVariants = append(vendorVariants, snapshot.version())
-			}
+		if m.InstallInRecovery() {
+			recoveryVariantNeeded = true
 		} else {
-			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+			vendorVariants = append(vendorVariants, m.SnapshotVersion(mctx))
 		}
 	} else if m.HasNonSystemVariants() && !m.IsVndkExt() {
 		// This will be available to /system unless it is product_specific
@@ -351,7 +492,7 @@
 				productVariants = append(productVariants, productVndkVersion)
 			}
 		}
-	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
+	} else if vendorSpecific && m.SdkVersion() == "" {
 		// This will be available in /vendor (or /odm) only
 
 		// kernel_headers is a special module type whose exported headers
@@ -360,7 +501,7 @@
 		// For other modules, we assume that modules under proprietary
 		// paths are compatible for BOARD_VNDK_VERSION. The other modules
 		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
-		if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+		if m.KernelHeadersDecorator() {
 			vendorVariants = append(vendorVariants,
 				platformVndkVersion,
 				boardVndkVersion,
@@ -378,7 +519,7 @@
 	}
 
 	if boardVndkVersion != "" && productVndkVersion != "" {
-		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
+		if coreVariantNeeded && productSpecific && m.SdkVersion() == "" {
 			// The module has "product_specific: true" that does not create core variant.
 			coreVariantNeeded = false
 			productVariants = append(productVariants, productVndkVersion)
@@ -390,60 +531,60 @@
 		productVariants = []string{}
 	}
 
-	if Bool(m.Properties.Ramdisk_available) {
+	if m.RamdiskAvailable() {
 		ramdiskVariantNeeded = true
 	}
 
-	if m.ModuleBase.InstallInRamdisk() {
+	if m.AndroidModuleBase().InstallInRamdisk() {
 		ramdiskVariantNeeded = true
 		coreVariantNeeded = false
 	}
 
-	if Bool(m.Properties.Vendor_ramdisk_available) {
+	if m.VendorRamdiskAvailable() {
 		vendorRamdiskVariantNeeded = true
 	}
 
-	if m.ModuleBase.InstallInVendorRamdisk() {
+	if m.AndroidModuleBase().InstallInVendorRamdisk() {
 		vendorRamdiskVariantNeeded = true
 		coreVariantNeeded = false
 	}
 
-	if Bool(m.Properties.Recovery_available) {
+	if m.RecoveryAvailable() {
 		recoveryVariantNeeded = true
 	}
 
-	if m.ModuleBase.InstallInRecovery() {
+	if m.AndroidModuleBase().InstallInRecovery() {
 		recoveryVariantNeeded = true
 		coreVariantNeeded = false
 	}
 
 	// If using a snapshot, the recovery variant under AOSP directories is not needed,
 	// except for kernel headers, which needs all variants.
-	if _, ok := m.linker.(*kernelHeadersDecorator); !ok &&
-		!m.isSnapshotPrebuilt() &&
+	if m.KernelHeadersDecorator() &&
+		!m.IsSnapshotPrebuilt() &&
 		usingRecoverySnapshot &&
 		!isRecoveryProprietaryModule(mctx) {
 		recoveryVariantNeeded = false
 	}
 
 	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
+		m.AppendExtraVariant(VendorVariationPrefix + variant)
 	}
 
 	for _, variant := range android.FirstUniqueStrings(productVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
+		m.AppendExtraVariant(ProductVariationPrefix + variant)
 	}
 
-	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
-	m.Properties.VendorRamdiskVariantNeeded = vendorRamdiskVariantNeeded
-	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
-	m.Properties.CoreVariantNeeded = coreVariantNeeded
+	m.SetRamdiskVariantNeeded(ramdiskVariantNeeded)
+	m.SetVendorRamdiskVariantNeeded(vendorRamdiskVariantNeeded)
+	m.SetRecoveryVariantNeeded(recoveryVariantNeeded)
+	m.SetCoreVariantNeeded(coreVariantNeeded)
 
 	// Disable the module if no variants are needed.
 	if !ramdiskVariantNeeded &&
 		!recoveryVariantNeeded &&
 		!coreVariantNeeded &&
-		len(m.Properties.ExtraVariants) == 0 {
+		len(m.ExtraVariants()) == 0 {
 		m.Disable()
 	}
 }
@@ -460,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 738b45f..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.
@@ -483,10 +484,16 @@
 
 func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	outputPaths, objPaths, ok := bazelCtx.GetOutputFilesAndCcObjectFiles(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	if err != nil {
+		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+		return false
+	}
 	if !ok {
 		return ok
 	}
+	outputPaths := ccInfo.OutputFiles
+	objPaths := ccInfo.CcObjectFiles
 	if len(outputPaths) > 1 {
 		// TODO(cparsons): This is actually expected behavior for static libraries with no srcs.
 		// We should support this.
diff --git a/cc/linkable.go b/cc/linkable.go
index 6aa238b..571a3bb 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -98,10 +98,24 @@
 	InVendor() bool
 
 	UseSdk() bool
+
+	// IsLlndk returns true for both LLNDK (public) and LLNDK-private libs.
+	IsLlndk() bool
+
+	// IsLlndkPublic returns true only for LLNDK (public) libs.
+	IsLlndkPublic() bool
+
+	// IsLlndkHeaders returns true if this module is an LLNDK headers module.
+	IsLlndkHeaders() bool
+
+	// IsLlndkLibrary returns true if this module is an LLNDK library module.
+	IsLlndkLibrary() bool
+
+	// HasLlndkStubs returns true if this module has LLNDK stubs.
+	HasLlndkStubs() bool
+
 	UseVndk() bool
 	MustUseVendorVariant() bool
-	IsLlndk() bool
-	IsLlndkPublic() bool
 	IsVndk() bool
 	IsVndkExt() bool
 	IsVndkPrivate() bool
@@ -110,6 +124,9 @@
 	HasNonSystemVariants() bool
 	InProduct() bool
 
+	// SubName returns the modules SubName, used for image and NDK/SDK variations.
+	SubName() string
+
 	SdkVersion() string
 	MinSdkVersion() string
 	AlwaysSdk() bool
@@ -121,6 +138,10 @@
 	SetPreventInstall()
 	// SetHideFromMake sets the HideFromMake property to 'true' for this module.
 	SetHideFromMake()
+
+	// KernelHeadersDecorator returns true if this is a kernel headers decorator module.
+	// This is specific to cc and should always return false for all other packages.
+	KernelHeadersDecorator() bool
 }
 
 var (
@@ -152,6 +173,15 @@
 	}
 }
 
+// DepTagMakeSuffix returns the makeSuffix value of a particular library dependency tag.
+// Returns an empty string if not a library dependency tag.
+func DepTagMakeSuffix(depTag blueprint.DependencyTag) string {
+	if libDepTag, ok := depTag.(libraryDependencyTag); ok {
+		return libDepTag.makeSuffix
+	}
+	return ""
+}
+
 // SharedDepTag returns the dependency tag for any C++ shared libraries.
 func SharedDepTag() blueprint.DependencyTag {
 	return libraryDependencyTag{Kind: sharedLibraryDependency}
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 10de889..95d8477 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -20,6 +20,7 @@
 	"sync"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 )
@@ -78,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 {
@@ -147,8 +143,8 @@
 		return false
 	}
 
-	this.unversionedUntil, err = nativeApiLevelFromUserWithDefault(ctx,
-		String(this.properties.Unversioned_until), "minimum")
+	str := proptools.StringDefault(this.properties.Unversioned_until, "minimum")
+	this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str)
 	if err != nil {
 		ctx.PropertyErrorf("unversioned_until", err.Error())
 		return false
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/object_test.go b/cc/object_test.go
index 6ff8a00..f82d544 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"android/soong/android"
 	"testing"
 )
 
@@ -27,5 +28,28 @@
 			linker_script: "foo.lds",
 		}`)
 	})
+}
 
+func TestCcObjectWithBazel(t *testing.T) {
+	bp := `
+cc_object {
+	name: "foo",
+	srcs: ["baz.o"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToOutputFiles: map[string][]string{
+			"//foo/bar:bar": []string{"bazel_out.o"}}}
+	ctx := testCcWithConfig(t, config)
+
+	module := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon").Module()
+	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/bazel_out.o"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 3829b1e..c19b1ff 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -329,10 +329,14 @@
 
 func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	staticLibs, ok := bazelCtx.GetPrebuiltCcStaticLibraryFiles(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	if err != nil {
+		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+	}
 	if !ok {
 		return false
 	}
+	staticLibs := ccInfo.CcStaticLibraryFiles
 	if len(staticLibs) > 1 {
 		ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
 		return false
diff --git a/cc/sabi.go b/cc/sabi.go
index 4a1ba3c..c0eb57c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -141,7 +141,7 @@
 	}
 
 	// Don't create ABI dump for prebuilts.
-	if m.Prebuilt() != nil || m.isSnapshotPrebuilt() {
+	if m.Prebuilt() != nil || m.IsSnapshotPrebuilt() {
 		return false
 	}
 
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index af05102..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
 }
@@ -559,10 +563,18 @@
 		return nil
 	}
 
+	// Flags specified directly to this module.
 	p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
 	p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
 	p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
 
+	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
+	p.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
+	p.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
+	p.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
+	p.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
+	p.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
+
 	in := android.PathForModuleSrc(ctx, *p.properties.Src)
 	p.unstrippedOutputFile = in
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 3437d77..2f68cca 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -173,7 +173,7 @@
 		return false
 	}
 	// the module must be installed in target image
-	if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
+	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.inImage(m)() {
 		return false
 	}
 	// skip kernel_headers which always depend on vendor
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 1c3f1b4..66396f7 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -424,6 +424,20 @@
 		srcs: ["client.cpp"],
 	}
 
+	cc_library_shared {
+		name: "libclient_cfi",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		static_libs: ["libvendor"],
+		sanitize: {
+			cfi: true,
+		},
+		srcs: ["client.cpp"],
+	}
+
 	cc_binary {
 		name: "bin_without_snapshot",
 		vendor: true,
@@ -492,13 +506,13 @@
 		arch: {
 			arm64: {
 				src: "libvndk.a",
-				export_include_dirs: ["include/libvndk"],
 			},
 			arm: {
 				src: "libvndk.a",
-				export_include_dirs: ["include/libvndk"],
 			},
 		},
+		shared_libs: ["libvndk"],
+		export_shared_lib_headers: ["libvndk"],
 	}
 
 	vendor_snapshot_shared {
@@ -584,10 +598,18 @@
 		vendor: true,
 		arch: {
 			arm64: {
+				cfi: {
+					src: "libvendor.cfi.a",
+					export_include_dirs: ["include/libvendor_cfi"],
+				},
 				src: "libvendor.a",
 				export_include_dirs: ["include/libvendor"],
 			},
 			arm: {
+				cfi: {
+					src: "libvendor.cfi.a",
+					export_include_dirs: ["include/libvendor_cfi"],
+				},
 				src: "libvendor.a",
 				export_include_dirs: ["include/libvendor"],
 			},
@@ -726,29 +748,31 @@
 	depsBp := GatherRequiredDepsForTest(android.Android)
 
 	mockFS := map[string][]byte{
-		"deps/Android.bp":              []byte(depsBp),
-		"framework/Android.bp":         []byte(frameworkBp),
-		"framework/symbol.txt":         nil,
-		"vendor/Android.bp":            []byte(vendorProprietaryBp),
-		"vendor/bin":                   nil,
-		"vendor/bin32":                 nil,
-		"vendor/bin.cpp":               nil,
-		"vendor/client.cpp":            nil,
-		"vendor/include/libvndk/a.h":   nil,
-		"vendor/include/libvendor/b.h": nil,
-		"vendor/libc++_static.a":       nil,
-		"vendor/libc++demangle.a":      nil,
-		"vendor/libgcc_striped.a":      nil,
-		"vendor/libvndk.a":             nil,
-		"vendor/libvendor.a":           nil,
-		"vendor/libvendor.so":          nil,
-		"vendor/lib32.a":               nil,
-		"vendor/lib32.so":              nil,
-		"vendor/lib64.a":               nil,
-		"vendor/lib64.so":              nil,
-		"vndk/Android.bp":              []byte(vndkBp),
-		"vndk/include/libvndk/a.h":     nil,
-		"vndk/libvndk.so":              nil,
+		"deps/Android.bp":                  []byte(depsBp),
+		"framework/Android.bp":             []byte(frameworkBp),
+		"framework/symbol.txt":             nil,
+		"vendor/Android.bp":                []byte(vendorProprietaryBp),
+		"vendor/bin":                       nil,
+		"vendor/bin32":                     nil,
+		"vendor/bin.cpp":                   nil,
+		"vendor/client.cpp":                nil,
+		"vendor/include/libvndk/a.h":       nil,
+		"vendor/include/libvendor/b.h":     nil,
+		"vendor/include/libvendor_cfi/c.h": nil,
+		"vendor/libc++_static.a":           nil,
+		"vendor/libc++demangle.a":          nil,
+		"vendor/libgcc_striped.a":          nil,
+		"vendor/libvndk.a":                 nil,
+		"vendor/libvendor.a":               nil,
+		"vendor/libvendor.cfi.a":           nil,
+		"vendor/libvendor.so":              nil,
+		"vendor/lib32.a":                   nil,
+		"vendor/lib32.so":                  nil,
+		"vendor/lib64.a":                   nil,
+		"vendor/lib64.so":                  nil,
+		"vndk/Android.bp":                  []byte(vndkBp),
+		"vndk/include/libvndk/a.h":         nil,
+		"vndk/libvndk.so":                  nil,
 	}
 
 	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
@@ -766,6 +790,9 @@
 	staticVariant := "android_vendor.30_arm64_armv8-a_static"
 	binaryVariant := "android_vendor.30_arm64_armv8-a"
 
+	sharedCfiVariant := "android_vendor.30_arm64_armv8-a_shared_cfi"
+	staticCfiVariant := "android_vendor.30_arm64_armv8-a_static_cfi"
+
 	shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared"
 	binary32Variant := "android_vendor.30_arm_armv7-a-neon"
 
@@ -808,9 +835,22 @@
 		t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
 	}
 
-	// bin_without_snapshot uses libvndk.vendor_static.30.arm64
+	// libclient_cfi uses libvendor.vendor_static.30.arm64's cfi variant
+	libclientCfiCcFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("cc").Args["cFlags"]
+	if !strings.Contains(libclientCfiCcFlags, "-Ivendor/include/libvendor_cfi") {
+		t.Errorf("flags for libclient_cfi must contain %#v, but was %#v.",
+			"-Ivendor/include/libvendor_cfi", libclientCfiCcFlags)
+	}
+
+	libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"]
+	libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.30.arm64"})
+	if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) {
+		t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags)
+	}
+
+	// bin_without_snapshot uses libvndk.vendor_static.30.arm64 (which reexports vndk's exported headers)
 	binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
-	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
+	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivndk/include/libvndk") {
 		t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
 			"-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
 	}
diff --git a/cc/vndk.go b/cc/vndk.go
index b7047e9..1a8a454 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -609,8 +609,8 @@
 	}
 	// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
 	// !installable: Snapshot only cares about "installable" modules.
-	// isSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
-	if !m.InVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
+	// IsSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
+	if !m.InVendor() || !m.installable(apexInfo) || m.IsSnapshotPrebuilt() {
 		return nil, "", false
 	}
 	l, ok := m.linker.(snapshotLibraryInterface)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 1863ece..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 {
@@ -75,8 +89,8 @@
 	return ctx
 }
 
-func newConfig(srcDir string) android.Config {
-	configuration, err := android.NewConfig(srcDir, bootstrap.CmdlineBuildDir(), bootstrap.CmdlineModuleListFile())
+func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
+	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)
@@ -127,7 +152,7 @@
 }
 
 func writeMetrics(configuration android.Config) {
-	metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb")
+	metricsFile := filepath.Join(configuration.BuildDir(), "soong_build_metrics.pb")
 	err := android.WriteMetrics(configuration, metricsFile)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
@@ -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() {
@@ -188,25 +251,21 @@
 
 	shared.ReexecWithDelveMaybe(delveListen, delvePath)
 	android.InitSandbox(topDir)
-	android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
 
-	usedVariablesFile := shared.JoinPath(outDir, "soong.environment.used")
+	availableEnv := parseAvailableEnv()
+
 	// The top-level Blueprints file is passed as the first argument.
 	srcDir := filepath.Dir(flag.Arg(0))
-	configuration := newConfig(srcDir)
+	configuration := newConfig(srcDir, outDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
-		shared.JoinPath(outDir, "soong.environment.used"),
+		usedEnvFile,
 	}
 
 	if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
 		configuration.SetAllowMissingDependencies()
 	}
 
-	// These two are here so that we restart a non-debugged soong_build when the
-	// user sets SOONG_DELVE the first time.
-	configuration.Getenv("SOONG_DELVE")
-	configuration.Getenv("SOONG_DELVE_PATH")
 	if shared.IsDebugging() {
 		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
 		// enabled even if it completed successfully.
@@ -218,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)
-	writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
+	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
 
-func writeUsedVariablesFile(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 variables 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 variables 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
@@ -274,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.
@@ -281,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)
@@ -299,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.
@@ -315,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/bootimg.go b/filesystem/bootimg.go
index 3dcc416..29a8a39 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -61,7 +61,7 @@
 	Vendor_boot *bool
 
 	// Optional kernel commandline
-	Cmdline *string
+	Cmdline *string `android:"arch_variant"`
 
 	// File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
 	// and `header_version` is greater than or equal to 4.
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/filesystem/vbmeta.go b/filesystem/vbmeta.go
index f823387..3f16c0d 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -110,6 +110,9 @@
 	return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName())
 }
 
+// See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE
+const vbmetaMaxSize = 64 * 1024
+
 func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	extractedPublicKeys := v.extractPublicKeys(ctx)
 
@@ -172,6 +175,13 @@
 	}
 
 	cmd.FlagWithOutput("--output ", v.output)
+
+	// libavb expects to be able to read the maximum vbmeta size, so we must provide a partition
+	// which matches this or the read will fail.
+	builder.Command().Text("truncate").
+		FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)).
+		Output(v.output)
+
 	builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName()))
 
 	v.installDir = android.PathForModuleInstall(ctx, "etc")
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/Android.bp b/java/Android.bp
index 8334b85..2a4b596 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -32,6 +32,7 @@
         "boot_image.go",
         "boot_jars.go",
         "builder.go",
+        "classpath_fragment.go",
         "device_host_converter.go",
         "dex.go",
         "dexpreopt.go",
@@ -42,6 +43,7 @@
         "gen.go",
         "genrule.go",
         "hiddenapi.go",
+        "hiddenapi_modular.go",
         "hiddenapi_singleton.go",
         "jacoco.go",
         "java.go",
diff --git a/java/aar.go b/java/aar.go
index a122a94..04727e4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -218,7 +218,7 @@
 	linkDeps = append(linkDeps, assetDeps...)
 
 	// SDK version flags
-	minSdkVersion, err := sdkContext.MinSdkVersion().EffectiveVersionString(ctx)
+	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 	}
@@ -609,6 +609,9 @@
 	hideApexVariantFromMake bool
 
 	aarPath android.Path
+
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
 }
 
 var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -625,23 +628,23 @@
 	}
 }
 
-func (a *AARImport) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(a.properties.Sdk_version))
+func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
 }
 
 func (a *AARImport) SystemModules() string {
 	return ""
 }
 
-func (a *AARImport) MinSdkVersion() android.SdkSpec {
+func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if a.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*a.properties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *a.properties.Min_sdk_version)
 	}
-	return a.SdkVersion()
+	return a.SdkVersion(ctx)
 }
 
-func (a *AARImport) TargetSdkVersion() android.SdkSpec {
-	return a.SdkVersion()
+func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return a.SdkVersion(ctx)
 }
 
 func (a *AARImport) javaVersion() string {
@@ -727,6 +730,9 @@
 		return
 	}
 
+	a.sdkVersion = a.SdkVersion(ctx)
+	a.minSdkVersion = a.MinSdkVersion(ctx)
+
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
 
 	aarName := ctx.ModuleName() + ".aar"
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 8510f30..331f941 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -51,7 +51,7 @@
 	if isLibrary {
 		args = append(args, "--library")
 	} else {
-		minSdkVersion, err := sdkContext.MinSdkVersion().EffectiveVersion(ctx)
+		minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
@@ -87,7 +87,7 @@
 		args = append(args, "--logging-parent", loggingParent)
 	}
 	var deps android.Paths
-	targetSdkVersion, err := sdkContext.TargetSdkVersion().EffectiveVersionString(ctx)
+	targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
 	}
@@ -96,7 +96,7 @@
 		deps = append(deps, ApiFingerprintPath(ctx))
 	}
 
-	minSdkVersion, err := sdkContext.MinSdkVersion().EffectiveVersionString(ctx)
+	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 	}
diff --git a/java/androidmk.go b/java/androidmk.go
index 75661a7..4e594a2 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -106,7 +106,7 @@
 					if len(library.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
 					}
-					entries.SetString("LOCAL_SDK_VERSION", library.SdkVersion().Raw)
+					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion.String())
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
 
@@ -205,7 +205,7 @@
 				}
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
@@ -255,7 +255,7 @@
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
 				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.SdkVersion().Raw)
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
 			},
 		},
 	}}
diff --git a/java/app.go b/java/app.go
index 04406e7..5695022 100755
--- a/java/app.go
+++ b/java/app.go
@@ -213,7 +213,7 @@
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 
-	if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion().Specified() {
+	if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion(ctx).Specified() {
 		ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
 	}
 
@@ -222,7 +222,7 @@
 		a.aapt.deps(ctx, sdkDep)
 	}
 
-	usesSDK := a.SdkVersion().Specified() && a.SdkVersion().Kind != android.SdkCorePlatform
+	usesSDK := a.SdkVersion(ctx).Specified() && a.SdkVersion(ctx).Kind != android.SdkCorePlatform
 
 	if usesSDK && Bool(a.appProperties.Jni_uses_sdk_apis) {
 		ctx.PropertyErrorf("jni_uses_sdk_apis",
@@ -279,14 +279,14 @@
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
 	if a.Updatable() {
-		if !a.SdkVersion().Stable() {
-			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion())
+		if !a.SdkVersion(ctx).Stable() {
+			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx))
 		}
 		if String(a.deviceProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
 
-		if minSdkVersion, err := a.MinSdkVersion().EffectiveVersion(ctx); err == nil {
+		if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil {
 			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
 			android.CheckMinSdkVersion(a, ctx, minSdkVersion)
 		} else {
@@ -314,7 +314,7 @@
 		// The domain of cc.sdk_version is "current" and <number>
 		// We can rely on android.SdkSpec to convert it to <number> so that "current" is
 		// handled properly regardless of sdk finalization.
-		jniSdkVersion, err := android.SdkSpecFrom(dep.SdkVersion()).EffectiveVersion(ctx)
+		jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.SdkVersion()).EffectiveVersion(ctx)
 		if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
 			ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
 				dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
@@ -327,9 +327,9 @@
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
-	minSdkVersion, err := a.MinSdkVersion().EffectiveVersion(ctx)
+	minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx)
 	if err != nil {
-		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(), err)
+		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(ctx), err)
 	}
 
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
@@ -381,7 +381,7 @@
 
 func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
 	usePlatformAPI := proptools.Bool(a.Module.deviceProperties.Platform_apis)
-	if ctx.Module().(android.SdkContext).SdkVersion().Kind == android.SdkModule {
+	if ctx.Module().(android.SdkContext).SdkVersion(ctx).Kind == android.SdkModule {
 		usePlatformAPI = true
 	}
 	a.aapt.usesNonSdkApis = usePlatformAPI
@@ -724,8 +724,8 @@
 }
 
 type appDepsInterface interface {
-	SdkVersion() android.SdkSpec
-	MinSdkVersion() android.SdkSpec
+	SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+	MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
 	RequiresStableAPIs(ctx android.BaseModuleContext) bool
 }
 
@@ -738,8 +738,8 @@
 	seenModulePaths := make(map[string]bool)
 
 	if checkNativeSdkVersion {
-		checkNativeSdkVersion = app.SdkVersion().Specified() &&
-			app.SdkVersion().Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
+		checkNativeSdkVersion = app.SdkVersion(ctx).Specified() &&
+			app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
 	}
 
 	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
@@ -829,12 +829,16 @@
 			depsInfo[depName] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface{ MinSdkVersion() string }); ok {
-				if v := m.MinSdkVersion(); v != "" {
-					toMinSdkVersion = v
+			if m, ok := to.(interface {
+				MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+			}); ok {
+				if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
+					toMinSdkVersion = v.ApiLevel.String()
 				}
-			} else if m, ok := to.(interface{ MinSdkVersionString() string }); ok {
-				if v := m.MinSdkVersionString(); v != "" {
+			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
+				// string
+				if v := m.MinSdkVersion(); v != "" {
 					toMinSdkVersion = v
 				}
 			}
@@ -848,7 +852,7 @@
 		return true
 	})
 
-	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersionString(), depsInfo)
+	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depsInfo)
 }
 
 func (a *AndroidApp) Updatable() bool {
diff --git a/java/app_import.go b/java/app_import.go
index 32cec23..839051e 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -394,12 +394,12 @@
 	return false
 }
 
-func (a *AndroidAppImport) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom("")
+func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecPrivate
 }
 
-func (a *AndroidAppImport) MinSdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom("")
+func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecPrivate
 }
 
 var _ android.ApexModule = (*AndroidAppImport)(nil)
diff --git a/java/base.go b/java/base.go
index 9bc0738..19c85cd 100644
--- a/java/base.go
+++ b/java/base.go
@@ -370,10 +370,13 @@
 	modulePaths []string
 
 	hideApexVariantFromMake bool
+
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
 }
 
-func (j *Module) CheckStableSdkVersion() error {
-	sdkVersion := j.SdkVersion()
+func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
+	sdkVersion := j.SdkVersion(ctx)
 	if sdkVersion.Stable() {
 		return nil
 	}
@@ -393,7 +396,7 @@
 func (j *Module) checkSdkVersions(ctx android.ModuleContext) {
 	if j.RequiresStableAPIs(ctx) {
 		if sc, ok := ctx.Module().(android.SdkContext); ok {
-			if !sc.SdkVersion().Specified() {
+			if !sc.SdkVersion(ctx).Specified() {
 				ctx.PropertyErrorf("sdk_version",
 					"sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
 			}
@@ -418,7 +421,7 @@
 func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
 	if sc, ok := ctx.Module().(android.SdkContext); ok {
 		usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
-		sdkVersionSpecified := sc.SdkVersion().Specified()
+		sdkVersionSpecified := sc.SdkVersion(ctx).Specified()
 		if usePlatformAPI && sdkVersionSpecified {
 			ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
 		} else if !usePlatformAPI && !sdkVersionSpecified {
@@ -512,30 +515,30 @@
 	return false
 }
 
-func (j *Module) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(j.deviceProperties.Sdk_version))
+func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version))
 }
 
 func (j *Module) SystemModules() string {
 	return proptools.String(j.deviceProperties.System_modules)
 }
 
-func (j *Module) MinSdkVersion() android.SdkSpec {
+func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if j.deviceProperties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*j.deviceProperties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *j.deviceProperties.Min_sdk_version)
 	}
-	return j.SdkVersion()
-}
-
-func (j *Module) TargetSdkVersion() android.SdkSpec {
-	if j.deviceProperties.Target_sdk_version != nil {
-		return android.SdkSpecFrom(*j.deviceProperties.Target_sdk_version)
-	}
-	return j.SdkVersion()
+	return j.SdkVersion(ctx)
 }
 
 func (j *Module) MinSdkVersionString() string {
-	return j.MinSdkVersion().ApiLevel.String()
+	return j.minSdkVersion.Raw
+}
+
+func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	if j.deviceProperties.Target_sdk_version != nil {
+		return android.SdkSpecFrom(ctx, *j.deviceProperties.Target_sdk_version)
+	}
+	return j.SdkVersion(ctx)
 }
 
 func (j *Module) AvailableFor(what string) bool {
@@ -1209,7 +1212,7 @@
 			}
 			// Dex compilation
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(), outputFile, jarName)
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
 			if ctx.Failed() {
 				return
 			}
@@ -1267,9 +1270,9 @@
 		j.linter.srcJars = srcJars
 		j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
 		j.linter.classes = j.implementationJarFile
-		j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion())
-		j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion())
-		j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion())
+		j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx))
+		j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx))
+		j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx))
 		j.linter.javaLanguageLevel = flags.javaVersion.String()
 		j.linter.kotlinLanguageLevel = "1.3"
 		if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() {
@@ -1471,7 +1474,7 @@
 // Implements android.ApexModule
 func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
-	sdkSpec := j.MinSdkVersion()
+	sdkSpec := j.MinSdkVersion(ctx)
 	if !sdkSpec.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
 	}
@@ -1551,10 +1554,10 @@
 
 type moduleWithSdkDep interface {
 	android.Module
-	getSdkLinkType(name string) (ret sdkLinkType, stubs bool)
+	getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool)
 }
 
-func (m *Module) getSdkLinkType(name string) (ret sdkLinkType, stubs bool) {
+func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
 	switch name {
 	case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
 		"stub-annotations", "private-stub-annotations-jar",
@@ -1576,7 +1579,7 @@
 		return linkType, true
 	}
 
-	ver := m.SdkVersion()
+	ver := m.SdkVersion(ctx)
 	switch ver.Kind {
 	case android.SdkCore:
 		return javaCore, false
@@ -1606,11 +1609,11 @@
 		return
 	}
 
-	myLinkType, stubs := j.getSdkLinkType(ctx.ModuleName())
+	myLinkType, stubs := j.getSdkLinkType(ctx, ctx.ModuleName())
 	if stubs {
 		return
 	}
-	depLinkType, _ := dep.getSdkLinkType(ctx.OtherModuleName(dep))
+	depLinkType, _ := dep.getSdkLinkType(ctx, ctx.OtherModuleName(dep))
 
 	if myLinkType.rank() < depLinkType.rank() {
 		ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+
@@ -1638,7 +1641,7 @@
 		}
 	}
 
-	sdkLinkType, _ := j.getSdkLinkType(ctx.ModuleName())
+	sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
@@ -1656,7 +1659,7 @@
 		if dep, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
 			case libTag:
-				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion())...)
+				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
new file mode 100644
index 0000000..d497460
--- /dev/null
+++ b/java/classpath_fragment.go
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java
+
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+)
+
+// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto
+// config files based on build configuration to embed into /system and /apex on a device.
+//
+// See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables
+// on the device.
+
+type classpathType int
+
+const (
+	// Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto
+	BOOTCLASSPATH classpathType = iota
+	DEX2OATBOOTCLASSPATH
+	SYSTEMSERVERCLASSPATH
+)
+
+func (c classpathType) String() string {
+	return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c]
+}
+
+type classpathFragmentProperties struct {
+}
+
+// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
+// variables at runtime.
+type classpathFragment interface {
+	android.Module
+
+	classpathFragmentBase() *ClasspathFragmentBase
+}
+
+// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
+// such modules are expected to call initClasspathFragment().
+type ClasspathFragmentBase struct {
+	properties classpathFragmentProperties
+
+	outputFilepath android.OutputPath
+	installDirPath android.InstallPath
+}
+
+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/droiddoc.go b/java/droiddoc.go
index e527d59..01c0f16 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -261,20 +261,20 @@
 
 var _ android.OutputFileProducer = (*Javadoc)(nil)
 
-func (j *Javadoc) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(j.properties.Sdk_version))
+func (j *Javadoc) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
 }
 
 func (j *Javadoc) SystemModules() string {
 	return proptools.String(j.properties.System_modules)
 }
 
-func (j *Javadoc) MinSdkVersion() android.SdkSpec {
-	return j.SdkVersion()
+func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
 }
 
-func (j *Javadoc) TargetSdkVersion() android.SdkSpec {
-	return j.SdkVersion()
+func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
 }
 
 func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
@@ -386,7 +386,7 @@
 			}
 		case libTag:
 			if dep, ok := module.(SdkLibraryDependency); ok {
-				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion())...)
+				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
 				dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 208ced7..3ecb977 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -111,6 +111,13 @@
 
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
+// hiddenAPISupportingModule is the interface that is implemented by any module that supports
+// contributing to the hidden API processing.
+type hiddenAPISupportingModule interface {
+	android.Module
+	hiddenAPIIntf
+}
+
 // Initialize the hiddenapi structure
 func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) {
 	// If hiddenapi processing is disabled treat this as inactive.
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
new file mode 100644
index 0000000..7cf082b
--- /dev/null
+++ b/java/hiddenapi_modular.go
@@ -0,0 +1,219 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+)
+
+// Contains support for processing hiddenAPI in a modular fashion.
+
+// 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
+// for each of those signatures will be updated in a property specific way.
+//
+// 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 HiddenAPIFlagFileProperties struct {
+	// Marks each signature in the referenced files as being unsupported.
+	Unsupported []string `android:"path"`
+
+	// Marks each signature in the referenced files as being unsupported because it has been removed.
+	// Any conflicts with other flags are ignored.
+	Removed []string `android:"path"`
+
+	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= R
+	// and low priority.
+	Max_target_r_low_priority []string `android:"path"`
+
+	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= Q.
+	Max_target_q []string `android:"path"`
+
+	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= P.
+	Max_target_p []string `android:"path"`
+
+	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= O
+	// and low priority. Any conflicts with other flags are ignored.
+	Max_target_o_low_priority []string `android:"path"`
+
+	// Marks each signature in the referenced files as being blocked.
+	Blocked []string `android:"path"`
+
+	// Marks each signature in every package in the referenced files as being unsupported.
+	Unsupported_packages []string `android:"path"`
+}
+
+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
+}
+
+type hiddenAPIFlagFileCategory struct {
+	// propertyName is the name of the property for this category.
+	propertyName string
+
+	// propertyAccessor retrieves the value of the property for this category from the set of
+	// properties.
+	propertyAccessor func(properties *HiddenAPIFlagFileProperties) []string
+
+	// commandMutator adds the appropriate command line options for this category to the supplied
+	// command
+	commandMutator func(command *android.RuleBuilderCommand, path android.Path)
+}
+
+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 ")
+		},
+	},
+}
+
+// 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
+// flags from all the modules, the stub flags, augmented with some additional configuration files.
+//
+// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
+// an entry for every single member in the dex implementation jars of the individual modules. Every
+// signature in any of the other files MUST be included in this file.
+//
+// moduleSpecificFlagsPaths are the paths to the flags files generated by each module using
+// information from the baseFlagsPath as well as from annotations within the source.
+//
+// augmentationInfo is a struct containing paths to files that augment the information provided by
+// the moduleSpecificFlagsPaths.
+// ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the
+// flags from all the modules, the stub flags, augmented with some additional configuration files.
+//
+// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
+// an entry for every single member in the dex implementation jars of the individual modules. Every
+// signature in any of the other files MUST be included in this file.
+//
+// moduleSpecificFlagsPaths are the paths to the flags files generated by each module using
+// information from the baseFlagsPath as well as from annotations within the source.
+//
+// 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 hiddenAPIFlagFileInfo) {
+	tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
+	rule := android.NewRuleBuilder(pctx, ctx)
+	command := rule.Command().
+		BuiltTool("generate_hiddenapi_lists").
+		FlagWithInput("--csv ", baseFlagsPath).
+		Inputs(moduleSpecificFlagsPaths).
+		FlagWithOutput("--output ", tempPath)
+
+	// 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)
+
+	rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
+}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 7e9477b..ed0b722 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -15,10 +15,7 @@
 package java
 
 import (
-	"fmt"
-
 	"android/soong/android"
-	"android/soong/genrule"
 )
 
 func init() {
@@ -27,8 +24,6 @@
 
 func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
 	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
-	ctx.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory)
-	ctx.RegisterModuleType("hiddenapi_flags", hiddenAPIFlagsFactory)
 }
 
 var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
@@ -102,11 +97,15 @@
 // yet been created.
 func hiddenAPISingletonPaths(ctx android.PathContext) hiddenAPISingletonPathsStruct {
 	return ctx.Config().Once(hiddenAPISingletonPathsKey, func() interface{} {
+		// Make the paths relative to the out/soong/hiddenapi directory instead of to the out/soong/
+		// directory. This ensures that if they are used as java_resources they do not end up in a
+		// hiddenapi directory in the resulting APK.
+		hiddenapiDir := android.PathForOutput(ctx, "hiddenapi")
 		return hiddenAPISingletonPathsStruct{
-			flags:     android.PathForOutput(ctx, "hiddenapi", "hiddenapi-flags.csv"),
-			index:     android.PathForOutput(ctx, "hiddenapi", "hiddenapi-index.csv"),
-			metadata:  android.PathForOutput(ctx, "hiddenapi", "hiddenapi-unsupported.csv"),
-			stubFlags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-stub-flags.txt"),
+			flags:     hiddenapiDir.Join(ctx, "hiddenapi-flags.csv"),
+			index:     hiddenapiDir.Join(ctx, "hiddenapi-index.csv"),
+			metadata:  hiddenapiDir.Join(ctx, "hiddenapi-unsupported.csv"),
+			stubFlags: hiddenapiDir.Join(ctx, "hiddenapi-stub-flags.txt"),
 		}
 	}).(hiddenAPISingletonPathsStruct)
 }
@@ -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,63 +315,21 @@
 	return outputPath
 }
 
-// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
-// the unsupported API.
-func flagsRule(ctx android.SingletonContext) android.Path {
-	var flagsCSV android.Paths
-	var combinedRemovedApis android.Path
+func prebuiltIndexRule(ctx android.SingletonContext) {
+	outputPath := hiddenAPISingletonPaths(ctx).index
+	inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
 
-	ctx.VisitAllModules(func(module android.Module) {
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if csv := h.flagsCSV(); csv != nil {
-				flagsCSV = append(flagsCSV, csv)
-			}
-		} else if g, ok := module.(*genrule.Module); ok {
-			if ctx.ModuleName(module) == "combined-removed-dex" {
-				if len(g.GeneratedSourceFiles()) != 1 || combinedRemovedApis != nil {
-					ctx.Errorf("Expected 1 combined-removed-dex module that generates 1 output file.")
-				}
-				combinedRemovedApis = g.GeneratedSourceFiles()[0]
-			}
-		}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: outputPath,
+		Input:  inputPath,
 	})
+}
 
-	if combinedRemovedApis == nil {
-		ctx.Errorf("Failed to find combined-removed-dex.")
-	}
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-
+// 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 {
 	outputPath := hiddenAPISingletonPaths(ctx).flags
-	tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
-
-	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
-
-	rule.Command().
-		BuiltTool("generate_hiddenapi_lists").
-		FlagWithInput("--csv ", stubFlags).
-		Inputs(flagsCSV).
-		FlagWithInput("--unsupported ",
-			android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-unsupported.txt")).
-		FlagWithInput("--unsupported ", combinedRemovedApis).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed").
-		FlagWithInput("--max-target-r ",
-			android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
-		FlagWithInput("--max-target-q ",
-			android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-q.txt")).
-		FlagWithInput("--max-target-p ",
-			android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-p.txt")).
-		FlagWithInput("--max-target-o ", android.PathForSource(
-			ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
-		FlagWithInput("--blocked ",
-			android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-force-blocked.txt")).
-		FlagWithInput("--unsupported ", android.PathForSource(
-			ctx, "frameworks/base/boot/hiddenapi/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
-		FlagWithOutput("--output ", tempPath)
-
-	commitChangeForRestat(rule, tempPath, outputPath)
-
-	rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
-
 	return outputPath
 }
 
@@ -396,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.
@@ -441,105 +365,3 @@
 		Text("fi").
 		Text(")")
 }
-
-type hiddenAPIFlagsProperties struct {
-	// name of the file into which the flags will be copied.
-	Filename *string
-}
-
-type hiddenAPIFlags struct {
-	android.ModuleBase
-
-	properties hiddenAPIFlagsProperties
-
-	outputFilePath android.OutputPath
-}
-
-func (h *hiddenAPIFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	filename := String(h.properties.Filename)
-
-	inputPath := hiddenAPISingletonPaths(ctx).flags
-	h.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
-
-	// This ensures that outputFilePath has the correct name for others to
-	// use, as the source file may have a different name.
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
-		Output: h.outputFilePath,
-		Input:  inputPath,
-	})
-}
-
-func (h *hiddenAPIFlags) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{h.outputFilePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-// hiddenapi-flags provides access to the hiddenapi-flags.csv file generated during the build.
-func hiddenAPIFlagsFactory() android.Module {
-	module := &hiddenAPIFlags{}
-	module.AddProperties(&module.properties)
-	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
-	return module
-}
-
-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 5c449e5..5ea9a5b 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -23,12 +23,6 @@
 	"github.com/google/blueprint/proptools"
 )
 
-func fixtureSetBootJarsProductVariable(bootJars ...string) android.FixturePreparer {
-	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
-	})
-}
-
 func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer {
 	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
@@ -41,7 +35,7 @@
 func TestHiddenAPISingleton(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -56,61 +50,6 @@
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
-func TestHiddenAPIIndexSingleton(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		hiddenApiFixtureFactory,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("bar"),
-		fixtureSetBootJarsProductVariable("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" +
@@ -119,7 +58,7 @@
 
 	android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
 		RunTestWithBp(t, `
 		java_library {
@@ -139,7 +78,7 @@
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_import {
 			name: "foo",
@@ -157,7 +96,7 @@
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -185,7 +124,7 @@
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -295,7 +234,7 @@
 
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 		fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
 	).RunTestWithBp(t, `
 		java_import {
diff --git a/java/java.go b/java/java.go
index fa7c96e..ee4f2eb 100644
--- a/java/java.go
+++ b/java/java.go
@@ -356,7 +356,7 @@
 	if javaVersion != "" {
 		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() {
-		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion())
+		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
 	} else {
 		return JAVA_VERSION_9
 	}
@@ -463,6 +463,9 @@
 	// would the <x> library if <x> was configured as a boot jar.
 	j.initHiddenAPI(ctx, j.ConfigurationName())
 
+	j.sdkVersion = j.SdkVersion(ctx)
+	j.minSdkVersion = j.MinSdkVersion(ctx)
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
@@ -1130,33 +1133,28 @@
 	exportAidlIncludeDirs android.Paths
 
 	hideApexVariantFromMake bool
+
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
 }
 
-func (j *Import) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(j.properties.Sdk_version))
-}
-
-func (j *Import) makeSdkVersion() string {
-	return j.SdkVersion().Raw
+func (j *Import) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
 }
 
 func (j *Import) SystemModules() string {
 	return "none"
 }
 
-func (j *Import) MinSdkVersion() android.SdkSpec {
+func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if j.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*j.properties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *j.properties.Min_sdk_version)
 	}
-	return j.SdkVersion()
+	return j.SdkVersion(ctx)
 }
 
-func (j *Import) TargetSdkVersion() android.SdkSpec {
-	return j.SdkVersion()
-}
-
-func (j *Import) MinSdkVersionString() string {
-	return j.MinSdkVersion().ApiLevel.String()
+func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
 }
 
 func (j *Import) Prebuilt() *android.Prebuilt {
@@ -1192,6 +1190,9 @@
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.sdkVersion = j.SdkVersion(ctx)
+	j.minSdkVersion = j.MinSdkVersion(ctx)
+
 	// Initialize the hiddenapi structure.
 	j.initHiddenAPI(ctx, j.BaseModuleName())
 
@@ -1230,7 +1231,7 @@
 		} else if dep, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
 			case libTag:
-				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion())...)
+				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			}
 		}
 
@@ -1291,7 +1292,7 @@
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(), outputFile, jarName)
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
 			if ctx.Failed() {
 				return
 			}
@@ -1359,7 +1360,7 @@
 // Implements android.ApexModule
 func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
-	sdkSpec := j.MinSdkVersion()
+	sdkSpec := j.MinSdkVersion(ctx)
 	if !sdkSpec.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
 	}
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 95d19b9..cb8ad68 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -15,6 +15,8 @@
 package java
 
 import (
+	"fmt"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 	"github.com/google/blueprint"
@@ -57,6 +59,7 @@
 
 type platformBootclasspathModule struct {
 	android.ModuleBase
+	ClasspathFragmentBase
 
 	properties platformBootclasspathProperties
 
@@ -69,6 +72,15 @@
 	//
 	// Currently only for testing.
 	fragments []android.Module
+
+	// Path to the monolithic hiddenapi-flags.csv file.
+	hiddenAPIFlagsCSV android.OutputPath
+
+	// Path to the monolithic hiddenapi-index.csv file.
+	hiddenAPIIndexCSV android.OutputPath
+
+	// Path to the monolithic hiddenapi-unsupported.csv file.
+	hiddenAPIMetadataCSV android.OutputPath
 }
 
 // ApexVariantReference specifies a particular apex variant of a module.
@@ -84,19 +96,49 @@
 }
 
 type platformBootclasspathProperties struct {
-
 	// The names of the bootclasspath_fragment modules that form part of this
 	// platform_bootclasspath.
 	Fragments []ApexVariantReference
+
+	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)
+
+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.
+func (b *platformBootclasspathModule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "hiddenapi-flags.csv":
+		return android.Paths{b.hiddenAPIFlagsCSV}, nil
+	case "hiddenapi-index.csv":
+		return android.Paths{b.hiddenAPIIndexCSV}, nil
+	case "hiddenapi-metadata.csv":
+		return android.Paths{b.hiddenAPIMetadataCSV}, nil
+	}
+
+	return nil, fmt.Errorf("unknown tag %s", tag)
+}
+
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if SkipDexpreoptBootJars(ctx) {
 		return
@@ -168,7 +210,30 @@
 	// error, unless missing dependencies are allowed. The simplest way to handle that is to add a
 	// dependency that will not be satisfied and the default behavior will handle it.
 	if !addedDep {
-		ctx.AddFarVariationDependencies(variations, tag, name)
+		// Add dependency on the unprefixed (i.e. source or renamed prebuilt) module which we know does
+		// not exist. The resulting error message will contain useful information about the available
+		// variants.
+		reportMissingVariationDependency(ctx, variations, name)
+
+		// Add dependency on the missing prefixed prebuilt variant too if a module with that name exists
+		// so that information about its available variants will be reported too.
+		if ctx.OtherModuleExists(prebuiltName) {
+			reportMissingVariationDependency(ctx, variations, prebuiltName)
+		}
+	}
+}
+
+// reportMissingVariationDependency intentionally adds a dependency on a missing variation in order
+// to generate an appropriate error message with information about the available variations.
+func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variations []blueprint.Variation, name string) {
+	modules := ctx.AddFarVariationDependencies(variations, nil, name)
+	if len(modules) != 1 {
+		panic(fmt.Errorf("Internal Error: expected one module, found %d", len(modules)))
+		return
+	}
+	if modules[0] != nil {
+		panic(fmt.Errorf("Internal Error: expected module to be missing but was found: %q", modules[0]))
+		return
 	}
 }
 
@@ -182,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 {
@@ -191,6 +258,8 @@
 		}
 	})
 
+	b.generateHiddenAPIBuildActions(ctx, b.configuredModules)
+
 	// Nothing to do if skipping the dexpreopt of boot image jars.
 	if SkipDexpreoptBootJars(ctx) {
 		return
@@ -215,3 +284,101 @@
 func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
 	return defaultBootImageConfig(ctx)
 }
+
+// generateHiddenAPIBuildActions generates all the hidden API related build rules.
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module) {
+
+	// Save the paths to the monolithic files for retrieval via OutputFiles().
+	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
+	b.hiddenAPIIndexCSV = hiddenAPISingletonPaths(ctx).index
+	b.hiddenAPIMetadataCSV = hiddenAPISingletonPaths(ctx).metadata
+
+	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
+	// optimization that can be used to reduce the incremental build time but as its name suggests it
+	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
+		for _, path := range paths {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Touch,
+				Output: path,
+			})
+		}
+		return
+	}
+
+	hiddenAPISupportingModules := []hiddenAPISupportingModule{}
+	for _, module := range modules {
+		if h, ok := module.(hiddenAPISupportingModule); ok {
+			if h.bootDexJar() == nil {
+				ctx.ModuleErrorf("module %s does not provide a bootDexJar file", module)
+			}
+			if h.flagsCSV() == nil {
+				ctx.ModuleErrorf("module %s does not provide a flagsCSV file", module)
+			}
+			if h.indexCSV() == nil {
+				ctx.ModuleErrorf("module %s does not provide an indexCSV file", module)
+			}
+			if h.metadataCSV() == nil {
+				ctx.ModuleErrorf("module %s does not provide a metadataCSV file", module)
+			}
+
+			if ctx.Failed() {
+				continue
+			}
+
+			hiddenAPISupportingModules = append(hiddenAPISupportingModules, h)
+		} else {
+			ctx.ModuleErrorf("module %s of type %s does not support hidden API processing", module, ctx.OtherModuleType(module))
+		}
+	}
+
+	moduleSpecificFlagsPaths := android.Paths{}
+	for _, module := range hiddenAPISupportingModules {
+		moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV())
+	}
+
+	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 ebbe3a5..e51b049 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -31,7 +31,7 @@
 func TestPlatformBootclasspath(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
@@ -131,3 +131,186 @@
 		})
 	})
 }
+
+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,
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
+		android.PrepareForTestWithAndroidMk,
+		android.FixtureWithRootAndroidBp(`
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+				dists: [
+					{
+						targets: ["droidcore"],
+						tag: "hiddenapi-flags.csv",
+					},
+				],
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["a.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+			}
+
+			java_library {
+				name: "foo",
+				srcs: ["a.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+			}
+		`),
+	).RunTest(t)
+
+	platformBootclasspath := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, platformBootclasspath)
+	goals := entries[0].GetDistForGoals(platformBootclasspath)
+	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/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 8a442b5..c33e6c2 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -253,14 +253,8 @@
 		files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
 		for _, f := range files {
 			localPath := strings.TrimPrefix(f, mydir)
-			module, _, scope := parseApiFilePath(mctx, localPath)
-
-			// Figure out which module is referenced by this file. Special case for "android".
-			referencedModule := strings.TrimSuffix(module, "incompatibilities")
-			referencedModule = strings.TrimSuffix(referencedModule, "-")
-			if referencedModule == "" {
-				referencedModule = "android"
-			}
+			filename, _, scope := parseApiFilePath(mctx, localPath)
+			referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
 
 			createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
 
diff --git a/java/rro.go b/java/rro.go
index 4ae0014..2e58c04 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -141,23 +141,23 @@
 	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
 }
 
-func (r *RuntimeResourceOverlay) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(r.properties.Sdk_version))
+func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(r.properties.Sdk_version))
 }
 
 func (r *RuntimeResourceOverlay) SystemModules() string {
 	return ""
 }
 
-func (r *RuntimeResourceOverlay) MinSdkVersion() android.SdkSpec {
+func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if r.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*r.properties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *r.properties.Min_sdk_version)
 	}
-	return r.SdkVersion()
+	return r.SdkVersion(ctx)
 }
 
-func (r *RuntimeResourceOverlay) TargetSdkVersion() android.SdkSpec {
-	return r.SdkVersion()
+func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return r.SdkVersion(ctx)
 }
 
 func (r *RuntimeResourceOverlay) Certificate() Certificate {
diff --git a/java/sdk.go b/java/sdk.go
index f324b76..d6e20a7 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -61,7 +61,7 @@
 }
 
 func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
-	sdkVersion := sdkContext.SdkVersion()
+	sdkVersion := sdkContext.SdkVersion(ctx)
 	if !sdkVersion.Valid() {
 		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.Raw)
 		return sdkDep{}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 96135c3..e5ee397 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1512,7 +1512,7 @@
 	// force override sdk_version to module_current so that the closest possible API
 	// surface could be found in selectHeaderJarsForSdkVersion
 	if module.defaultsToStubs() && !sdkVersion.Specified() {
-		sdkVersion = android.SdkSpecFrom("module_current")
+		sdkVersion = android.SdkSpecFrom(ctx, "module_current")
 	}
 
 	// Only provide access to the implementation library if it is actually built.
diff --git a/java/testing.go b/java/testing.go
index 6ebc747..aee0710 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -178,6 +178,43 @@
 	return fs
 }
 
+// FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and
+// Config.productVariables structs. As a side effect that enables dexpreopt.
+func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer {
+	artBootJars := []string{}
+	for _, j := range bootJars {
+		artApex := false
+		for _, artApexName := range artApexNames {
+			if strings.HasPrefix(j, artApexName+":") {
+				artApex = true
+				break
+			}
+		}
+		if artApex {
+			artBootJars = append(artBootJars, j)
+		}
+	}
+	return android.GroupFixturePreparers(
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+		}),
+		dexpreopt.FixtureSetBootJars(bootJars...),
+		dexpreopt.FixtureSetArtBootJars(artBootJars...),
+	)
+}
+
+// FixtureConfigureUpdatableBootJars configures the updatable boot jars in both the
+// dexpreopt.GlobalConfig and Config.productVariables structs. As a side effect that enables
+// dexpreopt.
+func FixtureConfigureUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+	return android.GroupFixturePreparers(
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+		}),
+		dexpreopt.FixtureSetUpdatableBootJars(bootJars...),
+	)
+}
+
 // registerRequiredBuildComponentsForTest registers the build components used by
 // PrepareForTestWithJavaDefaultModules.
 //
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/Android.bp b/rust/Android.bp
index a29c474..a6c4e07 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -14,6 +14,7 @@
     ],
     srcs: [
         "androidmk.go",
+        "benchmark.go",
         "binary.go",
         "bindgen.go",
         "builder.go",
@@ -35,6 +36,7 @@
         "testing.go",
     ],
     testSrcs: [
+        "benchmark_test.go",
         "binary_test.go",
         "bindgen_test.go",
         "builder_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index dea32a3..5f89d73 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -60,6 +60,10 @@
 				entries.AddStrings("LOCAL_SHARED_LIBRARIES", mod.Properties.AndroidMkSharedLibs...)
 				entries.AddStrings("LOCAL_STATIC_LIBRARIES", mod.Properties.AndroidMkStaticLibs...)
 				entries.AddStrings("LOCAL_SOONG_LINK_TYPE", mod.makeLinkType)
+				if mod.UseVndk() {
+					entries.SetBool("LOCAL_USE_VNDK", true)
+				}
+
 			},
 		},
 	}
@@ -75,6 +79,7 @@
 		mod.SubAndroidMk(&ret, mod.sanitize)
 	}
 
+	ret.SubName += mod.Properties.RustSubName
 	ret.SubName += mod.Properties.SubName
 
 	return []android.AndroidMkEntries{ret}
@@ -107,6 +112,20 @@
 	cc.AndroidMkWriteTestData(test.data, ret)
 }
 
+func (benchmark *benchmarkDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+	benchmark.binaryDecorator.AndroidMk(ctx, ret)
+	ret.Class = "NATIVE_TESTS"
+	ret.ExtraEntries = append(ret.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
+			if benchmark.testConfig != nil {
+				entries.SetString("LOCAL_FULL_TEST_CONFIG", benchmark.testConfig.String())
+			}
+			entries.SetBool("LOCAL_NATIVE_BENCHMARK", true)
+			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(benchmark.Properties.Auto_gen_config, true))
+		})
+}
+
 func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
 	ctx.SubAndroidMk(ret, library.baseCompiler)
 
diff --git a/rust/benchmark.go b/rust/benchmark.go
new file mode 100644
index 0000000..b89f5cd
--- /dev/null
+++ b/rust/benchmark.go
@@ -0,0 +1,129 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"android/soong/tradefed"
+)
+
+type BenchmarkProperties struct {
+	// Disables the creation of a test-specific directory when used with
+	// relative_install_path. Useful if several tests need to be in the same
+	// directory, but test_per_src doesn't work.
+	No_named_install_directory *bool
+
+	// the name of the test configuration (for example "AndroidBenchmark.xml") that should be
+	// installed with the module.
+	Test_config *string `android:"path,arch_variant"`
+
+	// the name of the test configuration template (for example "AndroidBenchmarkTemplate.xml") that
+	// should be installed with the module.
+	Test_config_template *string `android:"path,arch_variant"`
+
+	// list of compatibility suites (for example "cts", "vts") that the module should be
+	// installed into.
+	Test_suites []string `android:"arch_variant"`
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
+}
+
+type benchmarkDecorator struct {
+	*binaryDecorator
+	Properties BenchmarkProperties
+	testConfig android.Path
+}
+
+func NewRustBenchmark(hod android.HostOrDeviceSupported) (*Module, *benchmarkDecorator) {
+	// Build both 32 and 64 targets for device benchmarks.
+	// Cannot build both for host benchmarks yet if the benchmark depends on
+	// something like proc-macro2 that cannot be built for both.
+	multilib := android.MultilibBoth
+	if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported {
+		multilib = android.MultilibFirst
+	}
+	module := newModule(hod, multilib)
+
+	benchmark := &benchmarkDecorator{
+		binaryDecorator: &binaryDecorator{
+			baseCompiler: NewBaseCompiler("nativebench", "nativebench64", InstallInData),
+		},
+	}
+
+	module.compiler = benchmark
+	module.AddProperties(&benchmark.Properties)
+	return module, benchmark
+}
+
+func init() {
+	android.RegisterModuleType("rust_benchmark", RustBenchmarkFactory)
+	android.RegisterModuleType("rust_benchmark_host", RustBenchmarkHostFactory)
+}
+
+func RustBenchmarkFactory() android.Module {
+	module, _ := NewRustBenchmark(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func RustBenchmarkHostFactory() android.Module {
+	module, _ := NewRustBenchmark(android.HostSupported)
+	return module.Init()
+}
+
+func (benchmark *benchmarkDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
+	return rlibAutoDep
+}
+
+func (benchmark *benchmarkDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	return RlibLinkage
+}
+
+func (benchmark *benchmarkDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = benchmark.binaryDecorator.compilerFlags(ctx, flags)
+	return flags
+}
+
+func (benchmark *benchmarkDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = benchmark.binaryDecorator.compilerDeps(ctx, deps)
+
+	deps.Rustlibs = append(deps.Rustlibs, "libcriterion")
+
+	return deps
+}
+
+func (benchmark *benchmarkDecorator) compilerProps() []interface{} {
+	return append(benchmark.binaryDecorator.compilerProps(), &benchmark.Properties)
+}
+
+func (benchmark *benchmarkDecorator) install(ctx ModuleContext) {
+	benchmark.testConfig = tradefed.AutoGenRustBenchmarkConfig(ctx,
+		benchmark.Properties.Test_config,
+		benchmark.Properties.Test_config_template,
+		benchmark.Properties.Test_suites,
+		nil,
+		benchmark.Properties.Auto_gen_config)
+
+	// default relative install path is module name
+	if !Bool(benchmark.Properties.No_named_install_directory) {
+		benchmark.baseCompiler.relative = ctx.ModuleName()
+	} else if String(benchmark.baseCompiler.Properties.Relative_install_path) == "" {
+		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
+	}
+
+	benchmark.binaryDecorator.install(ctx)
+}
diff --git a/rust/benchmark_test.go b/rust/benchmark_test.go
new file mode 100644
index 0000000..734dda7
--- /dev/null
+++ b/rust/benchmark_test.go
@@ -0,0 +1,54 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestRustBenchmark(t *testing.T) {
+	ctx := testRust(t, `
+		rust_benchmark_host {
+			name: "my_bench",
+			srcs: ["foo.rs"],
+		}`)
+
+	testingModule := ctx.ModuleForTests("my_bench", "linux_glibc_x86_64")
+	expectedOut := "my_bench/linux_glibc_x86_64/my_bench"
+	outPath := testingModule.Output("my_bench").Output.String()
+	if !strings.Contains(outPath, expectedOut) {
+		t.Errorf("wrong output path: %v;  expected: %v", outPath, expectedOut)
+	}
+}
+
+func TestRustBenchmarkLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_benchmark {
+			name: "my_bench",
+			srcs: ["foo.rs"],
+		}`)
+
+	testingModule := ctx.ModuleForTests("my_bench", "android_arm64_armv8-a").Module().(*Module)
+
+	if !android.InList("libcriterion.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for libcriterion not detected as a rustlib-defined rlib dependency for device rust_benchmark module")
+	}
+	if !android.InList("libstd", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_benchmark module 'my_bench' does not link libstd as an rlib")
+	}
+}
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/compiler.go b/rust/compiler.go
index aaa1924..bc034d7 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -303,7 +303,6 @@
 			if ctx.Target().Os == android.BuildOs {
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
-
 			deps.Stdlibs = append(deps.Stdlibs, stdlib)
 		}
 	}
@@ -344,6 +343,10 @@
 	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
+
+	if compiler.location == InstallInData && ctx.RustModule().UseVndk() {
+		dir = filepath.Join(dir, "vendor")
+	}
 	return android.PathForModuleInstall(ctx, dir, compiler.subDir,
 		compiler.relativeInstallPath(), compiler.relative)
 }
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 6035e68..d699971 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -70,10 +70,9 @@
 }
 
 func (fuzzer *fuzzDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.StaticLibs = append(deps.StaticLibs,
-		config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
-	deps.SharedLibs = append(deps.SharedLibs,
-		config.LibclangRuntimeLibrary(ctx.toolchain(), "asan"))
+	if libFuzzerRuntimeLibrary := config.LibFuzzerRuntimeLibrary(ctx.toolchain()); libFuzzerRuntimeLibrary != "" {
+		deps.StaticLibs = append(deps.StaticLibs, libFuzzerRuntimeLibrary)
+	}
 	deps.SharedLibs = append(deps.SharedLibs, "libc++")
 	deps.Rlibs = append(deps.Rlibs, "liblibfuzzer_sys")
 
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index f93ccc7..2524f91 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -37,9 +37,6 @@
 
 	// Check that appropriate dependencies are added and that the rustlib linkage is correct.
 	fuzz_libtest_mod := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
-	if !android.InList("libclang_rt.asan-aarch64-android", fuzz_libtest_mod.Properties.AndroidMkSharedLibs) {
-		t.Errorf("libclang_rt.asan-aarch64-android shared library dependency missing for rust_fuzz module.")
-	}
 	if !android.InList("liblibfuzzer_sys.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
 		t.Errorf("liblibfuzzer_sys rlib library dependency missing for rust_fuzz module. %#v", fuzz_libtest_mod.Properties.AndroidMkRlibs)
 	}
@@ -49,18 +46,18 @@
 
 	// Check that compiler flags are set appropriately .
 	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Output("fuzz_libtest")
-	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=address") ||
+	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
 		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
 		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, address sanitizer).")
+		t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
 
 	}
 
 	// Check that dependencies have 'fuzzer' variants produced for them as well.
 	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
-	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=address") ||
+	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
 		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
 		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, address sanitizer).")
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
 	}
 }
diff --git a/rust/image.go b/rust/image.go
index 628aca3..900842e 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -23,6 +23,68 @@
 
 var _ android.ImageInterface = (*Module)(nil)
 
+var _ cc.ImageMutatableModule = (*Module)(nil)
+
+func (mod *Module) VendorAvailable() bool {
+	return Bool(mod.VendorProperties.Vendor_available)
+}
+
+func (mod *Module) OdmAvailable() bool {
+	return Bool(mod.VendorProperties.Odm_available)
+}
+
+func (mod *Module) ProductAvailable() bool {
+	return false
+}
+
+func (mod *Module) RamdiskAvailable() bool {
+	return false
+}
+
+func (mod *Module) VendorRamdiskAvailable() bool {
+	return Bool(mod.Properties.Vendor_ramdisk_available)
+}
+
+func (mod *Module) AndroidModuleBase() *android.ModuleBase {
+	return &mod.ModuleBase
+}
+
+func (mod *Module) RecoveryAvailable() bool {
+	return false
+}
+
+func (mod *Module) ExtraVariants() []string {
+	return mod.Properties.ExtraVariants
+}
+
+func (mod *Module) AppendExtraVariant(extraVariant string) {
+	mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, extraVariant)
+}
+
+func (mod *Module) SetRamdiskVariantNeeded(b bool) {
+	if b {
+		panic("Setting ramdisk variant needed for Rust module is unsupported: " + mod.BaseModuleName())
+	}
+}
+
+func (mod *Module) SetVendorRamdiskVariantNeeded(b bool) {
+	mod.Properties.VendorRamdiskVariantNeeded = b
+}
+
+func (mod *Module) SetRecoveryVariantNeeded(b bool) {
+	if b {
+		panic("Setting recovery variant needed for Rust module is unsupported: " + mod.BaseModuleName())
+	}
+}
+
+func (mod *Module) SetCoreVariantNeeded(b bool) {
+	mod.Properties.CoreVariantNeeded = b
+}
+
+func (mod *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+	panic("Rust modules do not support snapshotting: " + mod.BaseModuleName())
+}
+
 func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return mod.Properties.VendorRamdiskVariantNeeded
 }
@@ -35,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()
 }
@@ -43,6 +109,29 @@
 	return mod.Properties.ExtraVariants
 }
 
+func (mod *Module) IsSnapshotPrebuilt() bool {
+	// Rust does not support prebuilts in its snapshots
+	return false
+}
+
+func (ctx *moduleContext) SocSpecific() bool {
+	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
+	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
+	// unless they have "odm_available: true".
+	return ctx.ModuleContext.SocSpecific() || (ctx.RustModule().InVendor() && !ctx.RustModule().VendorVariantToOdm())
+}
+
+func (ctx *moduleContext) DeviceSpecific() bool {
+	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
+	return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
+}
+
+// Returns true when this module creates a vendor variant and wants to install the vendor variant
+// to the odm partition.
+func (c *Module) VendorVariantToOdm() bool {
+	return Bool(c.VendorProperties.Odm_available)
+}
+
 func (ctx *moduleContext) ProductSpecific() bool {
 	return false
 }
@@ -84,10 +173,15 @@
 	return mod.HasVendorVariant() || mod.HasProductVariant()
 }
 
-func (c *Module) InProduct() bool {
+func (mod *Module) InProduct() bool {
 	return false
 }
 
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (mod *Module) InVendor() bool {
+	return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
+}
+
 func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
 	m := module.(*Module)
 	if variant == android.VendorRamdiskVariation {
@@ -107,9 +201,6 @@
 }
 
 func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
-	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-
 	// Rust does not support installing to the product image yet.
 	if Bool(mod.VendorProperties.Product_available) {
 		mctx.PropertyErrorf("product_available",
@@ -121,60 +212,19 @@
 		mctx.PropertyErrorf("double_loadable",
 			"Rust modules do not yet support double loading")
 	}
-
-	coreVariantNeeded := true
-	vendorRamdiskVariantNeeded := false
-
-	var vendorVariants []string
-
-	if mod.HasVendorVariant() {
-		prop := "vendor_available"
-		if Bool(mod.VendorProperties.Odm_available) {
-			prop = "odm_available"
-		}
-
-		if vendorSpecific {
-			mctx.PropertyErrorf(prop,
-				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
-		}
-
-		if lib, ok := mod.compiler.(libraryInterface); ok {
-			// Explicitly disallow rust_ffi variants which produce shared libraries from setting vendor_available.
-			// Vendor variants do not produce an error for dylibs, rlibs with dylib-std linkage are disabled in the respective library
-			// mutators until support is added.
-			//
-			// We can't check shared() here because image mutator is called before the library mutator, so we need to
-			// check buildShared()
-			if lib.buildShared() {
-				mctx.PropertyErrorf(prop, "cannot be set for rust_ffi or rust_ffi_shared modules.")
-			} else {
-				vendorVariants = append(vendorVariants, platformVndkVersion)
-			}
-		}
-	}
-
 	if Bool(mod.Properties.Vendor_ramdisk_available) {
 		if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && lib.buildShared()) {
 			mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
-		} else {
-			vendorRamdiskVariantNeeded = true
 		}
 	}
 
-	if vendorSpecific {
-		if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && (lib.buildShared() || lib.buildDylib() || lib.buildRlib())) {
-			mctx.ModuleErrorf("Rust vendor specific modules are currently only supported for rust_ffi_static modules.")
-		} else {
-			coreVariantNeeded = false
-			vendorVariants = append(vendorVariants, platformVndkVersion)
+	cc.MutateImage(mctx, mod)
+
+	if !mod.Properties.CoreVariantNeeded || mod.HasNonSystemVariants() {
+
+		if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+			// Rust does not support prebuilt libraries on non-System images.
+			mctx.ModuleErrorf("Rust prebuilt modules not supported for non-system images.")
 		}
 	}
-
-	mod.Properties.CoreVariantNeeded = coreVariantNeeded
-	mod.Properties.VendorRamdiskVariantNeeded = vendorRamdiskVariantNeeded
-
-	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, cc.VendorVariationPrefix+variant)
-	}
-
 }
diff --git a/rust/image_test.go b/rust/image_test.go
index 7677cce..95e788f 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -40,8 +40,8 @@
 
 	vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor.29_arm64_armv8-a").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("vendorBinary should have a dependency on libfoo_vendor")
+	if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
 	}
 }
 
@@ -87,47 +87,19 @@
 	}
 }
 
-// Test that shared libraries cannot be made vendor available until proper support is added.
+// Test that prebuilt libraries cannot be made vendor available.
 func TestForbiddenVendorLinkage(t *testing.T) {
-	testRustError(t, "cannot be set for rust_ffi or rust_ffi_shared modules.", `
-		rust_ffi_shared {
-			name: "libfoo_vendor",
-			crate_name: "foo",
-			srcs: ["foo.rs"],
-			vendor_available: true,
-		}
-	`)
-	testRustError(t, "cannot be set for rust_ffi or rust_ffi_shared modules.", `
-		rust_ffi_shared {
-			name: "libfoo_vendor",
-			crate_name: "foo",
-			srcs: ["foo.rs"],
-			vendor_ramdisk_available: true,
-		}
-	`)
-	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
-		rust_ffi {
-			name: "libfoo_vendor",
-			crate_name: "foo",
-			srcs: ["foo.rs"],
+	testRustVndkError(t, "Rust prebuilt modules not supported for non-system images.", `
+		rust_prebuilt_library {
+			name: "librust_prebuilt",
+			crate_name: "rust_prebuilt",
+			rlib: {
+				srcs: ["libtest.rlib"],
+			},
+			dylib: {
+				srcs: ["libtest.so"],
+			},
 			vendor: true,
 		}
-	`)
-	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
-		rust_library {
-			name: "libfoo_vendor",
-			crate_name: "foo",
-			srcs: ["foo.rs"],
-			vendor: true,
-		}
-	`)
-	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
-		rust_binary {
-			name: "foo_vendor",
-			crate_name: "foo",
-			srcs: ["foo.rs"],
-			vendor: true,
-		}
-	`)
-
+       `)
 }
diff --git a/rust/library.go b/rust/library.go
index 71fe1f5..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)
@@ -596,9 +596,9 @@
 			v.(*Module).compiler.(libraryInterface).setRlib()
 		case dylibVariation:
 			v.(*Module).compiler.(libraryInterface).setDylib()
-			if v.(*Module).ModuleBase.ImageVariation().Variation != android.CoreVariation {
+			if v.(*Module).ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
 				// TODO(b/165791368)
-				// Disable dylib non-core variations until we support these.
+				// Disable dylib Vendor Ramdisk variations until we support these.
 				v.(*Module).Disable()
 			}
 		case "source":
@@ -637,14 +637,14 @@
 				dylib := modules[1].(*Module)
 				rlib.compiler.(libraryInterface).setRlibStd()
 				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation != android.CoreVariation {
+				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
 					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on non-core variations until non-core dylib
+					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
 					// variants are properly supported.
 					dylib.Disable()
 				}
-				rlib.Properties.SubName += RlibStdlibSuffix
-				dylib.Properties.SubName += DylibStdlibSuffix
+				rlib.Properties.RustSubName += RlibStdlibSuffix
+				dylib.Properties.RustSubName += DylibStdlibSuffix
 			}
 		}
 	}
diff --git a/rust/rust.go b/rust/rust.go
index 34e197a..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"
@@ -73,6 +74,11 @@
 	VndkVersion          string `blueprint:"mutated"`
 	SubName              string `blueprint:"mutated"`
 
+	// SubName is used by CC for tracking image variants / SDK versions. RustSubName is used for Rust-specific
+	// subnaming which shouldn't be visible to CC modules (such as the rlib stdlinkage subname). This should be
+	// appended before SubName.
+	RustSubName string `blueprint:"mutated"`
+
 	// Set by imageMutator
 	CoreVariantNeeded          bool     `blueprint:"mutated"`
 	VendorRamdiskVariantNeeded bool     `blueprint:"mutated"`
@@ -131,11 +137,6 @@
 	mod.Properties.PreventInstall = true
 }
 
-// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (mod *Module) InVendor() bool {
-	return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
-}
-
 func (mod *Module) SetHideFromMake() {
 	mod.Properties.HideFromMake = true
 }
@@ -231,7 +232,11 @@
 }
 
 func (mod *Module) MustUseVendorVariant() bool {
-	return false
+	return true
+}
+
+func (mod *Module) SubName() string {
+	return mod.Properties.SubName
 }
 
 func (mod *Module) IsVndk() bool {
@@ -255,6 +260,22 @@
 	return false
 }
 
+func (m *Module) IsLlndkHeaders() bool {
+	return false
+}
+
+func (m *Module) IsLlndkLibrary() bool {
+	return false
+}
+
+func (mod *Module) KernelHeadersDecorator() bool {
+	return false
+}
+
+func (m *Module) HasLlndkStubs() bool {
+	return false
+}
+
 func (mod *Module) SdkVersion() string {
 	return ""
 }
@@ -434,6 +455,7 @@
 	module.AddProperties(
 		&BaseProperties{},
 		&cc.VendorProperties{},
+		&BenchmarkProperties{},
 		&BindgenProperties{},
 		&BaseCompilerProperties{},
 		&BinaryCompilerProperties{},
@@ -730,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) {
@@ -842,8 +864,10 @@
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
+
 		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
 			//Handle Rust Modules
+			makeLibName := cc.MakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
 
 			switch depTag {
 			case dylibDepTag:
@@ -853,19 +877,19 @@
 					return
 				}
 				directDylibDeps = append(directDylibDeps, rustDep)
-				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, depName)
+				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
 			case rlibDepTag:
 
 				rlib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !rlib.rlib() {
-					ctx.ModuleErrorf("mod %q not an rlib library", depName+rustDep.Properties.SubName)
+					ctx.ModuleErrorf("mod %q not an rlib library", makeLibName)
 					return
 				}
 				directRlibDeps = append(directRlibDeps, rustDep)
-				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName+rustDep.Properties.SubName)
+				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
-				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
+				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
 			case android.SourceDepTag:
 				// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
 				// OS/Arch variant is used.
@@ -909,6 +933,7 @@
 
 		} else if ccDep, ok := dep.(cc.LinkableInterface); ok {
 			//Handle C dependencies
+			makeLibName := cc.MakeLibName(ctx, mod, ccDep, depName)
 			if _, ok := ccDep.(*Module); !ok {
 				if ccDep.Module().Target().Os != ctx.Os() {
 					ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
@@ -950,7 +975,7 @@
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
-				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
+				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
 			case cc.IsSharedDepTag(depTag):
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
@@ -960,7 +985,7 @@
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 				directSharedLibDeps = append(directSharedLibDeps, ccDep)
-				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
+				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
 				exportDep = true
 			case cc.IsHeaderDepTag(depTag):
 				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 890fb26..6ae05d9 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -113,6 +113,24 @@
 		RunTestWithBp(t, bp)
 }
 
+// testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors.
+func testRustVndkError(t *testing.T, pattern string, bp string) {
+	skipTestIfOsNotSupported(t)
+	android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.DeviceVndkVersion = StringPtr("current")
+				variables.ProductVndkVersion = StringPtr("current")
+				variables.Platform_vndk_version = StringPtr("VER")
+			},
+		),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+		RunTestWithBp(t, bp)
+}
+
 // testRustCtx is used to build a particular test environment. Unless your
 // tests requires a specific setup, prefer the wrapping functions: testRust,
 // testRustCov or testRustError.
@@ -384,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/sanitize.go b/rust/sanitize.go
index ae3eff0..0a53f98 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -46,7 +46,6 @@
 	"-C llvm-args=-sanitizer-coverage-inline-8bit-counters",
 	"-C llvm-args=-sanitizer-coverage-trace-geps",
 	"-C llvm-args=-sanitizer-coverage-prune-blocks=0",
-	"-Z sanitizer=address",
 
 	// Sancov breaks with lto
 	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
@@ -109,6 +108,11 @@
 	}
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
+		if ctx.Arch().ArchType == android.Arm64 {
+			flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
+		} else {
+			flags.RustFlags = append(flags.RustFlags, asanFlags...)
+		}
 	}
 	if Bool(sanitize.Properties.Sanitize.Address) {
 		flags.RustFlags = append(flags.RustFlags, asanFlags...)
@@ -133,12 +137,14 @@
 		var depTag blueprint.DependencyTag
 		var deps []string
 
-		if Bool(mod.sanitize.Properties.Sanitize.Fuzzer) || Bool(mod.sanitize.Properties.Sanitize.Address) {
+		if mod.IsSanitizerEnabled(cc.Asan) ||
+			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType != android.Arm64) {
 			variations = append(variations,
 				blueprint.Variation{Mutator: "link", Variation: "shared"})
 			depTag = cc.SharedDepTag()
 			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
-		} else if mod.IsSanitizerEnabled(cc.Hwasan) {
+		} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
+			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
 			// TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
 			if binary, ok := mod.compiler.(*binaryDecorator); ok {
 				if Bool(binary.Properties.Static_executable) {
@@ -285,11 +291,9 @@
 
 func (mod *Module) StaticallyLinked() bool {
 	if lib, ok := mod.compiler.(libraryInterface); ok {
-		if lib.rlib() || lib.static() {
-			return true
-		}
-	} else if Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable) {
-		return true
+		return lib.rlib() || lib.static()
+	} else if binary, ok := mod.compiler.(*binaryDecorator); ok {
+		return Bool(binary.Properties.Static_executable)
 	}
 	return false
 }
diff --git a/rust/testing.go b/rust/testing.go
index 75adcfc..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()),
 )
@@ -127,6 +129,7 @@
 			system_shared_libs: [],
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
+			vendor_available: true,
 		}
 		cc_library {
 			name: "libprotobuf-cpp-full",
@@ -150,7 +153,7 @@
 			host_supported: true,
 			vendor_available: true,
 			vendor_ramdisk_available: true,
-                        native_coverage: false,
+			native_coverage: false,
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
@@ -163,7 +166,7 @@
 			host_supported: true,
 			vendor_available: true,
 			vendor_ramdisk_available: true,
-                        native_coverage: false,
+			native_coverage: false,
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
@@ -192,11 +195,19 @@
 			srcs:["foo.rs"],
 			host_supported: true,
 		}
+		rust_library {
+			name: "libcriterion",
+			crate_name: "criterion",
+			srcs:["foo.rs"],
+			host_supported: true,
+		}
 `
 	return bp
 }
 
 func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("rust_benchmark", RustBenchmarkFactory)
+	ctx.RegisterModuleType("rust_benchmark_host", RustBenchmarkHostFactory)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
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/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 b2f7b2b..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() {
@@ -295,6 +349,120 @@
   grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
 }
 
+# Tests a glob in a build= statement in an Android.bp file, which is interpreted
+# during bootstrapping.
+function test_glob_during_bootstrapping() {
+  setup
+
+  mkdir -p a
+  cat > a/Android.bp <<'EOF'
+build=["foo*.bp"]
+EOF
+  cat > a/fooa.bp <<'EOF'
+bootstrap_go_package {
+  name: "picard-soong-rules",
+  pkgPath: "android/soong/picard",
+  deps: [
+    "blueprint",
+    "soong",
+    "soong-android",
+  ],
+  srcs: [
+    "picard.go",
+  ],
+  pluginFor: ["soong_build"],
+}
+EOF
+
+  cat > a/picard.go <<'EOF'
+package picard
+
+import (
+  "android/soong/android"
+  "github.com/google/blueprint"
+)
+
+var (
+  pctx = android.NewPackageContext("picard")
+)
+
+func init() {
+  android.RegisterSingletonType("picard", PicardSingleton)
+}
+
+func PicardSingleton() android.Singleton {
+  return &picardSingleton{}
+}
+
+type picardSingleton struct{}
+
+var Message = "Make it so."
+
+func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+  picardRule := ctx.Rule(pctx, "picard",
+    blueprint.RuleParams{
+      Command: "echo " + Message + " > ${out}",
+      CommandDeps: []string{},
+      Description: "Something quotable",
+    })
+
+  outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
+  var deps android.Paths
+
+  ctx.Build(pctx, android.BuildParams{
+    Rule: picardRule,
+    Output: outputFile,
+    Inputs: deps,
+  })
+}
+
+EOF
+
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
+
+  cat > a/foob.bp <<'EOF'
+bootstrap_go_package {
+  name: "worf-soong-rules",
+  pkgPath: "android/soong/worf",
+  deps: [
+    "blueprint",
+    "soong",
+    "soong-android",
+    "picard-soong-rules",
+  ],
+  srcs: [
+    "worf.go",
+  ],
+  pluginFor: ["soong_build"],
+}
+EOF
+
+  cat > a/worf.go <<'EOF'
+package worf
+
+import "android/soong/picard"
+
+func init() {
+   picard.Message = "Engage."
+}
+EOF
+
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$mtime1" == "$mtime2" ]]; then
+    fail "Output Ninja file did not change"
+  fi
+
+  grep -q "Engage" out/soong/build.ninja || fail "New action not present"
+
+  if grep -q "Make it so" out/soong/build.ninja; then
+    fail "Original action still present"
+  fi
+}
+
 function test_null_build_after_docs {
   setup
   run_soong
@@ -309,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
@@ -321,10 +566,15 @@
 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
 test_delete_android_bp
 test_add_file_to_soong_build
+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 3c97e14..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 'echo cd / && echo 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/tradefed/autogen.go b/tradefed/autogen.go
index 27d71e8..3d96c84 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -245,6 +245,25 @@
 	return path
 }
 
+func AutoGenRustBenchmarkConfig(ctx android.ModuleContext, testConfigProp *string,
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+	if autogenPath != nil {
+		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+		if templatePath.Valid() {
+			autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
+		} else {
+			if ctx.Device() {
+				autogenTemplate(ctx, autogenPath, "${RustDeviceBenchmarkConfigTemplate}", config, "")
+			} else {
+				autogenTemplate(ctx, autogenPath, "${RustHostBenchmarkConfigTemplate}", config, "")
+			}
+		}
+		return autogenPath
+	}
+	return path
+}
+
 func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
 	testSuites []string, autoGenConfig *bool) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
diff --git a/tradefed/config.go b/tradefed/config.go
index f3566a8..999424c 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -34,6 +34,8 @@
 	pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
 	pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
 	pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
+	pctx.SourcePathVariable("RustDeviceBenchmarkConfigTemplate", "build/make/core/rust_device_benchmark_config_template.xml")
+	pctx.SourcePathVariable("RustHostBenchmarkConfigTemplate", "build/make/core/rust_host_benchmark_config_template.xml")
 	pctx.SourcePathVariable("RobolectricTestConfigTemplate", "build/make/core/robolectric_test_config_template.xml")
 	pctx.SourcePathVariable("ShellTestConfigTemplate", "build/make/core/shell_test_config_template.xml")
 
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
index f9682e4..9b5a20f 100644
--- a/tradefed/makevars.go
+++ b/tradefed/makevars.go
@@ -33,6 +33,8 @@
 	ctx.Strict("PYTHON_BINARY_HOST_TEST_CONFIG_TEMPLATE", "${PythonBinaryHostTestConfigTemplate}")
 	ctx.Strict("RUST_DEVICE_TEST_CONFIG_TEMPLATE", "${RustDeviceTestConfigTemplate}")
 	ctx.Strict("RUST_HOST_TEST_CONFIG_TEMPLATE", "${RustHostTestConfigTemplate}")
+	ctx.Strict("RUST_DEVICE_BENCHMARK_CONFIG_TEMPLATE", "${RustDeviceBenchmarkConfigTemplate}")
+	ctx.Strict("RUST_HOST_BENCHMARK_CONFIG_TEMPLATE", "${RustHostBenchmarkConfigTemplate}")
 	ctx.Strict("SHELL_TEST_CONFIG_TEMPLATE", "${ShellTestConfigTemplate}")
 
 	ctx.Strict("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 9afcb88..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,10 +117,52 @@
 	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")
+	if args.DelveListen != "" {
+		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{
@@ -113,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) {
@@ -124,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)
@@ -133,16 +206,13 @@
 		}
 	}
 
+	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"))
-	// These two dependencies are read from bootstrap.go, but also need to be here
-	// so that soong_build can declare a dependency on them
-	soongBuildEnv.Set("SOONG_DELVE", os.Getenv("SOONG_DELVE"))
-	soongBuildEnv.Set("SOONG_DELVE_PATH", os.Getenv("SOONG_DELVE_PATH"))
-	soongBuildEnv.Set("SOONG_OUTDIR", config.SoongOutDir())
 	// For Bazel mixed builds.
 	soongBuildEnv.Set("BAZEL_PATH", "./tools/bazel")
 	soongBuildEnv.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
@@ -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)
 		}
 	}()
 
@@ -215,13 +284,6 @@
 		// This is currently how the command line to invoke soong_build finds the
 		// root of the source tree and the output root
 		ninjaEnv.Set("TOP", os.Getenv("TOP"))
-		ninjaEnv.Set("SOONG_OUTDIR", config.SoongOutDir())
-
-		// For debugging
-		if os.Getenv("SOONG_DELVE") != "" {
-			ninjaEnv.Set("SOONG_DELVE", os.Getenv("SOONG_DELVE"))
-			ninjaEnv.Set("SOONG_DELVE_PATH", shared.ResolveDelveBinary())
-		}
 
 		cmd.Environment = &ninjaEnv
 		cmd.Sandbox = soongSandbox
diff --git a/zip/zip.go b/zip/zip.go
index a6490d4..84e974b 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -292,11 +292,11 @@
 				continue
 			}
 
-			globbed, _, err := z.fs.Glob(s, nil, followSymlinks)
+			result, err := z.fs.Glob(s, nil, followSymlinks)
 			if err != nil {
 				return err
 			}
-			if len(globbed) == 0 {
+			if len(result.Matches) == 0 {
 				err := &os.PathError{
 					Op:   "lstat",
 					Path: s,
@@ -308,7 +308,7 @@
 					return err
 				}
 			}
-			srcs = append(srcs, globbed...)
+			srcs = append(srcs, result.Matches...)
 		}
 		if fa.GlobDir != "" {
 			if exists, isDir, err := z.fs.Exists(fa.GlobDir); err != nil {
@@ -336,11 +336,11 @@
 					return err
 				}
 			}
-			globbed, _, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
+			result, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
 			if err != nil {
 				return err
 			}
-			srcs = append(srcs, globbed...)
+			srcs = append(srcs, result.Matches...)
 		}
 		for _, src := range srcs {
 			err := fillPathPairs(fa, src, &pathMappings, args.NonDeflatedFiles, noCompression)