Merge "Allow PRODUCT_CFI_INCLUDE_PATHS to work with blueprints also"
diff --git a/android/bazel.go b/android/bazel.go
index 7123ed2..fa19e52 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -148,11 +148,12 @@
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
+		"external/bazel-skylib":/* recursive = */ true,
 
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
 		"prebuilts/r8":/* recursive = */ false,
-		"packages/apps/Music":/* recursive = */ false,
+		"packages/apps/Music":/* recursive = */ true,
 	}
 
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
@@ -166,9 +167,10 @@
 		"system/logging/liblog":           Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":     Bp2BuildDefaultTrueRecursively,
-		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
-		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
+		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
+		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
+		"external/libcxxabi":              Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                  Bp2BuildDefaultTrueRecursively,
 		"prebuilts/clang/host/linux-x86":  Bp2BuildDefaultTrueRecursively,
 	}
@@ -204,16 +206,11 @@
 		"libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx
 		"fmtlib",                   // cc_library_static, fatal error: 'cassert' file not found, from libcxx
 		"fmtlib_ndk",               // cc_library_static, fatal error: 'cassert' file not found
+		"liblog",                   // http://b/186822772: cc_library, 'sys/cdefs.h' file not found
 		"libbase",                  // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx.
+		// Also depends on fmtlib.
 
-		// http://b/186024507: Includes errors because of the system_shared_libs default value.
-		// Missing -isystem bionic/libc/include through the libc/libm/libdl
-		// default dependencies if system_shared_libs is unset.
-		"liblog",                 // http://b/186822772: cc_library, 'sys/cdefs.h' file not found
-		"libjemalloc5_jet",       // cc_library, 'sys/cdefs.h' file not found
-		"libseccomp_policy",      // http://b/186476753: cc_library, 'linux/filter.h' not found
-		"note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found
-		"note_memtag_heap_sync",  // http://b/185127353: cc_library_static, error: feature.h not found
+		"libseccomp_policy", // depends on libbase
 
 		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
 
@@ -236,7 +233,10 @@
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
-	mixedBuildsDisabledList = []string{}
+	mixedBuildsDisabledList = []string{
+		"libc++abi",      // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+		"libc++demangle", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+	}
 
 	// Used for quicker lookups
 	bp2buildModuleDoNotConvert  = map[string]bool{}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 341d500..312f009 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -28,8 +28,6 @@
 
 	"android/soong/bazel/cquery"
 
-	"github.com/google/blueprint/bootstrap"
-
 	"android/soong/bazel"
 	"android/soong/shared"
 )
@@ -57,6 +55,17 @@
 	archType    ArchType
 }
 
+// bazelHandler is the interface for a helper object related to deferring to Bazel for
+// processing a module (during Bazel mixed builds). Individual module types should define
+// their own bazel handler if they support deferring to Bazel.
+type BazelHandler interface {
+	// Issue query to Bazel to retrieve information about Bazel's view of the current module.
+	// If Bazel returns this information, set module properties on the current module to reflect
+	// the returned information.
+	// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
+	GenerateBazelBuildActions(ctx ModuleContext, label string) bool
+}
+
 type BazelContext interface {
 	// The below methods involve queuing cquery requests to be later invoked
 	// by bazel. If any of these methods return (_, false), then the request
@@ -69,6 +78,9 @@
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
 	GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error)
 
+	// Returns the executable binary resultant from building together the python sources
+	GetPythonBinary(label string, archType ArchType) (string, bool)
+
 	// ** End cquery methods
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -123,8 +135,9 @@
 type MockBazelContext struct {
 	OutputBaseDir string
 
-	LabelToOutputFiles map[string][]string
-	LabelToCcInfo      map[string]cquery.CcInfo
+	LabelToOutputFiles  map[string][]string
+	LabelToCcInfo       map[string]cquery.CcInfo
+	LabelToPythonBinary map[string]string
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
@@ -137,6 +150,11 @@
 	return result, ok, nil
 }
 
+func (m MockBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+	result, ok := m.LabelToPythonBinary[label]
+	return result, ok
+}
+
 func (m MockBazelContext) InvokeBazel() error {
 	panic("unimplemented")
 }
@@ -174,6 +192,16 @@
 	return ret, ok, err
 }
 
+func (bazelCtx *bazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+	rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, archType)
+	var ret string
+	if ok {
+		bazelOutput := strings.TrimSpace(rawString)
+		ret = cquery.GetPythonBinary.ParseResult(bazelOutput)
+	}
+	return ret, ok
+}
+
 func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
@@ -182,6 +210,10 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+	panic("unimplemented")
+}
+
 func (n noopBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
@@ -726,7 +758,7 @@
 
 	// Add ninja file dependencies for files which all bazel invocations require.
 	bazelBuildList := absolutePath(filepath.Join(
-		filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list"))
+		filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
 	ctx.AddNinjaFileDeps(bazelBuildList)
 
 	data, err := ioutil.ReadFile(bazelBuildList)
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index b050774..c09d218 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -115,6 +115,11 @@
 
 func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList {
 	var labels bazel.LabelList
+	// In some cases, a nil string list is different than an explicitly empty list.
+	if len(modules) == 0 && modules != nil {
+		labels.Includes = []bazel.Label{}
+		return labels
+	}
 	for _, module := range modules {
 		bpText := module
 		if m := SrcIsModule(module); m == "" {
diff --git a/android/config.go b/android/config.go
index b3b8f3c..35403b8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -79,10 +79,6 @@
 	return false // Never compile Go code in the main build for debugging
 }
 
-func (c Config) SrcDir() string {
-	return c.srcDir
-}
-
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -126,7 +122,6 @@
 
 	deviceConfig *deviceConfig
 
-	srcDir         string // the path of the root source directory
 	buildDir       string // the path of the build output directory
 	moduleListFile string // the path to the file which lists blueprint files to parse.
 
@@ -209,6 +204,20 @@
 		Bool(configurable.GcovCoverage) ||
 			Bool(configurable.ClangCoverage))
 
+	// when Platform_sdk_final is true (or PLATFORM_VERSION_CODENAME is REL), use Platform_sdk_version;
+	// if false (pre-released version, for example), use Platform_sdk_codename.
+	if Bool(configurable.Platform_sdk_final) {
+		if configurable.Platform_sdk_version != nil {
+			configurable.Platform_sdk_version_or_codename =
+				proptools.StringPtr(strconv.Itoa(*(configurable.Platform_sdk_version)))
+		} else {
+			return fmt.Errorf("Platform_sdk_version cannot be pointed by a NULL pointer")
+		}
+	} else {
+		configurable.Platform_sdk_version_or_codename =
+			proptools.StringPtr(String(configurable.Platform_sdk_codename))
+	}
+
 	return saveToBazelConfigFile(configurable, filepath.Dir(filename))
 }
 
@@ -388,7 +397,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, c.env)
+	newConfig, err := NewConfig(c.buildDir, c.moduleListFile, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -399,14 +408,13 @@
 
 // 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, availableEnv map[string]string) (Config, error) {
+func NewConfig(buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
 
 		env: availableEnv,
 
-		srcDir:            srcDir,
 		buildDir:          buildDir,
 		multilibConflicts: make(map[ArchType]bool),
 
@@ -425,7 +433,7 @@
 		return Config{}, err
 	}
 
-	absSrcDir, err := filepath.Abs(srcDir)
+	absSrcDir, err := filepath.Abs(".")
 	if err != nil {
 		return Config{}, err
 	}
@@ -583,7 +591,7 @@
 
 // GoRoot returns the path to the root directory of the Go toolchain.
 func (c *config) GoRoot() string {
-	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
+	return fmt.Sprintf("prebuilts/go/%s", c.PrebuiltOS())
 }
 
 // PrebuiltBuildTool returns the path to a tool in the prebuilts directory containing
diff --git a/android/filegroup.go b/android/filegroup.go
index e207412..97dd136 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,8 +15,9 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"strings"
+
+	"android/soong/bazel"
 )
 
 func init() {
@@ -108,7 +109,7 @@
 	return module
 }
 
-func (fg *fileGroup) generateBazelBuildActions(ctx ModuleContext) bool {
+func (fg *fileGroup) GenerateBazelBuildActions(ctx ModuleContext) bool {
 	if !fg.MixedBuildsEnabled(ctx) {
 		return false
 	}
@@ -131,7 +132,7 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	if fg.generateBazelBuildActions(ctx) {
+	if fg.GenerateBazelBuildActions(ctx) {
 		return
 	}
 
diff --git a/android/module.go b/android/module.go
index 5e2e06a..7451e09 100644
--- a/android/module.go
+++ b/android/module.go
@@ -235,8 +235,8 @@
 
 	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
 	// direct dependencies on the same module visit will be called multiple times on that module
-	// and OtherModuleDependencyTag will return a different tag for each.  It skips any
-	// dependencies that are not an android.Module.
+	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
+	// dependencies are not an android.Module.
 	//
 	// The Module passed to the visit function should not be retained outside of the visit
 	// function, it may be invalidated by future mutators.
diff --git a/android/paths.go b/android/paths.go
index 9c9914e..71caaab 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -622,7 +622,7 @@
 // It intended for use in globs that only list files that exist, so it allows '$' in
 // filenames.
 func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths {
-	prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
+	prefix := ctx.ModuleDir() + "/"
 	if prefix == "./" {
 		prefix = ""
 	}
@@ -658,7 +658,7 @@
 	}
 	// Use Glob so that if the default doesn't exist, a dependency is added so that when it
 	// is created, we're run again.
-	path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
+	path := filepath.Join(ctx.ModuleDir(), def)
 	return Glob(ctx, path, nil)
 }
 
@@ -986,7 +986,7 @@
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
 	p, err := validateSafePath(pathComponents...)
-	ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
+	ret := SourcePath{basePath{p, ""}, "."}
 	if err != nil {
 		return ret, err
 	}
@@ -1002,7 +1002,7 @@
 // pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
 func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
 	p, err := validatePath(pathComponents...)
-	ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
+	ret := SourcePath{basePath{p, ""}, "."}
 	if err != nil {
 		return ret, err
 	}
diff --git a/android/variable.go b/android/variable.go
index 0fb9078..5cf9aa8 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -42,6 +42,10 @@
 			Cflags  []string
 		}
 
+		Platform_sdk_version_or_codename struct {
+			Java_resource_dirs []string
+		}
+
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {
@@ -164,6 +168,7 @@
 	Platform_version_name                     *string  `json:",omitempty"`
 	Platform_sdk_version                      *int     `json:",omitempty"`
 	Platform_sdk_codename                     *string  `json:",omitempty"`
+	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
 	Platform_sdk_final                        *bool    `json:",omitempty"`
 	Platform_version_active_codenames         []string `json:",omitempty"`
 	Platform_vndk_version                     *string  `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index e525aff..b47f36b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1100,13 +1100,6 @@
 			mctx.ModuleErrorf("base property is not set")
 			return
 		}
-		// Workaround the issue reported in b/191269918 by using the unprefixed module name of this
-		// module as the default variation to use if dependencies of this module do not have the correct
-		// apex variant name. This name matches the name used to create the variations of modules for
-		// which apexModuleTypeRequiresVariant return true.
-		// TODO(b/191269918): Remove this workaround.
-		unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
-		mctx.SetDefaultDependencyVariation(&unprefixedModuleName)
 		mctx.CreateVariations(apexBundleName)
 		if strings.HasPrefix(apexBundleName, "com.android.art") {
 			// TODO(b/183882457): See note for CreateAliasVariation above.
@@ -1587,6 +1580,7 @@
 	JacocoReportClassesFile() android.Path
 	Certificate() java.Certificate
 	BaseModuleName() string
+	LintDepSets() java.LintDepSets
 }
 
 var _ androidApp = (*java.AndroidApp)(nil)
@@ -1601,6 +1595,7 @@
 	fileToCopy := aapp.OutputFile()
 	af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
 	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
+	af.lintDepSets = aapp.LintDepSets()
 	af.certificate = aapp.Certificate()
 
 	if app, ok := aapp.(interface {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 41bfcea..f07bf63 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4663,6 +4663,7 @@
 
 			prebuilt_bootclasspath_fragment {
 				name: "art-bootclasspath-fragment",
+				image_name: "art",
 				contents: ["core-oj"],
 				hidden_api: {
 					annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
@@ -4871,7 +4872,7 @@
 
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
-		java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar"),
+		java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -4949,6 +4950,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_sdk_library_import {
@@ -4958,6 +4960,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 	`
 
@@ -4998,6 +5001,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_sdk_library_import {
@@ -5007,6 +5011,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 	`
 
@@ -5120,6 +5125,7 @@
 			prefer: true,
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_library {
@@ -5136,6 +5142,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 
 		java_sdk_library {
@@ -5209,6 +5216,7 @@
 			name: "libfoo",
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_sdk_library_import {
@@ -5225,6 +5233,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
 		}
 	`
 
@@ -5286,6 +5295,7 @@
 			prefer: true,
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_library {
@@ -5302,6 +5312,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 
 		java_sdk_library {
@@ -6899,6 +6910,7 @@
 			apex_available: [
 				"some-updatable-apex",
 			],
+			permitted_packages: ["some.updatable.apex.lib"],
 		}
 
 		java_library {
@@ -6908,6 +6920,7 @@
 				"some-non-updatable-apex",
 			],
 			compile_dex: true,
+			permitted_packages: ["some.non.updatable.apex.lib"],
 		}
 
 		bootclasspath_fragment {
@@ -7118,7 +7131,9 @@
 				"myapex",
 			],
 		}
-	`)
+	`,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	)
 }
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
@@ -7148,18 +7163,30 @@
 	}
 
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
-		preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
-		fragment := java.ApexVariantReference{
-			Apex:   proptools.StringPtr("com.android.art.debug"),
-			Module: proptools.StringPtr("art-bootclasspath-fragment"),
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
+		fragments := []java.ApexVariantReference{
+			{
+				Apex:   proptools.StringPtr("com.android.art.debug"),
+				Module: proptools.StringPtr("art-bootclasspath-fragment"),
+			},
+			{
+				Apex:   proptools.StringPtr("some-non-updatable-apex"),
+				Module: proptools.StringPtr("some-non-updatable-fragment"),
+			},
 		}
-		testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
+		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
 	})
 
 	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`
 		// Update the dexpreopt BootJars directly.
-		preparer := prepareSetBootJars("com.android.art.debug:some-art-lib")
+		preparer := android.GroupFixturePreparers(
+			prepareSetBootJars("com.android.art.debug:some-art-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
@@ -7179,12 +7206,15 @@
 
 	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`
-		preparer := java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib")
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-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) {
-		preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+		preparer := java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
 		fragment := java.ApexVariantReference{
 			Apex:   proptools.StringPtr("some-non-updatable-apex"),
 			Module: proptools.StringPtr("some-non-updatable-fragment"),
@@ -7212,13 +7242,22 @@
 	})
 
 	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
-		preparer := java.FixtureConfigureBootJars("platform:some-platform-lib")
-		testNoUpdatableJarsInBootImage(t, "", preparer)
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureBootJars("platform:some-platform-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
+		fragments := []java.ApexVariantReference{
+			{
+				Apex:   proptools.StringPtr("some-non-updatable-apex"),
+				Module: proptools.StringPtr("some-non-updatable-fragment"),
+			},
+		}
+		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
 	})
 }
 
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
-	preparer := java.FixtureConfigureBootJars("myapex:libfoo")
+	preparer := java.FixtureConfigureApexBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
 		fragment := java.ApexVariantReference{
 			Apex:   proptools.StringPtr("myapex"),
@@ -7256,6 +7295,7 @@
 				name: "libfoo",
 				jars: ["libfoo.jar"],
 				apex_available: ["myapex"],
+				permitted_packages: ["libfoo"],
 			}
 		`, "", preparer, fragment)
 	})
@@ -8168,6 +8208,8 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAndroidBuildComponents,
 		android.FixtureWithRootAndroidBp(bp),
+		dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
 		android.FixtureMergeEnv(map[string]string{
 			"EMMA_INSTRUMENT": "true",
 		}),
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 6098989..3e19014 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -130,7 +130,8 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		// Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+		java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"),
 		prepareForTestWithArtApex,
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -642,7 +643,7 @@
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
-		java.FixtureConfigureBootJars("myapex:foo", "myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -893,7 +894,8 @@
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -1062,7 +1064,8 @@
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
diff --git a/apex/builder.go b/apex/builder.go
index 148f42f..5baa5c0 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -75,7 +75,7 @@
 	// by default set to (uid/gid/mode) = (1000/1000/0644)
 	// TODO(b/113082813) make this configurable using config.fs syntax
 	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
-		Command: `( echo '/ 1000 1000 0755' ` +
+		Command: `( set -e; echo '/ 1000 1000 0755' ` +
 			`&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
 			`&& for i in  ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
 			`&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 0193127..e2d8465 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -58,6 +58,7 @@
 		}),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
+		java.FixtureConfigureApexBootJars("myapex:bar"),
 		android.FixtureWithRootAndroidBp(`
 		apex {
 			name: "com.android.art",
@@ -79,6 +80,7 @@
 
 		bootclasspath_fragment {
 			name: "art-bootclasspath-fragment",
+			image_name: "art",
 			apex_available: [
 				"com.android.art",
 			],
@@ -193,6 +195,10 @@
 					apex: "com.android.art",
 					module: "art-bootclasspath-fragment",
 				},
+				{
+					apex: "myapex",
+					module: "mybootclasspath-fragment",
+				},
 			],
 		}
 	`),
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index eaee20d..e0421f6 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -39,7 +39,7 @@
 		prepareForTestWithMyapex,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
-		java.FixtureConfigureBootJars("myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:bar"),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
@@ -159,11 +159,12 @@
 		android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
 	}
 
-	android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
 	android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
 	android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
 	android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths)
-	android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths)
+
+	android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
 }
 
 func TestPlatformBootclasspathDependencies(t *testing.T) {
@@ -194,6 +195,7 @@
 
 		bootclasspath_fragment {
 			name: "art-bootclasspath-fragment",
+			image_name: "art",
 			apex_available: [
 				"com.android.art",
 			],
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 537f51d..a64c6f4 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -15,6 +15,7 @@
 package apex
 
 import (
+	"android/soong/dexpreopt"
 	"testing"
 
 	"android/soong/android"
@@ -30,6 +31,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
@@ -81,6 +83,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
diff --git a/bazel/configurability.go b/bazel/configurability.go
index f5f0913..7aaff9a 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -56,7 +56,7 @@
 	// This is consistently named "conditions_default" to mirror the Soong
 	// config variable default key in an Android.bp file, although there's no
 	// integration with Soong config variables (yet).
-	conditionsDefault = "conditions_default"
+	ConditionsDefaultConfigKey = "conditions_default"
 
 	ConditionsDefaultSelectKey = "//conditions:default"
 
@@ -72,45 +72,45 @@
 	// A map of architectures to the Bazel label of the constraint_value
 	// for the @platforms//cpu:cpu constraint_setting
 	platformArchMap = map[string]string{
-		archArm:           "//build/bazel/platforms/arch:arm",
-		archArm64:         "//build/bazel/platforms/arch:arm64",
-		archX86:           "//build/bazel/platforms/arch:x86",
-		archX86_64:        "//build/bazel/platforms/arch:x86_64",
-		conditionsDefault: ConditionsDefaultSelectKey, // The default condition of as arch select map.
+		archArm:                    "//build/bazel/platforms/arch:arm",
+		archArm64:                  "//build/bazel/platforms/arch:arm64",
+		archX86:                    "//build/bazel/platforms/arch:x86",
+		archX86_64:                 "//build/bazel/platforms/arch:x86_64",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of as arch select map.
 	}
 
 	// A map of target operating systems to the Bazel label of the
 	// constraint_value for the @platforms//os:os constraint_setting
 	platformOsMap = map[string]string{
-		osAndroid:         "//build/bazel/platforms/os:android",
-		osDarwin:          "//build/bazel/platforms/os:darwin",
-		osLinux:           "//build/bazel/platforms/os:linux",
-		osLinuxMusl:       "//build/bazel/platforms/os:linux_musl",
-		osLinuxBionic:     "//build/bazel/platforms/os:linux_bionic",
-		osWindows:         "//build/bazel/platforms/os:windows",
-		conditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
+		osAndroid:                  "//build/bazel/platforms/os:android",
+		osDarwin:                   "//build/bazel/platforms/os:darwin",
+		osLinux:                    "//build/bazel/platforms/os:linux",
+		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
+		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
+		osWindows:                  "//build/bazel/platforms/os:windows",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
 
 	platformBionicMap = map[string]string{
-		"bionic":          "//build/bazel/platforms/os:bionic",
-		conditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
+		"bionic":                   "//build/bazel/platforms/os:bionic",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
 
 	platformOsArchMap = map[string]string{
-		osArchAndroidArm:        "//build/bazel/platforms/os_arch:android_arm",
-		osArchAndroidArm64:      "//build/bazel/platforms/os_arch:android_arm64",
-		osArchAndroidX86:        "//build/bazel/platforms/os_arch:android_x86",
-		osArchAndroidX86_64:     "//build/bazel/platforms/os_arch:android_x86_64",
-		osArchDarwinX86_64:      "//build/bazel/platforms/os_arch:darwin_x86_64",
-		osArchLinuxX86:          "//build/bazel/platforms/os_arch:linux_glibc_x86",
-		osArchLinuxX86_64:       "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
-		osArchLinuxMuslX86:      "//build/bazel/platforms/os_arch:linux_musl_x86",
-		osArchLinuxMuslX86_64:   "//build/bazel/platforms/os_arch:linux_musl_x86_64",
-		osArchLinuxBionicArm64:  "//build/bazel/platforms/os_arch:linux_bionic_arm64",
-		osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
-		osArchWindowsX86:        "//build/bazel/platforms/os_arch:windows_x86",
-		osArchWindowsX86_64:     "//build/bazel/platforms/os_arch:windows_x86_64",
-		conditionsDefault:       ConditionsDefaultSelectKey, // The default condition of an os select map.
+		osArchAndroidArm:           "//build/bazel/platforms/os_arch:android_arm",
+		osArchAndroidArm64:         "//build/bazel/platforms/os_arch:android_arm64",
+		osArchAndroidX86:           "//build/bazel/platforms/os_arch:android_x86",
+		osArchAndroidX86_64:        "//build/bazel/platforms/os_arch:android_x86_64",
+		osArchDarwinX86_64:         "//build/bazel/platforms/os_arch:darwin_x86_64",
+		osArchLinuxX86:             "//build/bazel/platforms/os_arch:linux_glibc_x86",
+		osArchLinuxX86_64:          "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
+		osArchLinuxMuslX86:         "//build/bazel/platforms/os_arch:linux_musl_x86",
+		osArchLinuxMuslX86_64:      "//build/bazel/platforms/os_arch:linux_musl_x86_64",
+		osArchLinuxBionicArm64:     "//build/bazel/platforms/os_arch:linux_bionic_arm64",
+		osArchLinuxBionicX86_64:    "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
+		osArchWindowsX86:           "//build/bazel/platforms/os_arch:windows_x86",
+		osArchWindowsX86_64:        "//build/bazel/platforms/os_arch:windows_x86_64",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
 )
 
@@ -181,7 +181,7 @@
 	case bionic:
 		return platformBionicMap[config]
 	case productVariables:
-		if config == conditionsDefault {
+		if config == ConditionsDefaultConfigKey {
 			return ConditionsDefaultSelectKey
 		}
 		return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 52c6c2f..131f0ec 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -6,8 +6,9 @@
 )
 
 var (
-	GetOutputFiles = &getOutputFilesRequestType{}
-	GetCcInfo      = &getCcInfoType{}
+	GetOutputFiles  = &getOutputFilesRequestType{}
+	GetPythonBinary = &getPythonBinaryRequestType{}
+	GetCcInfo       = &getCcInfoType{}
 )
 
 type CcInfo struct {
@@ -28,6 +29,8 @@
 
 type getOutputFilesRequestType struct{}
 
+type getPythonBinaryRequestType 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 getOutputFilesRequestType) Name() string {
@@ -53,6 +56,31 @@
 	return splitOrEmpty(rawString, ", ")
 }
 
+// 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 getPythonBinaryRequestType) Name() string {
+	return "getPythonBinary"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information.
+// The function should have the following properties:
+//   - `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 getPythonBinaryRequestType) StarlarkFunctionBody() string {
+	return "return providers(target)['FilesToRunProvider'].executable.path"
+}
+
+// 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 getPythonBinaryRequestType) ParseResult(rawString string) string {
+	return rawString
+}
+
 type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 035544e..49019ab 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -37,6 +37,31 @@
 	}
 }
 
+func TestGetPythonBinaryParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput string
+	}{
+		{
+			description:    "no result",
+			input:          "",
+			expectedOutput: "",
+		},
+		{
+			description:    "one result",
+			input:          "test",
+			expectedOutput: "test",
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetPythonBinary.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
diff --git a/bazel/properties.go b/bazel/properties.go
index 2656bad..1a846ba 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -69,6 +69,23 @@
 	Excludes []Label
 }
 
+func (ll *LabelList) Equals(other LabelList) bool {
+	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
+		return false
+	}
+	for i, _ := range ll.Includes {
+		if ll.Includes[i] != other.Includes[i] {
+			return false
+		}
+	}
+	for i, _ := range ll.Excludes {
+		if ll.Excludes[i] != other.Excludes[i] {
+			return false
+		}
+	}
+	return true
+}
+
 func (ll *LabelList) IsNil() bool {
 	return ll.Includes == nil && ll.Excludes == nil
 }
@@ -446,7 +463,7 @@
 // HasConfigurableValues returns whether there are configurable values within this set of selects.
 func (ll labelListSelectValues) HasConfigurableValues() bool {
 	for _, v := range ll {
-		if len(v.Includes) > 0 {
+		if v.Includes != nil {
 			return true
 		}
 	}
@@ -462,6 +479,13 @@
 	// The configured attribute label list Values. Optional
 	// a map of independent configurability axes
 	ConfigurableValues configurableLabelLists
+
+	// If true, differentiate between "nil" and "empty" list. nil means that
+	// this attribute should not be specified at all, and "empty" means that
+	// the attribute should be explicitly specified as an empty list.
+	// This mode facilitates use of attribute defaults: an empty list should
+	// override the default.
+	ForceSpecifyEmptyList bool
 }
 
 type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
@@ -546,6 +570,9 @@
 // Append all values, including os and arch specific ones, from another
 // LabelListAttribute to this LabelListAttribute.
 func (lla *LabelListAttribute) Append(other LabelListAttribute) {
+	if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
+		lla.Value.Includes = []Label{}
+	}
 	lla.Value.Append(other.Value)
 	if lla.ConfigurableValues == nil {
 		lla.ConfigurableValues = make(configurableLabelLists)
@@ -595,7 +622,7 @@
 
 		// Now that the Value list is finalized for this axis, compare it with the original
 		// list, and put the difference into the default condition for the axis.
-		lla.ConfigurableValues[axis][conditionsDefault] = SubtractBazelLabelList(baseLabels, lla.Value)
+		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = SubtractBazelLabelList(baseLabels, lla.Value)
 
 		// if everything ends up without includes, just delete the axis
 		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 9ec637a..78e3a74 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -39,6 +39,7 @@
         "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
+        "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
         "sh_conversion_test.go",
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index e5dbda6..eb3a73f 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -334,9 +334,7 @@
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
 
-		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
+		registerCustomModuleForBp2buildConversion(ctx)
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
 		if errored(t, "", errs) {
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 204c519..9e0c0a1 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -85,6 +85,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         # bazel_module start
 #         "label": attr.string(),
 #         "bp2build_available": attr.bool(),
@@ -114,6 +115,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
@@ -139,6 +141,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 8dcba55..bff192f 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1454,3 +1454,213 @@
 )`},
 	})
 }
+
+func TestCcLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty at root",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "root_empty",
+	  system_shared_libs: [],
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "root_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for static variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "static_empty",
+    static: {
+				system_shared_libs: [],
+		},
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "static_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    static = {
+        "system_dynamic_deps": [],
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsSharedEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for shared variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "shared_empty",
+    shared: {
+				system_shared_libs: [],
+		},
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "shared_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    shared = {
+        "system_dynamic_deps": [],
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsSharedBionicEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for shared, bionic variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "shared_empty",
+    target: {
+        bionic: {
+            shared: {
+                system_shared_libs: [],
+            }
+        }
+		},
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "shared_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    shared = {
+        "system_dynamic_deps": [],
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsLinuxBionicEmpty(t *testing.T) {
+	// Note that this behavior is technically incorrect (it's a simplification).
+	// The correct behavior would be if bp2build wrote `system_dynamic_deps = []`
+	// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
+	// b/195791252 tracks the fix.
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for linux_bionic variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "target_linux_bionic_empty",
+    target: {
+        linux_bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "target_linux_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for bionic variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "target_bionic_empty",
+    target: {
+        bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "target_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs set for shared and root",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {name: "libc"}
+cc_library {name: "libm"}
+
+cc_library {
+    name: "foo",
+    system_shared_libs: ["libc"],
+    shared: {
+				system_shared_libs: ["libm"],
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    shared = {
+        "system_dynamic_deps": [":libm"],
+    },
+    system_dynamic_deps = [":libc"],
+)`, `cc_library(
+    name = "libc",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+)`, `cc_library(
+    name = "libm",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+)`},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 1dc6713..d9145f6 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -73,6 +73,8 @@
 	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+	// Required for system_shared_libs dependencies.
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
 }
 
 func runCcLibraryStaticTestCase(t *testing.T, tc bp2buildTestCase) {
@@ -1427,3 +1429,185 @@
 )`},
 	})
 }
+
+func TestStaticLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty root",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "root_empty",
+	  system_shared_libs: [],
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "root_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty static default",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_defaults {
+    name: "static_empty_defaults",
+    static: {
+				system_shared_libs: [],
+		},
+}
+cc_library_static {
+    name: "static_empty",
+	  defaults: ["static_empty_defaults"],
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "static_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty for bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "target_bionic_empty",
+    target: {
+        bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxBionicEmpty(t *testing.T) {
+	// Note that this behavior is technically incorrect (it's a simplification).
+	// The correct behavior would be if bp2build wrote `system_dynamic_deps = []`
+	// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
+	// b/195791252 tracks the fix.
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty for linux_bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "target_linux_bionic_empty",
+    target: {
+        linux_bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_linux_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsBionic(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_libs set for bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library{name: "libc"}
+
+cc_library_static {
+    name: "target_bionic",
+    target: {
+        bionic: {
+            system_shared_libs: ["libc"],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_bionic",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = select({
+        "//build/bazel/platforms/os:bionic": [":libc"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxRootAndLinuxBionic(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_libs set for root and linux_bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library{name: "libc"}
+cc_library{name: "libm"}
+
+cc_library_static {
+    name: "target_linux_bionic",
+		system_shared_libs: ["libc"],
+    target: {
+        linux_bionic: {
+            system_shared_libs: ["libm"],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_linux_bionic",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [":libc"] + select({
+        "//build/bazel/platforms/os:linux_bionic": [":libm"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index c8105eb..005f13d 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -82,7 +82,12 @@
 			continue
 		}
 		archSelects := map[string]reflect.Value{}
+		defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
 		for config, labels := range configToLabels {
+			// Omit any entries in the map which match the default value, for brevity.
+			if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
+				continue
+			}
 			selectKey := axis.SelectKey(config)
 			if use, value := labelListSelectValue(selectKey, labels); use {
 				archSelects[selectKey] = value
@@ -118,6 +123,8 @@
 	var value reflect.Value
 	var configurableAttrs []selects
 	var defaultSelectValue *string
+	// If true, print the default attribute value, even if the attribute is zero.
+	shouldPrintDefault := false
 	switch list := v.(type) {
 	case bazel.StringListAttribute:
 		value, configurableAttrs = getStringListValues(list)
@@ -125,6 +132,9 @@
 	case bazel.LabelListAttribute:
 		value, configurableAttrs = getLabelListValues(list)
 		defaultSelectValue = &emptyBazelList
+		if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
+			shouldPrintDefault = true
+		}
 	case bazel.LabelAttribute:
 		value, configurableAttrs = getLabelValue(list)
 		defaultSelectValue = &bazelNone
@@ -166,6 +176,9 @@
 		}
 	}
 
+	if ret == "" && shouldPrintDefault {
+		return *defaultSelectValue, nil
+	}
 	return ret, nil
 }
 
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
new file mode 100644
index 0000000..3283952
--- /dev/null
+++ b/bp2build/performance_test.go
@@ -0,0 +1,175 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+// to run the benchmarks in this file, you must run go test with the -bench.
+// The benchmarked portion will run for the specified time (can be set via -benchtime)
+// This can mean if you are benchmarking a faster portion of a larger operation, it will take
+// longer.
+// If you are seeing a small number of iterations for a specific run, the data is less reliable, to
+// run for longer, set -benchtime to a larger value.
+
+import (
+	"android/soong/android"
+	"fmt"
+	"math"
+	"strings"
+	"testing"
+)
+
+func genCustomModule(i int, convert bool) string {
+	var conversionString string
+	if convert {
+		conversionString = `bazel_module: { bp2build_available: true },`
+	}
+	return fmt.Sprintf(`
+custom {
+    name: "arch_paths_%[1]d",
+    string_list_prop: ["\t", "\n"],
+    string_prop: "a\t\n\r",
+    arch_paths: ["outer", ":outer_dep_%[1]d"],
+    arch: {
+      x86: {
+        arch_paths: ["abc", ":x86_dep_%[1]d"],
+      },
+      x86_64: {
+        arch_paths: ["64bit"],
+        arch_paths_exclude: ["outer"],
+      },
+    },
+		%[2]s
+}
+
+custom {
+    name: "outer_dep_%[1]d",
+		%[2]s
+}
+
+custom {
+    name: "x86_dep_%[1]d",
+		%[2]s
+}
+`, i, conversionString)
+}
+
+func genCustomModuleBp(pctConverted float64) string {
+	modules := 100
+
+	bp := make([]string, 0, modules)
+	toConvert := int(math.Round(float64(modules) * pctConverted))
+
+	for i := 0; i < modules; i++ {
+		bp = append(bp, genCustomModule(i, i < toConvert))
+	}
+	return strings.Join(bp, "\n\n")
+}
+
+var pctToConvert = []float64{0.0, 0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 1.0}
+
+func BenchmarkManyModulesFull(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				b.StartTimer()
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				_, errs = ctx.ResolveDependencies(config)
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				generateBazelTargetsForDir(codegenCtx, dir)
+				b.StopTimer()
+			}
+		})
+	}
+}
+
+func BenchmarkManyModulesResolveDependencies(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				b.StartTimer()
+				_, errs = ctx.ResolveDependencies(config)
+				b.StopTimer()
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				generateBazelTargetsForDir(codegenCtx, dir)
+			}
+		})
+	}
+}
+
+func BenchmarkManyModulesGenerateBazelTargetsForDir(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				_, errs = ctx.ResolveDependencies(config)
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				b.StartTimer()
+				generateBazelTargetsForDir(codegenCtx, dir)
+				b.StopTimer()
+			}
+		})
+	}
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index f3cd7f0..faf1a44 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -33,7 +33,8 @@
 	Nested_props     nestedProps
 	Nested_props_ptr *nestedProps
 
-	Arch_paths []string `android:"path,arch_variant"`
+	Arch_paths         []string `android:"path,arch_variant"`
+	Arch_paths_exclude []string `android:"path,arch_variant"`
 }
 
 type customModule struct {
@@ -155,16 +156,18 @@
 			return
 		}
 
-		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
+		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.props.Arch_paths, m.props.Arch_paths_exclude))
 
 		for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
 			for config, props := range configToProps {
 				if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
-					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
+					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
 				}
 			}
 		}
 
+		paths.ResolveExcludes()
+
 		attrs := &customBazelModuleAttributes{
 			String_prop:      m.props.String_prop,
 			String_list_prop: m.props.String_list_prop,
@@ -216,3 +219,9 @@
 	buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
 	return buildFileToTargets[dir]
 }
+
+func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
+	ctx.RegisterModuleType("custom", customModuleFactory)
+	ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
+	ctx.RegisterForBazelConversion()
+}
diff --git a/cc/Android.bp b/cc/Android.bp
index 164d32b..bff2761 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -13,6 +13,7 @@
         "soong-bazel",
         "soong-cc-config",
         "soong-etc",
+        "soong-fuzz",
         "soong-genrule",
         "soong-snapshot",
         "soong-tradefed",
@@ -59,7 +60,6 @@
         "binary.go",
         "binary_sdk_member.go",
         "fuzz.go",
-        "fuzz_common.go",
         "library.go",
         "library_headers.go",
         "library_sdk_member.go",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 68afd0d..1706d72 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -35,6 +35,8 @@
 	Static_deps        bazel.LabelListAttribute
 	Dynamic_deps       bazel.LabelListAttribute
 	Whole_archive_deps bazel.LabelListAttribute
+
+	System_dynamic_deps bazel.LabelListAttribute
 }
 
 func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
@@ -137,12 +139,19 @@
 		props = lib.SharedProperties.Shared
 	}
 
+	system_dynamic_deps := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.System_shared_libs))
+	system_dynamic_deps.ForceSpecifyEmptyList = true
+	if system_dynamic_deps.IsEmpty() && props.System_shared_libs != nil {
+		system_dynamic_deps.Value.Includes = []bazel.Label{}
+	}
+
 	attrs := staticOrSharedAttributes{
-		Copts:              bazel.StringListAttribute{Value: props.Cflags},
-		Srcs:               bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
-		Static_deps:        bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
-		Dynamic_deps:       bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, append(props.Shared_libs, props.System_shared_libs...))),
-		Whole_archive_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)),
+		Copts:               bazel.StringListAttribute{Value: props.Cflags},
+		Srcs:                bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
+		Static_deps:         bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
+		Dynamic_deps:        bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
+		Whole_archive_deps:  bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)),
+		System_dynamic_deps: system_dynamic_deps,
 	}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
@@ -151,6 +160,7 @@
 		attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
 		attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
 		attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
+		attrs.System_dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.System_shared_libs))
 	}
 
 	if isStatic {
@@ -230,6 +240,8 @@
 	// C++ options and sources
 	cppFlags bazel.StringListAttribute
 	srcs     bazel.LabelListAttribute
+
+	rtti bazel.BoolAttribute
 }
 
 // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
@@ -239,6 +251,7 @@
 	var asFlags bazel.StringListAttribute
 	var conlyFlags bazel.StringListAttribute
 	var cppFlags bazel.StringListAttribute
+	var rtti bazel.BoolAttribute
 
 	// Creates the -I flags for a directory, while making the directory relative
 	// to the exec root for Bazel to work.
@@ -292,6 +305,7 @@
 			asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags)
 			conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
 			cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags)
+			rtti.Value = baseCompilerProps.Rtti
 
 			for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
 				copts.Value = append(copts.Value, includeFlags(dir)...)
@@ -335,6 +349,7 @@
 				asFlags.SetSelectValue(axis, config, archVariantAsflags)
 				conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
 				cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
+				rtti.SetSelectValue(axis, config, baseCompilerProps.Rtti)
 			}
 		}
 	}
@@ -370,6 +385,7 @@
 		cSrcs:      cSrcs,
 		conlyFlags: conlyFlags,
 		cppFlags:   cppFlags,
+		rtti:       rtti,
 	}
 }
 
@@ -377,6 +393,7 @@
 type linkerAttributes struct {
 	deps                          bazel.LabelListAttribute
 	dynamicDeps                   bazel.LabelListAttribute
+	systemDynamicDeps             bazel.LabelListAttribute
 	wholeArchiveDeps              bazel.LabelListAttribute
 	exportedDeps                  bazel.LabelListAttribute
 	useLibcrt                     bazel.BoolAttribute
@@ -406,6 +423,7 @@
 	var exportedDeps bazel.LabelListAttribute
 	var dynamicDeps bazel.LabelListAttribute
 	var wholeArchiveDeps bazel.LabelListAttribute
+	var systemSharedDeps bazel.LabelListAttribute
 	var linkopts bazel.StringListAttribute
 	var versionScript bazel.LabelAttribute
 	var useLibcrt bazel.BoolAttribute
@@ -445,9 +463,16 @@
 			staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)
 			wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
 			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
-			// TODO(b/186024507): Handle system_shared_libs as its own attribute, so that the appropriate default
-			// may be supported.
-			sharedLibs := android.FirstUniqueStrings(append(baseLinkerProps.Shared_libs, baseLinkerProps.System_shared_libs...))
+
+			systemSharedLibs := android.FirstUniqueStrings(baseLinkerProps.System_shared_libs)
+			systemSharedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, systemSharedLibs))
+			systemSharedDeps.ForceSpecifyEmptyList = true
+			if systemSharedDeps.Value.IsNil() && baseLinkerProps.System_shared_libs != nil {
+				systemSharedDeps.Value.Includes = []bazel.Label{}
+			}
+
+			sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+
 			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
 			headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
@@ -474,7 +499,14 @@
 				staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
 				wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
 				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
-				sharedLibs := android.FirstUniqueStrings(append(baseLinkerProps.Shared_libs, baseLinkerProps.System_shared_libs...))
+
+				systemSharedLibs := android.FirstUniqueStrings(baseLinkerProps.System_shared_libs)
+				if len(systemSharedLibs) == 0 && baseLinkerProps.System_shared_libs != nil {
+					systemSharedLibs = baseLinkerProps.System_shared_libs
+				}
+				systemSharedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, systemSharedLibs))
+
+				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
 				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
 				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
@@ -550,13 +582,14 @@
 	headerDeps.Append(staticDeps)
 
 	return linkerAttributes{
-		deps:             headerDeps,
-		exportedDeps:     exportedDeps,
-		dynamicDeps:      dynamicDeps,
-		wholeArchiveDeps: wholeArchiveDeps,
-		linkopts:         linkopts,
-		useLibcrt:        useLibcrt,
-		versionScript:    versionScript,
+		deps:              headerDeps,
+		exportedDeps:      exportedDeps,
+		dynamicDeps:       dynamicDeps,
+		systemDynamicDeps: systemSharedDeps,
+		wholeArchiveDeps:  wholeArchiveDeps,
+		linkopts:          linkopts,
+		useLibcrt:         useLibcrt,
+		versionScript:     versionScript,
 
 		// Strip properties
 		stripKeepSymbols:              stripKeepSymbols,
diff --git a/cc/cc.go b/cc/cc.go
index 39d89e5..f65af30 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -29,6 +29,7 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/fuzz"
 	"android/soong/genrule"
 )
 
@@ -589,17 +590,6 @@
 	installInRoot() bool
 }
 
-// bazelHandler is the interface for a helper object related to deferring to Bazel for
-// processing a module (during Bazel mixed builds). Individual module types should define
-// their own bazel handler if they support deferring to Bazel.
-type bazelHandler interface {
-	// Issue query to Bazel to retrieve information about Bazel's view of the current module.
-	// If Bazel returns this information, set module properties on the current module to reflect
-	// the returned information.
-	// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
-	generateBazelBuildActions(ctx android.ModuleContext, label string) bool
-}
-
 type xref interface {
 	XrefCcFiles() android.Paths
 }
@@ -773,7 +763,7 @@
 // members of the cc.Module to this decorator. Thus, a cc_binary module has custom linker and
 // installer logic.
 type Module struct {
-	FuzzModule
+	fuzz.FuzzModule
 
 	android.SdkBase
 	android.BazelModuleBase
@@ -796,7 +786,7 @@
 	compiler     compiler
 	linker       linker
 	installer    installer
-	bazelHandler bazelHandler
+	bazelHandler android.BazelHandler
 
 	features []feature
 	stl      *stl
@@ -1696,7 +1686,7 @@
 	bazelModuleLabel := c.GetBazelLabel(actx, c)
 	bazelActionsUsed := false
 	if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil {
-		bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel)
+		bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel)
 	}
 	return bazelActionsUsed
 }
@@ -3426,7 +3416,7 @@
 		&TestProperties{},
 		&TestBinaryProperties{},
 		&BenchmarkProperties{},
-		&FuzzProperties{},
+		&fuzz.FuzzProperties{},
 		&StlProperties{},
 		&SanitizeProperties{},
 		&StripProperties{},
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 812a245..2d6bcb8 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -96,31 +96,25 @@
 	pctx.SourcePathVariable("Arm64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
 
-	pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
-	pctx.StaticVariable("Arm64Lldflags", strings.Join(arm64Lldflags, " "))
+	exportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
+	exportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
 
-	pctx.StaticVariable("Arm64Cflags", strings.Join(arm64Cflags, " "))
-	pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " "))
+	exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
+	exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
+
+	exportedStringListDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflags)
+	exportedStringListDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflags)
 
 	pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
 	pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
 	pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
 	pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
 
-	pctx.StaticVariable("Arm64CortexA53Cflags",
-		strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
-
-	pctx.StaticVariable("Arm64CortexA55Cflags",
-		strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
-
-	pctx.StaticVariable("Arm64KryoCflags",
-		strings.Join(arm64CpuVariantCflags["kryo"], " "))
-
-	pctx.StaticVariable("Arm64ExynosM1Cflags",
-		strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
-
-	pctx.StaticVariable("Arm64ExynosM2Cflags",
-		strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
+	pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
+	pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
+	pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
+	pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
 }
 
 var (
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index b5afe40..0fe5e68 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -188,6 +188,9 @@
 	exportStringListStaticVariable("ArmArmCflags", armArmCflags)
 	exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
 
+	exportedStringListDictVars.Set("ArmArchVariantCflags", armArchVariantCflags)
+	exportedStringListDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflags)
+
 	// Clang arch variant cflags
 	exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
 	exportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index e7e94a8..d19f5ac 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -15,98 +15,182 @@
 package config
 
 import (
-	"android/soong/android"
 	"fmt"
 	"regexp"
+	"sort"
 	"strings"
 )
 
+const (
+	bazelIndent = 4
+)
+
+type bazelVarExporter interface {
+	asBazel(exportedStringVariables, exportedStringListVariables) []bazelConstant
+}
+
 // Helpers for exporting cc configuration information to Bazel.
 var (
 	// Map containing toolchain variables that are independent of the
 	// environment variables of the build.
-	exportedStringListVars = exportedStringListVariables{}
-	exportedStringVars     = exportedStringVariables{}
+	exportedStringListVars     = exportedStringListVariables{}
+	exportedStringVars         = exportedStringVariables{}
+	exportedStringListDictVars = exportedStringListDictVariables{}
 )
 
+// Ensure that string s has no invalid characters to be generated into the bzl file.
+func validateCharacters(s string) string {
+	for _, c := range []string{`\n`, `"`, `\`} {
+		if strings.Contains(s, c) {
+			panic(fmt.Errorf("%s contains illegal character %s", s, c))
+		}
+	}
+	return s
+}
+
+type bazelConstant struct {
+	variableName       string
+	internalDefinition string
+}
+
 type exportedStringVariables map[string]string
-type exportedStringListVariables map[string][]string
 
 func (m exportedStringVariables) Set(k string, v string) {
 	m[k] = v
 }
 
+func bazelIndention(level int) string {
+	return strings.Repeat(" ", level*bazelIndent)
+}
+
+func printBazelList(items []string, indentLevel int) string {
+	list := make([]string, 0, len(items)+2)
+	list = append(list, "[")
+	innerIndent := bazelIndention(indentLevel + 1)
+	for _, item := range items {
+		list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
+	}
+	list = append(list, bazelIndention(indentLevel)+"]")
+	return strings.Join(list, "\n")
+}
+
+func (m exportedStringVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for k, variableValue := range m {
+		expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
+		if len(expandedVar) > 1 {
+			panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
+		}
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
+		})
+	}
+	return ret
+}
+
 // Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
 func exportStringStaticVariable(name string, value string) {
 	pctx.StaticVariable(name, value)
 	exportedStringVars.Set(name, value)
 }
 
+type exportedStringListVariables map[string][]string
+
 func (m exportedStringListVariables) Set(k string, v []string) {
 	m[k] = v
 }
 
+func (m exportedStringListVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	// For each exported variable, recursively expand elements in the variableValue
+	// list to ensure that interpolated variables are expanded according to their values
+	// in the variable scope.
+	for k, variableValue := range m {
+		var expandedVars []string
+		for _, v := range variableValue {
+			expandedVars = append(expandedVars, expandVar(v, stringScope, stringListScope)...)
+		}
+		// Assign the list as a bzl-private variable; this variable will be exported
+		// out through a constants struct later.
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: printBazelList(expandedVars, 0),
+		})
+	}
+	return ret
+}
+
 // Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
 func exportStringListStaticVariable(name string, value []string) {
 	pctx.StaticVariable(name, strings.Join(value, " "))
 	exportedStringListVars.Set(name, value)
 }
 
+type exportedStringListDictVariables map[string]map[string][]string
+
+func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
+	m[k] = v
+}
+
+func printBazelStringListDict(dict map[string][]string) string {
+	bazelDict := make([]string, 0, len(dict)+2)
+	bazelDict = append(bazelDict, "{")
+	for k, v := range dict {
+		bazelDict = append(bazelDict,
+			fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
+	}
+	bazelDict = append(bazelDict, "}")
+	return strings.Join(bazelDict, "\n")
+}
+
+// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
+func (m exportedStringListDictVariables) asBazel(_ exportedStringVariables, _ exportedStringListVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for k, dict := range m {
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: printBazelStringListDict(dict),
+		})
+	}
+	return ret
+}
+
 // BazelCcToolchainVars generates bzl file content containing variables for
 // Bazel's cc_toolchain configuration.
 func BazelCcToolchainVars() string {
+	return bazelToolchainVars(
+		exportedStringListDictVars,
+		exportedStringListVars,
+		exportedStringVars)
+}
+
+func bazelToolchainVars(vars ...bazelVarExporter) string {
 	ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
 
-	// Ensure that string s has no invalid characters to be generated into the bzl file.
-	validateCharacters := func(s string) string {
-		for _, c := range []string{`\n`, `"`, `\`} {
-			if strings.Contains(s, c) {
-				panic(fmt.Errorf("%s contains illegal character %s", s, c))
-			}
-		}
-		return s
+	results := []bazelConstant{}
+	for _, v := range vars {
+		results = append(results, v.asBazel(exportedStringVars, exportedStringListVars)...)
 	}
 
-	// For each exported variable, recursively expand elements in the variableValue
-	// list to ensure that interpolated variables are expanded according to their values
-	// in the variable scope.
-	for _, k := range android.SortedStringKeys(exportedStringListVars) {
-		variableValue := exportedStringListVars[k]
-		var expandedVars []string
-		for _, v := range variableValue {
-			expandedVars = append(expandedVars, expandVar(v, exportedStringVars, exportedStringListVars)...)
-		}
-		// Build the list for this variable.
-		list := "["
-		for _, flag := range expandedVars {
-			list += fmt.Sprintf("\n    \"%s\",", validateCharacters(flag))
-		}
-		list += "\n]"
-		// Assign the list as a bzl-private variable; this variable will be exported
-		// out through a constants struct later.
-		ret += fmt.Sprintf("_%s = %s\n", k, list)
-		ret += "\n"
-	}
+	sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
 
-	for _, k := range android.SortedStringKeys(exportedStringVars) {
-		variableValue := exportedStringVars[k]
-		expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
-		if len(expandedVar) > 1 {
-			panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
-		}
-		ret += fmt.Sprintf("_%s = \"%s\"\n", k, validateCharacters(expandedVar[0]))
-		ret += "\n"
+	definitions := make([]string, 0, len(results))
+	constants := make([]string, 0, len(results))
+	for _, b := range results {
+		definitions = append(definitions,
+			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
+		constants = append(constants,
+			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
 	}
 
 	// Build the exported constants struct.
+	ret += strings.Join(definitions, "\n\n")
+	ret += "\n\n"
 	ret += "constants = struct(\n"
-	for _, k := range android.SortedStringKeys(exportedStringVars) {
-		ret += fmt.Sprintf("    %s = _%s,\n", k, k)
-	}
-	for _, k := range android.SortedStringKeys(exportedStringListVars) {
-		ret += fmt.Sprintf("    %s = _%s,\n", k, k)
-	}
-	ret += ")"
+	ret += strings.Join(constants, "\n")
+	ret += "\n)"
+
 	return ret
 }
 
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index a4745e6..883597a 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -115,3 +115,143 @@
 		})
 	}
 }
+
+func TestBazelToolchainVars(t *testing.T) {
+	testCases := []struct {
+		name        string
+		vars        []bazelVarExporter
+		expectedOut string
+	}{
+		{
+			name: "exports strings",
+			vars: []bazelVarExporter{
+				exportedStringVariables{
+					"a": "b",
+					"c": "d",
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = "b"
+
+_c = "d"
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports string lists",
+			vars: []bazelVarExporter{
+				exportedStringListVariables{
+					"a": []string{"b1", "b2"},
+					"c": []string{"d1", "d2"},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = [
+    "b1",
+    "b2",
+]
+
+_c = [
+    "d1",
+    "d2",
+]
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports string lists dicts",
+			vars: []bazelVarExporter{
+				exportedStringListDictVariables{
+					"a": map[string][]string{"b1": []string{"b2"}},
+					"c": map[string][]string{"d1": []string{"d2"}},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+    "b1": [
+        "b2",
+    ],
+}
+
+_c = {
+    "d1": [
+        "d2",
+    ],
+}
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "sorts across types",
+			vars: []bazelVarExporter{
+				exportedStringVariables{
+					"b": "b-val",
+					"d": "d-val",
+				},
+				exportedStringListVariables{
+					"c": []string{"c-val"},
+					"e": []string{"e-val"},
+				},
+				exportedStringListDictVariables{
+					"a": map[string][]string{"a1": []string{"a2"}},
+					"f": map[string][]string{"f1": []string{"f2"}},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+    "a1": [
+        "a2",
+    ],
+}
+
+_b = "b-val"
+
+_c = [
+    "c-val",
+]
+
+_d = "d-val"
+
+_e = [
+    "e-val",
+]
+
+_f = {
+    "f1": [
+        "f2",
+    ],
+}
+
+constants = struct(
+    a = _a,
+    b = _b,
+    c = _c,
+    d = _d,
+    e = _e,
+    f = _f,
+)`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			out := bazelToolchainVars(tc.vars...)
+			if out != tc.expectedOut {
+				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
+			}
+		})
+	}
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 55e0d79..9773345 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,7 +46,6 @@
 
 		"-O2",
 		"-g",
-		"-fdebug-default-version=5",
 		"-fdebug-info-for-profiling",
 
 		"-fno-strict-aliasing",
@@ -229,6 +228,8 @@
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 		// New warnings to be fixed after clang-r407598
 		"-Wno-string-concatenation", // http://b/175068488
+		// New warnings to be fixed after clang-r428724
+		"-Wno-align-mismatch", // http://b/193679946
 	}
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -267,8 +268,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r416183b1"
-	ClangDefaultShortVersion = "12.0.7"
+	ClangDefaultVersion      = "clang-r428724"
+	ClangDefaultShortVersion = "13.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -354,6 +355,9 @@
 	exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes)
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
 
+	exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+	exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
+
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
 	pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_PREBUILTS_BASE"); override != "" {
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index c4f47a7..00f07ff 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -77,6 +77,14 @@
 		"popcnt": []string{"-mpopcnt"},
 		"aes_ni": []string{"-maes"},
 	}
+
+	x86_64DefaultArchVariantFeatures = []string{
+		"ssse3",
+		"sse4",
+		"sse4_1",
+		"sse4_2",
+		"popcnt",
+	}
 )
 
 const (
@@ -84,37 +92,38 @@
 )
 
 func init() {
-	android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64,
-		"ssse3",
-		"sse4",
-		"sse4_1",
-		"sse4_2",
-		"popcnt")
+	android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64, x86_64DefaultArchVariantFeatures...)
+	exportedStringListVars.Set("X86_64DefaultArchVariantFeatures", x86_64DefaultArchVariantFeatures)
 
 	pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
 
 	pctx.SourcePathVariable("X86_64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
 
-	pctx.StaticVariable("X86_64ToolchainCflags", "-m64")
-	pctx.StaticVariable("X86_64ToolchainLdflags", "-m64")
+	exportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
+	exportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
 
-	pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
-	pctx.StaticVariable("X86_64Lldflags", strings.Join(x86_64Ldflags, " "))
+	exportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
+	exportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
 
 	// Clang cflags
-	pctx.StaticVariable("X86_64Cflags", strings.Join(x86_64Cflags, " "))
-	pctx.StaticVariable("X86_64Cppflags", strings.Join(x86_64Cppflags, " "))
+	exportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
+	exportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
 
 	// Yasm flags
-	pctx.StaticVariable("X86_64YasmFlags", "-f elf64 -m amd64")
+	exportStringListStaticVariable("X86_64YasmFlags", []string{
+		"-f elf64",
+		"-m amd64",
+	})
 
 	// Extended cflags
 
+	exportedStringListDictVars.Set("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
+	exportedStringListDictVars.Set("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
+
 	// Architecture variant cflags
 	for variant, cflags := range x86_64ArchVariantCflags {
-		pctx.StaticVariable("X86_64"+variant+"VariantCflags",
-			strings.Join(cflags, " "))
+		pctx.StaticVariable("X86_64"+variant+"VariantCflags", strings.Join(cflags, " "))
 	}
 }
 
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 5e510a4..29f0593 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -97,25 +97,29 @@
 	pctx.SourcePathVariable("X86GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
 
-	pctx.StaticVariable("X86ToolchainCflags", "-m32")
-	pctx.StaticVariable("X86ToolchainLdflags", "-m32")
+	exportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
+	exportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
 
-	pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
-	pctx.StaticVariable("X86Lldflags", strings.Join(x86Ldflags, " "))
+	exportStringListStaticVariable("X86Ldflags", x86Ldflags)
+	exportStringListStaticVariable("X86Lldflags", x86Ldflags)
 
 	// Clang cflags
-	pctx.StaticVariable("X86Cflags", strings.Join(x86Cflags, " "))
-	pctx.StaticVariable("X86Cppflags", strings.Join(x86Cppflags, " "))
+	exportStringListStaticVariable("X86Cflags", x86Cflags)
+	exportStringListStaticVariable("X86Cppflags", x86Cppflags)
 
 	// Yasm flags
-	pctx.StaticVariable("X86YasmFlags", "-f elf32 -m x86")
+	exportStringListStaticVariable("X86YasmFlags", []string{
+		"-f elf32",
+		"-m x86",
+	})
 
 	// Extended cflags
+	exportedStringListDictVars.Set("X86ArchVariantCflags", x86ArchVariantCflags)
+	exportedStringListDictVars.Set("X86ArchFeatureCflags", x86ArchFeatureCflags)
 
 	// Architecture variant cflags
 	for variant, cflags := range x86ArchVariantCflags {
-		pctx.StaticVariable("X86"+variant+"VariantCflags",
-			strings.Join(cflags, " "))
+		pctx.StaticVariable("X86"+variant+"VariantCflags", strings.Join(cflags, " "))
 	}
 }
 
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 1d66cb7..e7fcfed 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -110,7 +110,7 @@
 	muslCrtBeginSharedBinary, muslCrtEndSharedBinary   = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}, []string{"libc_musl_crtend"}
 	muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
 
-	muslDefaultSharedLibraries = []string{"libjemalloc5", "libc_musl"}
+	muslDefaultSharedLibraries = []string{"libc_musl"}
 )
 
 const (
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8b0f93e..fbef12b 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -15,7 +15,6 @@
 package cc
 
 import (
-	"encoding/json"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -24,55 +23,9 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/fuzz"
 )
 
-type FuzzConfig struct {
-	// Email address of people to CC on bugs or contact about this fuzz target.
-	Cc []string `json:"cc,omitempty"`
-	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
-	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
-	// Specify whether to enable continuous fuzzing on host. Defaults to true.
-	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
-	// Component in Google's bug tracking system that bugs should be filed to.
-	Componentid *int64 `json:"componentid,omitempty"`
-	// Hotlists in Google's bug tracking system that bugs should be marked with.
-	Hotlists []string `json:"hotlists,omitempty"`
-	// Specify whether this fuzz target was submitted by a researcher. Defaults
-	// to false.
-	Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
-	// Specify who should be acknowledged for CVEs in the Android Security
-	// Bulletin.
-	Acknowledgement []string `json:"acknowledgement,omitempty"`
-	// Additional options to be passed to libfuzzer when run in Haiku.
-	Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
-	// Additional options to be passed to HWASAN when running on-device in Haiku.
-	Hwasan_options []string `json:"hwasan_options,omitempty"`
-	// Additional options to be passed to HWASAN when running on host in Haiku.
-	Asan_options []string `json:"asan_options,omitempty"`
-}
-
-func (f *FuzzConfig) String() string {
-	b, err := json.Marshal(f)
-	if err != nil {
-		panic(err)
-	}
-
-	return string(b)
-}
-
-type FuzzProperties struct {
-	// Optional list of seed files to be installed to the fuzz target's output
-	// directory.
-	Corpus []string `android:"path"`
-	// Optional list of data files to be installed to the fuzz target's output
-	// directory. Directory structure relative to the module is preserved.
-	Data []string `android:"path"`
-	// Optional dictionary to be installed to the fuzz target's output directory.
-	Dictionary *string `android:"path"`
-	// Config for running the target on fuzzing infrastructure.
-	Fuzz_config *FuzzConfig
-}
-
 func init() {
 	android.RegisterModuleType("cc_fuzz", FuzzFactory)
 	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
@@ -94,7 +47,7 @@
 	*binaryDecorator
 	*baseCompiler
 
-	fuzzPackagedModule FuzzPackagedModule
+	fuzzPackagedModule fuzz.FuzzPackagedModule
 
 	installedSharedDeps []string
 }
@@ -355,7 +308,7 @@
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
 type ccFuzzPackager struct {
-	FuzzPackager
+	fuzz.FuzzPackager
 	sharedLibInstallStrings []string
 }
 
@@ -367,7 +320,7 @@
 	// Map between each architecture + host/device combination, and the files that
 	// need to be packaged (in the tuple of {source file, destination folder in
 	// archive}).
-	archDirs := make(map[ArchOs][]FileToZip)
+	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
 	// Map tracking whether each shared library has an install rule to avoid duplicate install rules from
 	// multiple fuzzers that depend on the same shared library.
@@ -384,7 +337,7 @@
 		}
 
 		// Discard non-fuzz targets.
-		if ok := IsValid(ccModule.FuzzModule); !ok {
+		if ok := fuzz.IsValid(ccModule.FuzzModule); !ok {
 			return
 		}
 
@@ -400,12 +353,12 @@
 
 		archString := ccModule.Arch().ArchType.String()
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
-		archOs := ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
 		// Grab the list of required shared libraries.
 		sharedLibraries := collectAllSharedDependencies(ctx, module)
 
-		var files []FileToZip
+		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
 		// Package the corpus, data, dict and config into a zipfile.
@@ -414,7 +367,7 @@
 		// Find and mark all the transiently-dependent shared libraries for
 		// packaging.
 		for _, library := range sharedLibraries {
-			files = append(files, FileToZip{library, "lib"})
+			files = append(files, fuzz.FileToZip{library, "lib"})
 
 			// For each architecture-specific shared library dependency, we need to
 			// install it to the output directory. Setup the install destination here,
@@ -446,7 +399,7 @@
 		}
 
 		// The executable.
-		files = append(files, FileToZip{ccModule.UnstrippedOutputFile(), ""})
+		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
@@ -454,7 +407,7 @@
 		}
 	})
 
-	s.CreateFuzzPackage(ctx, archDirs, Cc)
+	s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
 
 }
 
diff --git a/cc/library.go b/cc/library.go
index 6c7102e..84cd0d7 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -23,13 +23,12 @@
 	"strings"
 	"sync"
 
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
-
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
 // LibraryProperties is a collection of properties shared by cc library rules.
@@ -233,9 +232,11 @@
 	Implementation_deps bazel.LabelListAttribute
 	Dynamic_deps        bazel.LabelListAttribute
 	Whole_archive_deps  bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
 	Includes            bazel.StringListAttribute
 	Linkopts            bazel.StringListAttribute
 	Use_libcrt          bazel.BoolAttribute
+	Rtti                bazel.BoolAttribute
 
 	// This is shared only.
 	Version_script bazel.LabelAttribute
@@ -319,9 +320,11 @@
 		Deps:                linkerAttrs.exportedDeps,
 		Dynamic_deps:        linkerAttrs.dynamicDeps,
 		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
 		Includes:            exportedIncludes,
 		Linkopts:            linkerAttrs.linkopts,
 		Use_libcrt:          linkerAttrs.useLibcrt,
+		Rtti:                compilerAttrs.rtti,
 
 		Version_script: linkerAttrs.versionScript,
 
@@ -553,7 +556,7 @@
 }
 
 type ccLibraryBazelHandler struct {
-	bazelHandler
+	android.BazelHandler
 
 	module *Module
 }
@@ -639,7 +642,7 @@
 	return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
 }
 
-func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
 	if err != nil {
@@ -2329,9 +2332,12 @@
 	Implementation_deps bazel.LabelListAttribute
 	Deps                bazel.LabelListAttribute
 	Whole_archive_deps  bazel.LabelListAttribute
+	Dynamic_deps        bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
 	Linkopts            bazel.StringListAttribute
 	Linkstatic          bool
 	Use_libcrt          bazel.BoolAttribute
+	Rtti                bazel.BoolAttribute
 	Includes            bazel.StringListAttribute
 	Hdrs                bazel.LabelListAttribute
 
@@ -2340,6 +2346,8 @@
 	Conlyflags bazel.StringListAttribute
 	Srcs_as    bazel.LabelListAttribute
 	Asflags    bazel.StringListAttribute
+
+	Static staticOrSharedAttributes
 }
 
 type bazelCcLibraryStatic struct {
@@ -2365,16 +2373,33 @@
 		asFlags = bazel.MakeStringListAttribute(nil)
 	}
 
+	// Append static{} stanza properties. These won't be specified on
+	// cc_library_static itself, but may be specified in cc_defaults that this module
+	// depends on.
+	staticAttrs := bp2BuildParseStaticProps(ctx, module)
+
+	compilerAttrs.srcs.Append(staticAttrs.Srcs)
+	compilerAttrs.cSrcs.Append(staticAttrs.Srcs_c)
+	compilerAttrs.asSrcs.Append(staticAttrs.Srcs_as)
+	compilerAttrs.copts.Append(staticAttrs.Copts)
+	linkerAttrs.exportedDeps.Append(staticAttrs.Static_deps)
+	linkerAttrs.dynamicDeps.Append(staticAttrs.Dynamic_deps)
+	linkerAttrs.wholeArchiveDeps.Append(staticAttrs.Whole_archive_deps)
+	linkerAttrs.systemDynamicDeps.Append(staticAttrs.System_dynamic_deps)
+
 	attrs := &bazelCcLibraryStaticAttributes{
 		Copts:               compilerAttrs.copts,
 		Srcs:                compilerAttrs.srcs,
 		Implementation_deps: linkerAttrs.deps,
 		Deps:                linkerAttrs.exportedDeps,
 		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+		Dynamic_deps:        linkerAttrs.dynamicDeps,
+		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
 
 		Linkopts:   linkerAttrs.linkopts,
 		Linkstatic: true,
 		Use_libcrt: linkerAttrs.useLibcrt,
+		Rtti:       compilerAttrs.rtti,
 		Includes:   exportedIncludes,
 
 		Cppflags:   compilerAttrs.cppFlags,
diff --git a/cc/library_headers.go b/cc/library_headers.go
index d6b4529..1a276f4 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -44,13 +44,13 @@
 }
 
 type libraryHeaderBazelHander struct {
-	bazelHandler
+	android.BazelHandler
 
 	module  *Module
 	library *libraryDecorator
 }
 
-func (h *libraryHeaderBazelHander) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
 	if err != nil {
@@ -108,6 +108,7 @@
 	Includes            bazel.StringListAttribute
 	Deps                bazel.LabelListAttribute
 	Implementation_deps bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
 }
 
 type bazelCcLibraryHeaders struct {
@@ -146,6 +147,7 @@
 		Includes:            exportedIncludes,
 		Implementation_deps: linkerAttrs.deps,
 		Deps:                linkerAttrs.exportedDeps,
+		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/object.go b/cc/object.go
index bd9f228..5952f1e 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -47,12 +47,12 @@
 }
 
 type objectBazelHandler struct {
-	bazelHandler
+	android.BazelHandler
 
 	module *Module
 }
 
-func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
 	if ok {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index f7154ec..d324241 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -15,9 +15,10 @@
 package cc
 
 import (
-	"android/soong/android"
 	"path/filepath"
 	"strings"
+
+	"android/soong/android"
 )
 
 func init() {
@@ -321,13 +322,13 @@
 }
 
 type prebuiltStaticLibraryBazelHandler struct {
-	bazelHandler
+	android.BazelHandler
 
 	module  *Module
 	library *libraryDecorator
 }
 
-func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
 	if err != nil {
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 31c4443..f8d1841 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -329,7 +329,7 @@
     def parse(self) -> List[Version]:
         """Parses the symbol file and returns a list of Version objects."""
         versions = []
-        while self.next_line() != '':
+        while self.next_line():
             assert self.current_line is not None
             if '{' in self.current_line:
                 versions.append(self.parse_version())
@@ -376,7 +376,7 @@
         symbols: List[Symbol] = []
         global_scope = True
         cpp_symbols = False
-        while self.next_line() != '':
+        while self.next_line():
             if '}' in self.current_line:
                 # Line is something like '} BASE; # tags'. Both base and tags
                 # are optional here.
@@ -428,11 +428,11 @@
         A return value of '' indicates EOF.
         """
         line = self.input_file.readline()
-        while line.strip() == '' or line.strip().startswith('#'):
+        while not line.strip() or line.strip().startswith('#'):
             line = self.input_file.readline()
 
             # We want to skip empty lines, but '' indicates EOF.
-            if line == '':
+            if not line:
                 break
         self.current_line = line
         return self.current_line
diff --git a/cc/testing.go b/cc/testing.go
index e8f3481..071f1ec 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -451,16 +451,6 @@
 			name: "note_memtag_heap_sync",
 		}
 
-
-		cc_library {
-			name: "libjemalloc5",
-			host_supported: true,
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			stl: "none",
-		}
-
 		cc_library {
 			name: "libc_musl",
 			host_supported: true,
diff --git a/cc/tidy.go b/cc/tidy.go
index b2382e8..fefa7f0 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -146,6 +146,8 @@
 	tidyChecks = tidyChecks + ",-bugprone-signed-char-misuse"
 	// http://b/155034972
 	tidyChecks = tidyChecks + ",-bugprone-branch-clone"
+	// http://b/193716442
+	tidyChecks = tidyChecks + ",-bugprone-implicit-widening-of-multiplication-result"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
 	if ctx.Config().IsEnvTrue("WITH_TIDY") {
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index f1f7bc7..1280553 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -26,7 +26,7 @@
 	"io/ioutil"
 	"log"
 	"os"
-	"strings"
+	"strconv"
 )
 
 func main() {
@@ -59,20 +59,16 @@
 	fmt.Fprintln(script, "ENTRY(__dlwrap__start)")
 	fmt.Fprintln(script, "SECTIONS {")
 
+	progsWithFlagsCount := make(map[string]int)
+
 	for _, prog := range ef.Progs {
 		if prog.Type != elf.PT_LOAD {
 			continue
 		}
 
-		var progName string
-		progSection := progToFirstSection(prog, ef.Sections)
-		if progSection != nil {
-			progName = progSection.Name
-		} else {
-			progName = fmt.Sprintf(".sect%d", load)
-		}
-		sectionName := ".linker" + progName
-		symName := "__dlwrap_linker" + strings.ReplaceAll(progName, ".", "_")
+		progName := progNameFromFlags(prog.Flags, progsWithFlagsCount)
+		sectionName := ".linker_" + progName
+		symName := "__dlwrap_linker_" + progName
 
 		flags := ""
 		if prog.Flags&elf.PF_W != 0 {
@@ -83,6 +79,12 @@
 		}
 		fmt.Fprintf(asm, ".section %s, \"a%s\"\n", sectionName, flags)
 
+		if load == 0 {
+			fmt.Fprintln(asm, ".globl __dlwrap_linker")
+			fmt.Fprintln(asm, "__dlwrap_linker:")
+			fmt.Fprintln(asm)
+		}
+
 		fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
 
 		fmt.Fprintf(script, "  %s 0x%x : {\n", sectionName, baseLoadAddr+prog.Vaddr)
@@ -106,6 +108,10 @@
 		load += 1
 	}
 
+	fmt.Fprintln(asm, ".globl __dlwrap_linker_end")
+	fmt.Fprintln(asm, "__dlwrap_linker_end:")
+	fmt.Fprintln(asm)
+
 	fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
 
 	fmt.Fprintln(script, "}")
@@ -139,11 +145,25 @@
 	fmt.Fprintln(asm)
 }
 
-func progToFirstSection(prog *elf.Prog, sections []*elf.Section) *elf.Section {
-	for _, section := range sections {
-		if section.Addr == prog.Vaddr {
-			return section
-		}
+func progNameFromFlags(flags elf.ProgFlag, progsWithFlagsCount map[string]int) string {
+	s := ""
+	if flags&elf.PF_R != 0 {
+		s += "r"
 	}
-	return nil
+	if flags&elf.PF_W != 0 {
+		s += "w"
+	}
+	if flags&elf.PF_X != 0 {
+		s += "x"
+	}
+
+	count := progsWithFlagsCount[s]
+	count++
+	progsWithFlagsCount[s] = count
+
+	if count > 1 {
+		s += strconv.Itoa(count)
+	}
+
+	return s
 }
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index 21d8e21..e5be6c0 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -19,8 +19,8 @@
 blueprint_go_binary {
     name: "multiproduct_kati",
     deps: [
-        "soong-ui-build",
         "soong-ui-logger",
+        "soong-ui-signal",
         "soong-ui-terminal",
         "soong-ui-tracer",
         "soong-zip",
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 55a5470..2846387 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -15,22 +15,24 @@
 package main
 
 import (
+	"bufio"
 	"context"
 	"flag"
 	"fmt"
 	"io"
-	"io/ioutil"
+	"log"
 	"os"
+	"os/exec"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"strings"
 	"sync"
 	"syscall"
 	"time"
 
-	"android/soong/finder"
-	"android/soong/ui/build"
 	"android/soong/ui/logger"
+	"android/soong/ui/signal"
 	"android/soong/ui/status"
 	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
@@ -74,36 +76,6 @@
 	return nil
 }
 
-const errorLeadingLines = 20
-const errorTrailingLines = 20
-
-func errMsgFromLog(filename string) string {
-	if filename == "" {
-		return ""
-	}
-
-	data, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return ""
-	}
-
-	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
-	if len(lines) > errorLeadingLines+errorTrailingLines+1 {
-		lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
-			len(lines)-errorLeadingLines-errorTrailingLines)
-
-		lines = append(lines[:errorLeadingLines+1],
-			lines[len(lines)-errorTrailingLines:]...)
-	}
-	var buf strings.Builder
-	for _, line := range lines {
-		buf.WriteString("> ")
-		buf.WriteString(line)
-		buf.WriteString("\n")
-	}
-	return buf.String()
-}
-
 // TODO(b/70370883): This tool uses a lot of open files -- over the default
 // soft limit of 1024 on some systems. So bump up to the hard limit until I fix
 // the algorithm.
@@ -155,28 +127,59 @@
 }
 
 type mpContext struct {
-	Context context.Context
-	Logger  logger.Logger
-	Status  status.ToolStatus
-	Tracer  tracer.Tracer
-	Finder  *finder.Finder
-	Config  build.Config
+	Logger logger.Logger
+	Status status.ToolStatus
 
-	LogsDir string
+	SoongUi     string
+	MainOutDir  string
+	MainLogsDir string
+}
+
+func detectTotalRAM() uint64 {
+	var info syscall.Sysinfo_t
+	err := syscall.Sysinfo(&info)
+	if err != nil {
+		panic(err)
+	}
+	return info.Totalram * uint64(info.Unit)
+}
+
+func findNamedProducts(soongUi string, log logger.Logger) []string {
+	cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
+	output, err := cmd.Output()
+	if err != nil {
+		log.Fatalf("Cannot determine named products: %v", err)
+	}
+
+	rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
+	match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
+	return strings.Fields(match[1])
+}
+
+// ensureEmptyFileExists ensures that the containing directory exists, and the
+// specified file exists. If it doesn't exist, it will write an empty file.
+func ensureEmptyFileExists(file string, log logger.Logger) {
+	if _, err := os.Stat(file); os.IsNotExist(err) {
+		f, err := os.Create(file)
+		if err != nil {
+			log.Fatalf("Error creating %s: %q\n", file, err)
+		}
+		f.Close()
+	} else if err != nil {
+		log.Fatalf("Error checking %s: %q\n", file, err)
+	}
 }
 
 func main() {
 	stdio := terminal.StdioImpl{}
 
-	output := terminal.NewStatusOutput(stdio.Stdout(), "", false,
-		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
-
+	output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false)
 	log := logger.New(output)
 	defer log.Cleanup()
 
 	flag.Parse()
 
-	ctx, cancel := context.WithCancel(context.Background())
+	_, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
 	trace := tracer.New(log)
@@ -189,61 +192,59 @@
 	var failures failureCount
 	stat.AddOutput(&failures)
 
-	build.SetupSignals(log, cancel, func() {
+	signal.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
 		stat.Finish()
 	})
 
-	buildCtx := build.Context{ContextImpl: &build.ContextImpl{
-		Context: ctx,
-		Logger:  log,
-		Tracer:  trace,
-		Writer:  output,
-		Status:  stat,
-	}}
+	soongUi := "build/soong/soong_ui.bash"
 
-	args := ""
-	if *alternateResultDir {
-		args = "dist"
-	}
-	config := build.NewConfig(buildCtx, args)
-	if *outDir == "" {
+	var outputDir string
+	if *outDir != "" {
+		outputDir = *outDir
+	} else {
 		name := "multiproduct"
 		if !*incremental {
 			name += "-" + time.Now().Format("20060102150405")
 		}
 
-		*outDir = filepath.Join(config.OutDir(), name)
-
-		// Ensure the empty files exist in the output directory
-		// containing our output directory too. This is mostly for
-		// safety, but also triggers the ninja_build file so that our
-		// build servers know that they can parse the output as if it
-		// was ninja output.
-		build.SetupOutDir(buildCtx, config)
-
-		if err := os.MkdirAll(*outDir, 0777); err != nil {
-			log.Fatalf("Failed to create tempdir: %v", err)
+		outDirBase := os.Getenv("OUT_DIR")
+		if outDirBase == "" {
+			outDirBase = "out"
 		}
-	}
-	config.Environment().Set("OUT_DIR", *outDir)
-	log.Println("Output directory:", *outDir)
 
-	logsDir := filepath.Join(config.OutDir(), "logs")
+		outputDir = filepath.Join(outDirBase, name)
+	}
+
+	log.Println("Output directory:", outputDir)
+
+	// The ninja_build file is used by our buildbots to understand that the output
+	// can be parsed as ninja output.
+	if err := os.MkdirAll(outputDir, 0777); err != nil {
+		log.Fatalf("Failed to create output directory: %v", err)
+	}
+	ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
+
+	logsDir := filepath.Join(outputDir, "logs")
 	os.MkdirAll(logsDir, 0777)
 
-	build.SetupOutDir(buildCtx, config)
+	var configLogsDir string
+	if *alternateResultDir {
+		configLogsDir = filepath.Join(outputDir, "dist/logs")
+	} else {
+		configLogsDir = outputDir
+	}
 
-	os.MkdirAll(config.LogsDir(), 0777)
-	log.SetOutput(filepath.Join(config.LogsDir(), "soong.log"))
-	trace.SetOutput(filepath.Join(config.LogsDir(), "build.trace"))
+	os.MkdirAll(configLogsDir, 0777)
+	log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
+	trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
 
 	var jobs = *numJobs
 	if jobs < 1 {
 		jobs = runtime.NumCPU() / 4
 
-		ramGb := int(config.TotalRAM() / 1024 / 1024 / 1024)
+		ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
 		if ramJobs := ramGb / 25; ramGb > 0 && jobs > ramJobs {
 			jobs = ramJobs
 		}
@@ -256,17 +257,8 @@
 
 	setMaxFiles(log)
 
-	finder := build.NewSourceFinder(buildCtx, config)
-	defer finder.Shutdown()
-
-	build.FindSources(buildCtx, config, finder)
-
-	vars, err := build.DumpMakeVars(buildCtx, config, nil, []string{"all_named_products"})
-	if err != nil {
-		log.Fatal(err)
-	}
+	allProducts := findNamedProducts(soongUi, log)
 	var productsList []string
-	allProducts := strings.Fields(vars["all_named_products"])
 
 	if len(includeProducts) > 0 {
 		var missingProducts []string
@@ -314,19 +306,15 @@
 
 	log.Verbose("Got product list: ", finalProductsList)
 
-	s := buildCtx.Status.StartTool()
+	s := stat.StartTool()
 	s.SetTotalActions(len(finalProductsList))
 
 	mpCtx := &mpContext{
-		Context: ctx,
-		Logger:  log,
-		Status:  s,
-		Tracer:  trace,
-
-		Finder: finder,
-		Config: config,
-
-		LogsDir: logsDir,
+		Logger:      log,
+		Status:      s,
+		SoongUi:     soongUi,
+		MainOutDir:  outputDir,
+		MainLogsDir: logsDir,
 	}
 
 	products := make(chan string, len(productsList))
@@ -348,7 +336,7 @@
 					if product == "" {
 						return
 					}
-					buildProduct(mpCtx, product)
+					runSoongUiForProduct(mpCtx, product)
 				}
 			}
 		}()
@@ -360,7 +348,7 @@
 			FileArgs: []zip.FileArg{
 				{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
 			},
-			OutputFilePath:   filepath.Join(config.RealDistDir(), "logs.zip"),
+			OutputFilePath:   filepath.Join(outputDir, "dist/logs.zip"),
 			NumParallelJobs:  runtime.NumCPU(),
 			CompressionLevel: 5,
 		}
@@ -380,11 +368,33 @@
 	}
 }
 
-func buildProduct(mpctx *mpContext, product string) {
-	var stdLog string
+func cleanupAfterProduct(outDir, productZip string) {
+	if *keepArtifacts {
+		args := zip.ZipArgs{
+			FileArgs: []zip.FileArg{
+				{
+					GlobDir:             outDir,
+					SourcePrefixToStrip: outDir,
+				},
+			},
+			OutputFilePath:   productZip,
+			NumParallelJobs:  runtime.NumCPU(),
+			CompressionLevel: 5,
+		}
+		if err := zip.Zip(args); err != nil {
+			log.Fatalf("Error zipping artifacts: %v", err)
+		}
+	}
+	if !*incremental {
+		os.RemoveAll(outDir)
+	}
+}
 
-	outDir := filepath.Join(mpctx.Config.OutDir(), product)
-	logsDir := filepath.Join(mpctx.LogsDir, product)
+func runSoongUiForProduct(mpctx *mpContext, product string) {
+	outDir := filepath.Join(mpctx.MainOutDir, product)
+	logsDir := filepath.Join(mpctx.MainLogsDir, product)
+	productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
+	consoleLogPath := filepath.Join(logsDir, "std.log")
 
 	if err := os.MkdirAll(outDir, 0777); err != nil {
 		mpctx.Logger.Fatalf("Error creating out directory: %v", err)
@@ -393,98 +403,65 @@
 		mpctx.Logger.Fatalf("Error creating log directory: %v", err)
 	}
 
-	stdLog = filepath.Join(logsDir, "std.log")
-	f, err := os.Create(stdLog)
+	consoleLogFile, err := os.Create(consoleLogPath)
 	if err != nil {
-		mpctx.Logger.Fatalf("Error creating std.log: %v", err)
+		mpctx.Logger.Fatalf("Error creating console log file: %v", err)
 	}
-	defer f.Close()
+	defer consoleLogFile.Close()
 
-	log := logger.New(f)
-	defer log.Cleanup()
-	log.SetOutput(filepath.Join(logsDir, "soong.log"))
+	consoleLogWriter := bufio.NewWriter(consoleLogFile)
+	defer consoleLogWriter.Flush()
+
+	args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
+
+	if !*keepArtifacts {
+		args = append(args, "--empty-ninja-file")
+	}
+
+	if *onlyConfig {
+		args = append(args, "--config-only")
+	} else if *onlySoong {
+		args = append(args, "--soong-only")
+	}
+
+	if *alternateResultDir {
+		args = append(args, "dist")
+	}
+
+	cmd := exec.Command(mpctx.SoongUi, args...)
+	cmd.Stdout = consoleLogWriter
+	cmd.Stderr = consoleLogWriter
+	cmd.Env = append(os.Environ(),
+		"OUT_DIR="+outDir,
+		"TARGET_PRODUCT="+product,
+		"TARGET_BUILD_VARIANT="+*buildVariant,
+		"TARGET_BUILD_TYPE=release",
+		"TARGET_BUILD_APPS=",
+		"TARGET_BUILD_UNBUNDLED=")
 
 	action := &status.Action{
 		Description: product,
 		Outputs:     []string{product},
 	}
+
 	mpctx.Status.StartAction(action)
-	defer logger.Recover(func(err error) {
-		mpctx.Status.FinishAction(status.ActionResult{
-			Action: action,
-			Error:  err,
-			Output: errMsgFromLog(stdLog),
-		})
-	})
-
-	ctx := build.Context{ContextImpl: &build.ContextImpl{
-		Context: mpctx.Context,
-		Logger:  log,
-		Tracer:  mpctx.Tracer,
-		Writer:  f,
-		Thread:  mpctx.Tracer.NewThread(product),
-		Status:  &status.Status{},
-	}}
-	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "", false,
-		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
-
-	args := append([]string(nil), flag.Args()...)
-	args = append(args, "--skip-soong-tests")
-	config := build.NewConfig(ctx, args...)
-	config.Environment().Set("OUT_DIR", outDir)
-	if !*keepArtifacts {
-		config.SetEmptyNinjaFile(true)
-	}
-	build.FindSources(ctx, config, mpctx.Finder)
-	config.Lunch(ctx, product, *buildVariant)
-
-	defer func() {
-		if *keepArtifacts {
-			args := zip.ZipArgs{
-				FileArgs: []zip.FileArg{
-					{
-						GlobDir:             outDir,
-						SourcePrefixToStrip: outDir,
-					},
-				},
-				OutputFilePath:   filepath.Join(mpctx.Config.OutDir(), product+".zip"),
-				NumParallelJobs:  runtime.NumCPU(),
-				CompressionLevel: 5,
-			}
-			if err := zip.Zip(args); err != nil {
-				log.Fatalf("Error zipping artifacts: %v", err)
-			}
-		}
-		if !*incremental {
-			os.RemoveAll(outDir)
-		}
-	}()
-
-	config.SetSkipNinja(true)
-
-	buildWhat := build.RunProductConfig
-	if !*onlyConfig {
-		buildWhat |= build.RunSoong
-		if !*onlySoong {
-			buildWhat |= build.RunKati
-		}
-	}
+	defer cleanupAfterProduct(outDir, productZip)
 
 	before := time.Now()
-	build.Build(ctx, config)
+	err = cmd.Run()
 
-	// Save std_full.log if Kati re-read the makefiles
-	if buildWhat&build.RunKati != 0 {
-		if after, err := os.Stat(config.KatiBuildNinjaFile()); err == nil && after.ModTime().After(before) {
-			err := copyFile(stdLog, filepath.Join(filepath.Dir(stdLog), "std_full.log"))
+	if !*onlyConfig && !*onlySoong {
+		katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
+		if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
+			err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
 			if err != nil {
 				log.Fatalf("Error copying log file: %s", err)
 			}
 		}
 	}
-
 	mpctx.Status.FinishAction(status.ActionResult{
 		Action: action,
+		Error:  err,
 	})
 }
 
diff --git a/cmd/run_with_timeout/run_with_timeout.go b/cmd/run_with_timeout/run_with_timeout.go
index f2caaab..deaa842 100644
--- a/cmd/run_with_timeout/run_with_timeout.go
+++ b/cmd/run_with_timeout/run_with_timeout.go
@@ -47,7 +47,7 @@
 	flag.Parse()
 
 	if flag.NArg() < 1 {
-		fmt.Fprintln(os.Stderr, "command is required")
+		fmt.Fprintf(os.Stderr, "%s: error: command is required\n", os.Args[0])
 		usage()
 	}
 
@@ -55,9 +55,9 @@
 		os.Stdin, os.Stdout, os.Stderr)
 	if err != nil {
 		if exitErr, ok := err.(*exec.ExitError); ok {
-			fmt.Fprintln(os.Stderr, "process exited with error:", exitErr.Error())
+			fmt.Fprintf(os.Stderr, "%s: process exited with error: %s\n", os.Args[0], exitErr.Error())
 		} else {
-			fmt.Fprintln(os.Stderr, "error:", err.Error())
+			fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err.Error())
 		}
 		os.Exit(1)
 	}
@@ -115,6 +115,7 @@
 	if timeout > 0 {
 		timeoutCh = time.After(timeout)
 	}
+	startTime := time.Now()
 
 	select {
 	case err := <-waitCh:
@@ -126,10 +127,12 @@
 		// Continue below.
 	}
 
+	fmt.Fprintf(concurrentStderr, "%s: process timed out after %s\n", os.Args[0], time.Since(startTime))
 	// Process timed out before exiting.
 	defer cmd.Process.Signal(syscall.SIGKILL)
 
 	if onTimeoutCmdStr != "" {
+		fmt.Fprintf(concurrentStderr, "%s: running on_timeout command `%s`\n", os.Args[0], onTimeoutCmdStr)
 		onTimeoutCmd := exec.Command("sh", "-c", onTimeoutCmdStr)
 		onTimeoutCmd.Stdin, onTimeoutCmd.Stdout, onTimeoutCmd.Stderr = stdin, concurrentStdout, concurrentStderr
 		onTimeoutCmd.Env = append(os.Environ(), fmt.Sprintf("PID=%d", cmd.Process.Pid))
diff --git a/cmd/run_with_timeout/run_with_timeout_test.go b/cmd/run_with_timeout/run_with_timeout_test.go
index aebd336..ed6ec11 100644
--- a/cmd/run_with_timeout/run_with_timeout_test.go
+++ b/cmd/run_with_timeout/run_with_timeout_test.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"io"
+	"regexp"
 	"testing"
 	"time"
 )
@@ -60,7 +61,8 @@
 				args:    []string{"-c", "sleep 1 && echo foo"},
 				timeout: 1 * time.Millisecond,
 			},
-			wantErr: true,
+			wantStderr: ".*: process timed out after .*\n",
+			wantErr:    true,
 		},
 		{
 			name: "on_timeout command",
@@ -71,6 +73,7 @@
 				onTimeoutCmd: "echo bar",
 			},
 			wantStdout: "bar\n",
+			wantStderr: ".*: process timed out after .*\n.*: running on_timeout command `echo bar`\n",
 			wantErr:    true,
 		},
 	}
@@ -86,7 +89,7 @@
 			if gotStdout := stdout.String(); gotStdout != tt.wantStdout {
 				t.Errorf("runWithTimeout() gotStdout = %v, want %v", gotStdout, tt.wantStdout)
 			}
-			if gotStderr := stderr.String(); gotStderr != tt.wantStderr {
+			if gotStderr := stderr.String(); !regexp.MustCompile(tt.wantStderr).MatchString(gotStderr) {
 				t.Errorf("runWithTimeout() gotStderr = %v, want %v", gotStderr, tt.wantStderr)
 			}
 		})
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 0336fb6..af935db 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -44,6 +44,8 @@
 	docFile           string
 	bazelQueryViewDir string
 	bp2buildMarker    string
+
+	cmdlineArgs bootstrap.Args
 )
 
 func init() {
@@ -61,6 +63,21 @@
 	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")
+
+	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
+	flag.StringVar(&cmdlineArgs.GlobFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
+	flag.StringVar(&cmdlineArgs.GlobListDir, "globListDir", "", "the directory containing the glob list files")
+	flag.StringVar(&cmdlineArgs.BuildDir, "b", ".", "the build output directory")
+	flag.StringVar(&cmdlineArgs.NinjaBuildDir, "n", "", "the ninja builddir directory")
+	flag.StringVar(&cmdlineArgs.DepFile, "d", "", "the dependency file to output")
+	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
+	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
+	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
+	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
+	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
+	flag.BoolVar(&cmdlineArgs.UseValidations, "use-validations", false, "use validations to depend on go tests")
+	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
+	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -90,8 +107,8 @@
 	return ctx
 }
 
-func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineArgs.ModuleListFile, availableEnv)
+func newConfig(outDir string, availableEnv map[string]string) android.Config {
+	configuration, err := android.NewConfig(outDir, cmdlineArgs.ModuleListFile, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -107,7 +124,7 @@
 func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
 	var firstArgs, secondArgs bootstrap.Args
 
-	firstArgs = bootstrap.CmdlineArgs
+	firstArgs = cmdlineArgs
 	configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
 	bootstrap.RunBlueprint(firstArgs, firstCtx.Context, configuration)
 
@@ -123,7 +140,7 @@
 		os.Exit(1)
 	}
 	secondCtx := newContext(secondConfig, true)
-	secondArgs = bootstrap.CmdlineArgs
+	secondArgs = cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 	err = deptools.WriteDepFile(shared.JoinPath(topDir, secondArgs.DepFile), secondArgs.OutFile, ninjaDeps)
@@ -145,7 +162,7 @@
 
 func runSoongDocs(configuration android.Config) {
 	ctx := newContext(configuration, false)
-	soongDocsArgs := bootstrap.CmdlineArgs
+	soongDocsArgs := cmdlineArgs
 	bootstrap.RunBlueprint(soongDocsArgs, ctx.Context, configuration)
 	if err := writeDocs(ctx, configuration, docFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
@@ -183,7 +200,7 @@
 	generateQueryView := bazelQueryViewDir != ""
 	jsonModuleFile := configuration.Getenv("SOONG_DUMP_JSON_MODULE_GRAPH")
 
-	blueprintArgs := bootstrap.CmdlineArgs
+	blueprintArgs := cmdlineArgs
 	prepareBuildActions := !generateQueryView && jsonModuleFile == ""
 	if bazelConversionRequested {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
@@ -208,16 +225,16 @@
 	// Convert the Soong module graph into Bazel BUILD files.
 	if generateQueryView {
 		runQueryView(configuration, ctx)
-		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
+		return cmdlineArgs.OutFile // TODO: This is a lie
 	}
 
 	if jsonModuleFile != "" {
 		writeJsonModuleGraph(configuration, ctx, jsonModuleFile, extraNinjaDeps)
-		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
+		return cmdlineArgs.OutFile // TODO: This is a lie
 	}
 
 	writeMetrics(configuration)
-	return bootstrap.CmdlineArgs.OutFile
+	return cmdlineArgs.OutFile
 }
 
 // soong_ui dumps the available environment variables to
@@ -256,9 +273,7 @@
 
 	availableEnv := parseAvailableEnv()
 
-	// The top-level Blueprints file is passed as the first argument.
-	srcDir := filepath.Dir(flag.Arg(0))
-	configuration := newConfig(srcDir, outDir, availableEnv)
+	configuration := newConfig(outDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
 		usedEnvFile,
@@ -420,7 +435,7 @@
 // Read the bazel.list file that the Soong Finder already dumped earlier (hopefully)
 // It contains the locations of BUILD files, BUILD.bazel files, etc. in the source dir
 func getExistingBazelRelatedFiles(topDir string) ([]string, error) {
-	bazelFinderFile := filepath.Join(filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list")
+	bazelFinderFile := filepath.Join(filepath.Dir(cmdlineArgs.ModuleListFile), "bazel.list")
 	if !filepath.IsAbs(bazelFinderFile) {
 		// Assume this was a relative path under topDir
 		bazelFinderFile = filepath.Join(topDir, bazelFinderFile)
@@ -451,8 +466,8 @@
 	// 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.CmdlineArgs.ModuleListFile)
-	modulePaths, err := bp2buildCtx.ListModulePaths(configuration.SrcDir())
+	bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+	modulePaths, err := bp2buildCtx.ListModulePaths(".")
 	if err != nil {
 		panic(err)
 	}
@@ -465,11 +480,17 @@
 	// 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.
-	blueprintArgs := bootstrap.CmdlineArgs
+	blueprintArgs := cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	ninjaDeps = append(ninjaDeps, bootstrap.GlobFileListFiles(configuration)...)
+	// Generate out/soong/.bootstrap/build-globs.ninja with the actions to generate flattened globfiles
+	// containing the globs seen during bp2build conversion
+	if blueprintArgs.GlobFile != "" {
+		bootstrap.WriteBuildGlobsNinjaFile(blueprintArgs.GlobListDir, bp2buildCtx.Context, blueprintArgs, configuration)
+	}
+	// Add the depfile on the expanded globs in out/soong/.primary/globs
+	ninjaDeps = append(ninjaDeps, bootstrap.GlobFileListFiles(configuration, blueprintArgs.GlobListDir)...)
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files
 	// and print conversion metrics to the user.
@@ -487,8 +508,8 @@
 		"bazel-" + filepath.Base(topDir),
 	}
 
-	if bootstrap.CmdlineArgs.NinjaBuildDir[0] != '/' {
-		excludes = append(excludes, bootstrap.CmdlineArgs.NinjaBuildDir)
+	if cmdlineArgs.NinjaBuildDir[0] != '/' {
+		excludes = append(excludes, cmdlineArgs.NinjaBuildDir)
 	}
 
 	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
@@ -503,7 +524,7 @@
 	excludes = append(excludes, getTemporaryExcludes()...)
 
 	symlinkForestDeps := bp2build.PlantSymlinkForest(
-		topDir, workspaceRoot, generatedRoot, configuration.SrcDir(), excludes)
+		topDir, workspaceRoot, generatedRoot, ".", excludes)
 
 	// Only report metrics when in bp2build mode. The metrics aren't relevant
 	// for queryview, since that's a total repo-wide conversion and there's a
@@ -522,12 +543,4 @@
 
 	// Create an empty bp2build marker file.
 	touch(shared.JoinPath(topDir, bp2buildMarker))
-
-	// bp2build *always* writes a fake Ninja file containing just the nothing
-	// phony target if it ever re-runs. This allows bp2build to exit early with
-	// GENERATE_BAZEL_FILES=1 m nothing.
-	//
-	// If bp2build is invoked as part of an integrated mixed build, the fake
-	// build.ninja file will be rewritten later into the real file anyway.
-	writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
 }
diff --git a/cmd/soong_ui/Android.bp b/cmd/soong_ui/Android.bp
index 4f5eea9..9f10b82 100644
--- a/cmd/soong_ui/Android.bp
+++ b/cmd/soong_ui/Android.bp
@@ -20,6 +20,7 @@
     name: "soong_ui",
     deps: [
         "soong-ui-build",
+        "soong-ui-signal",
         "soong-ui-logger",
         "soong-ui-terminal",
         "soong-ui-tracer",
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 02c5229..d709787 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -30,6 +30,7 @@
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
+	"android/soong/ui/signal"
 	"android/soong/ui/status"
 	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
@@ -190,7 +191,7 @@
 	stat.AddOutput(trace.StatusTracer())
 
 	// Set up a cleanup procedure in case the normal termination process doesn't work.
-	build.SetupSignals(log, cancel, func() {
+	signal.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
 		stat.Finish()
diff --git a/cuj/Android.bp b/cuj/Android.bp
index a2da6e6..f9cfdc1 100644
--- a/cuj/Android.bp
+++ b/cuj/Android.bp
@@ -7,6 +7,7 @@
     deps: [
         "soong-ui-build",
         "soong-ui-logger",
+        "soong-ui-signal",
         "soong-ui-terminal",
         "soong-ui-tracer",
     ],
diff --git a/cuj/cuj.go b/cuj/cuj.go
index b4ae9a2..413f423 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -27,6 +27,7 @@
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
+	"android/soong/ui/signal"
 	"android/soong/ui/status"
 	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
@@ -65,7 +66,7 @@
 	stat.AddOutput(output)
 	stat.AddOutput(trace.StatusTracer())
 
-	build.SetupSignals(log, cancel, func() {
+	signal.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
 		stat.Finish()
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 245af2c..ebb8959 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -193,6 +193,9 @@
 	// The name of the library.
 	Name string
 
+	// If the library is optional or required.
+	Optional bool
+
 	// On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
 	Host android.Path
 
@@ -256,7 +259,7 @@
 
 // Add class loader context for the given library to the map entry for the given SDK version.
 func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
-	hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
+	optional bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
 
 	// For prebuilts, library should have the same name as the source module.
 	lib = android.RemoveOptionalPrebuiltPrefix(lib)
@@ -304,6 +307,7 @@
 
 	clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
 		Name:        lib,
+		Optional:    optional,
 		Host:        hostPath,
 		Device:      devicePath,
 		Subcontexts: subcontexts,
@@ -316,9 +320,9 @@
 // about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
 // are validated later before CLC is used (in validateClassLoaderContext).
 func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
-	lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
+	lib string, optional bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
 
-	err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
+	err := clcMap.addContext(ctx, sdkVer, lib, optional, hostPath, installPath, nestedClcMap)
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 	}
@@ -361,15 +365,21 @@
 // Returns top-level libraries in the CLC (conditional CLC, i.e. compatibility libraries are not
 // included). This is the list of libraries that should be in the <uses-library> tags in the
 // manifest. Some of them may be present in the source manifest, others are added by manifest_fixer.
-func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
+// Required and optional libraries are in separate lists.
+func (clcMap ClassLoaderContextMap) UsesLibs() (required []string, optional []string) {
 	if clcMap != nil {
 		clcs := clcMap[AnySdkVersion]
-		ulibs = make([]string, 0, len(clcs))
+		required = make([]string, 0, len(clcs))
+		optional = make([]string, 0, len(clcs))
 		for _, clc := range clcs {
-			ulibs = append(ulibs, clc.Name)
+			if clc.Optional {
+				optional = append(optional, clc.Name)
+			} else {
+				required = append(required, clc.Name)
+			}
 		}
 	}
-	return ulibs
+	return required, optional
 }
 
 func (clcMap ClassLoaderContextMap) Dump() string {
@@ -388,7 +398,8 @@
 // TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
 //
 func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
-	usesLibs := clcMap.UsesLibs()
+	required, optional := clcMap.UsesLibs()
+	usesLibs := append(required, optional...)
 
 	for sdkVer, clcs := range clcMap {
 		if sdkVer == AnySdkVersion {
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 610a4c9..0b7b546 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -49,32 +49,34 @@
 	//
 	ctx := testContext()
 
+	optional := false
+
 	m := make(ClassLoaderContextMap)
 
-	m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, AnySdkVersion, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
 
 	// Add some libraries with nested subcontexts.
 
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
-	m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "a1", optional, buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "b1", optional, buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
 
 	m2 := make(ClassLoaderContextMap)
-	m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
-	m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
-	m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+	m2.AddContext(ctx, AnySdkVersion, "a2", optional, buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "b2", optional, buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "c2", optional, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
 
 	m3 := make(ClassLoaderContextMap)
-	m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
-	m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "a3", optional, buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "b3", optional, buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
 
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
 	// When the same library is both in conditional and unconditional context, it should be removed
 	// from conditional context.
-	m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
-	m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, 42, "f", optional, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, AnySdkVersion, "f", optional, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
 
 	// Merge map with implicit root library that is among toplevel contexts => does nothing.
 	m.AddContextMap(m1, "c")
@@ -83,12 +85,12 @@
 	m.AddContextMap(m3, "m_g")
 
 	// Compatibility libraries with unknown install paths get default paths.
-	m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
-	m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlManager, optional, buildPath(ctx, AndroidHidlManager), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlBase, optional, buildPath(ctx, AndroidHidlBase), nil, nil)
 
 	// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
 	// needed as a compatibility library if "android.test.runner" is in CLC as well.
-	m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+	m.AddContext(ctx, 30, AndroidTestMock, optional, buildPath(ctx, AndroidTestMock), nil, nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
@@ -96,10 +98,10 @@
 
 	var haveStr string
 	var havePaths android.Paths
-	var haveUsesLibs []string
+	var haveUsesLibsReq, haveUsesLibsOpt []string
 	if valid && validationError == nil {
 		haveStr, havePaths = ComputeClassLoaderContext(m)
-		haveUsesLibs = m.UsesLibs()
+		haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
 	}
 
 	// Test that validation is successful (all paths are known).
@@ -148,20 +150,25 @@
 
 	// Test for libraries that are added by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		wantUsesLibs := []string{"a", "b", "c", "d", "f", "a3", "b3"}
-		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
-			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		wantUsesLibsReq := []string{"a", "b", "c", "d", "f", "a3", "b3"}
+		wantUsesLibsOpt := []string{}
+		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
+			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
+		}
+		if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
+			t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
 		}
 	})
 }
 
 func TestCLCJson(t *testing.T) {
 	ctx := testContext()
+	optional := false
 	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 	jsonCLC := toJsonClassLoaderContext(m)
 	restored := fromJsonClassLoaderContext(ctx, jsonCLC)
 	android.AssertIntEquals(t, "The size of the maps should be the same.", len(m), len(restored))
@@ -181,20 +188,25 @@
 // Test that unknown library paths cause a validation error.
 func testCLCUnknownPath(t *testing.T, whichPath string) {
 	ctx := testContext()
+	optional := false
 
 	m := make(ClassLoaderContextMap)
 	if whichPath == "build" {
-		m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+		m.AddContext(ctx, AnySdkVersion, "a", optional, nil, nil, nil)
 	} else {
-		m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+		m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), nil, nil)
 	}
 
 	// The library should be added to <uses-library> tags by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		haveUsesLibs := m.UsesLibs()
-		wantUsesLibs := []string{"a"}
-		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
-			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		haveUsesLibsReq, haveUsesLibsOpt := m.UsesLibs()
+		wantUsesLibsReq := []string{"a"}
+		wantUsesLibsOpt := []string{}
+		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
+			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
+		}
+		if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
+			t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
 		}
 	})
 
@@ -216,10 +228,11 @@
 // An attempt to add conditional nested subcontext should fail.
 func TestCLCNestedConditional(t *testing.T) {
 	ctx := testContext()
+	optional := false
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m1.AddContext(ctx, 42, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
 	m := make(ClassLoaderContextMap)
-	err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
+	err := m.addContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), m1)
 	checkError(t, err, "nested class loader context shouldn't have conditional part")
 }
 
@@ -227,11 +240,12 @@
 // they end up in the order that agrees with PackageManager.
 func TestCLCSdkVersionOrder(t *testing.T) {
 	ctx := testContext()
+	optional := false
 	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 2f99655..8f5c315 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -125,6 +125,20 @@
 	})
 }
 
+// FixtureSetSystemServerJars sets the SystemServerJars property.
+func FixtureSetSystemServerJars(jars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.SystemServerJars = android.CreateTestConfiguredJarList(jars)
+	})
+}
+
+// FixtureSetApexSystemServerJars sets the ApexSystemServerJars property in the global config.
+func FixtureSetApexSystemServerJars(jars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.ApexSystemServerJars = android.CreateTestConfiguredJarList(jars)
+	})
+}
+
 // FixtureSetPreoptWithUpdatableBcp sets the PreoptWithUpdatableBcp property in the global config.
 func FixtureSetPreoptWithUpdatableBcp(value bool) android.FixturePreparer {
 	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
diff --git a/fuzz/Android.bp b/fuzz/Android.bp
new file mode 100644
index 0000000..9d96e48
--- /dev/null
+++ b/fuzz/Android.bp
@@ -0,0 +1,15 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-fuzz",
+    pkgPath: "android/soong/fuzz",
+    deps: [
+        "soong-android",
+    ],
+    srcs: [
+        "fuzz_common.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/cc/fuzz_common.go b/fuzz/fuzz_common.go
similarity index 73%
rename from cc/fuzz_common.go
rename to fuzz/fuzz_common.go
index 98ed7f4..ccadc0f 100644
--- a/cc/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -12,14 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cc
+package fuzz
 
 // This file contains the common code for compiling C/C++ and Rust fuzzers for Android.
 
 import (
+	"encoding/json"
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 )
 
@@ -30,6 +33,8 @@
 	Rust Lang = "rust"
 )
 
+var BoolDefault = proptools.BoolDefault
+
 type FuzzModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -52,6 +57,44 @@
 	Dir          string
 }
 
+type FuzzConfig struct {
+	// Email address of people to CC on bugs or contact about this fuzz target.
+	Cc []string `json:"cc,omitempty"`
+	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
+	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
+	// Specify whether to enable continuous fuzzing on host. Defaults to true.
+	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
+	// Component in Google's bug tracking system that bugs should be filed to.
+	Componentid *int64 `json:"componentid,omitempty"`
+	// Hotlists in Google's bug tracking system that bugs should be marked with.
+	Hotlists []string `json:"hotlists,omitempty"`
+	// Specify whether this fuzz target was submitted by a researcher. Defaults
+	// to false.
+	Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
+	// Specify who should be acknowledged for CVEs in the Android Security
+	// Bulletin.
+	Acknowledgement []string `json:"acknowledgement,omitempty"`
+	// Additional options to be passed to libfuzzer when run in Haiku.
+	Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+	// Additional options to be passed to HWASAN when running on-device in Haiku.
+	Hwasan_options []string `json:"hwasan_options,omitempty"`
+	// Additional options to be passed to HWASAN when running on host in Haiku.
+	Asan_options []string `json:"asan_options,omitempty"`
+}
+
+type FuzzProperties struct {
+	// Optional list of seed files to be installed to the fuzz target's output
+	// directory.
+	Corpus []string `android:"path"`
+	// Optional list of data files to be installed to the fuzz target's output
+	// directory. Directory structure relative to the module is preserved.
+	Data []string `android:"path"`
+	// Optional dictionary to be installed to the fuzz target's output directory.
+	Dictionary *string `android:"path"`
+	// Config for running the target on fuzzing infrastructure.
+	Fuzz_config *FuzzConfig
+}
+
 type FuzzPackagedModule struct {
 	FuzzProperties        FuzzProperties
 	Dictionary            android.Path
@@ -151,7 +194,16 @@
 	return archDirs[archOs], true
 }
 
-func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang) {
+func (f *FuzzConfig) String() string {
+	b, err := json.Marshal(f)
+	if err != nil {
+		panic(err)
+	}
+
+	return string(b)
+}
+
+func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang, pctx android.PackageContext) {
 	var archOsList []ArchOs
 	for archOs := range archDirs {
 		archOsList = append(archOsList, archOs)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 71a8780..fdb3618 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -240,7 +240,7 @@
 }
 
 // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
-func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (c *Module) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
 	if ok {
@@ -560,7 +560,7 @@
 	bazelModuleLabel := g.GetBazelLabel(ctx, g)
 	bazelActionsUsed := false
 	if g.MixedBuildsEnabled(ctx) {
-		bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
+		bazelActionsUsed = g.GenerateBazelBuildActions(ctx, bazelModuleLabel)
 	}
 	if !bazelActionsUsed {
 		// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
diff --git a/java/aar.go b/java/aar.go
index 04727e4..afbaea2 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -502,11 +502,12 @@
 	if sdkDep.hasFrameworkLibs() {
 		a.aapt.deps(ctx, sdkDep)
 	}
+	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
 }
 
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
-	a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
 
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 331f941..1f7234d 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -71,12 +71,12 @@
 		args = append(args, "--use-embedded-dex")
 	}
 
-	for _, usesLib := range classLoaderContexts.UsesLibs() {
-		if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
-			args = append(args, "--optional-uses-library", usesLib)
-		} else {
-			args = append(args, "--uses-library", usesLib)
-		}
+	requiredUsesLibs, optionalUsesLibs := classLoaderContexts.UsesLibs()
+	for _, usesLib := range requiredUsesLibs {
+		args = append(args, "--uses-library", usesLib)
+	}
+	for _, usesLib := range optionalUsesLibs {
+		args = append(args, "--optional-uses-library", usesLib)
 	}
 
 	if hasNoCode {
diff --git a/java/androidmk.go b/java/androidmk.go
index 04357e0..68ccd82 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -114,7 +114,8 @@
 						entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
 					}
 
-					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...)
+					requiredUsesLibs, optionalUsesLibs := library.classLoaderContexts.UsesLibs()
+					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", append(requiredUsesLibs, optionalUsesLibs...)...)
 
 					if len(library.additionalCheckedModules) != 0 {
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
diff --git a/java/app.go b/java/app.go
index 35ed27f..99e2ab6 100755
--- a/java/app.go
+++ b/java/app.go
@@ -649,8 +649,12 @@
 	a.usesLibrary.freezeEnforceUsesLibraries()
 
 	// Add implicit SDK libraries to <uses-library> list.
-	for _, usesLib := range a.classLoaderContexts.UsesLibs() {
-		a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
+	requiredUsesLibs, optionalUsesLibs := a.classLoaderContexts.UsesLibs()
+	for _, usesLib := range requiredUsesLibs {
+		a.usesLibrary.addLib(usesLib, false)
+	}
+	for _, usesLib := range optionalUsesLibs {
+		a.usesLibrary.addLib(usesLib, true)
 	}
 
 	// Check that the <uses-library> list is coherent with the manifest.
@@ -1220,17 +1224,17 @@
 
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
 	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
-		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
-		ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
+		ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
+		ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...)
 		// Only add these extra dependencies if the module depends on framework libs. This avoids
 		// creating a cyclic dependency:
 		//     e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
 		if hasFrameworkLibs {
 			// Dexpreopt needs paths to the dex jars of these libraries in order to construct
 			// class loader context for dex2oat. Add them as a dependency with a special tag.
-			ctx.AddVariationDependencies(nil, usesLibCompat29Tag, dexpreopt.CompatUsesLibs29...)
-			ctx.AddVariationDependencies(nil, usesLibCompat28Tag, dexpreopt.OptionalCompatUsesLibs28...)
-			ctx.AddVariationDependencies(nil, usesLibCompat30Tag, dexpreopt.OptionalCompatUsesLibs30...)
+			ctx.AddVariationDependencies(nil, usesLibCompat29ReqTag, dexpreopt.CompatUsesLibs29...)
+			ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
+			ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
 	}
 }
@@ -1289,7 +1293,7 @@
 				replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
 				replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
 			}
-			clcMap.AddContext(ctx, tag.sdkVersion, libName,
+			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
 				lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
diff --git a/java/app_import.go b/java/app_import.go
index 5a87b07..b5a6084 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -410,6 +410,10 @@
 	return android.SdkSpecPrivate
 }
 
+func (a *AndroidAppImport) LintDepSets() LintDepSets {
+	return LintDepSets{}
+}
+
 var _ android.ApexModule = (*AndroidAppImport)(nil)
 
 // Implements android.ApexModule
diff --git a/java/app_test.go b/java/app_test.go
index 7997f7a..8de6691 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2355,8 +2355,8 @@
 		`--uses-library quuz ` +
 		`--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer
 		`--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer
-		`--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer
-		`--uses-library runtime-library`
+		`--uses-library runtime-library ` +
+		`--optional-uses-library bar` // TODO(b/132357300): "bar" should not be passed to manifest_fixer
 	android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
 
 	// Test that all libraries are verified (library order matters).
diff --git a/java/base.go b/java/base.go
index 6b81196..ea5b137 100644
--- a/java/base.go
+++ b/java/base.go
@@ -605,7 +605,12 @@
 		if dep != nil {
 			if component, ok := dep.(SdkLibraryComponentDependency); ok {
 				if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
-					ctx.AddVariationDependencies(nil, usesLibTag, *lib)
+					// Add library as optional if it's one of the optional compatibility libs.
+					tag := usesLibReqTag
+					if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) {
+						tag = usesLibOptTag
+					}
+					ctx.AddVariationDependencies(nil, tag, *lib)
 				}
 			}
 		}
@@ -1230,7 +1235,7 @@
 			}
 			// Dex compilation
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), implementationAndResourcesJar, jarName)
 			if ctx.Failed() {
 				return
 			}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 107d34a..bb542c4 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -579,6 +579,14 @@
 	common := ctx.Module().(commonBootclasspathFragment)
 	output := common.produceHiddenAPIOutput(ctx, contents, input)
 
+	// If the source or prebuilts module does not provide a signature patterns file then generate one
+	// from the flags.
+	// TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of
+	//  their own.
+	if output.SignaturePatternsPath == nil {
+		output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath)
+	}
+
 	// Initialize a HiddenAPIInfo structure.
 	hiddenAPIInfo := HiddenAPIInfo{
 		// The monolithic hidden API processing needs access to the flag files that override the default
@@ -744,9 +752,6 @@
 	// Flag files by *hiddenAPIFlagFileCategory
 	Flag_files_by_category FlagFilesByCategory
 
-	// The path to the generated stub-flags.csv file.
-	Stub_flags_path android.OptionalPath
-
 	// The path to the generated annotation-flags.csv file.
 	Annotation_flags_path android.OptionalPath
 
@@ -756,6 +761,12 @@
 	// The path to the generated index.csv file.
 	Index_path android.OptionalPath
 
+	// The path to the generated signature-patterns.csv file.
+	Signature_patterns_path android.OptionalPath
+
+	// The path to the generated stub-flags.csv file.
+	Stub_flags_path android.OptionalPath
+
 	// The path to the generated all-flags.csv file.
 	All_flags_path android.OptionalPath
 }
@@ -772,10 +783,12 @@
 	b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory
 
 	// Copy all the generated file paths.
-	b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath)
 	b.Annotation_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AnnotationFlagsPath)
 	b.Metadata_path = android.OptionalPathForPath(hiddenAPIInfo.MetadataPath)
 	b.Index_path = android.OptionalPathForPath(hiddenAPIInfo.IndexPath)
+
+	b.Signature_patterns_path = android.OptionalPathForPath(hiddenAPIInfo.SignaturePatternsPath)
+	b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath)
 	b.All_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AllFlagsPath)
 
 	// Copy stub_libs properties.
@@ -839,10 +852,11 @@
 	}
 
 	// Copy all the generated files, if available.
-	copyOptionalPath(b.Stub_flags_path, "stub_flags")
 	copyOptionalPath(b.Annotation_flags_path, "annotation_flags")
 	copyOptionalPath(b.Metadata_path, "metadata")
 	copyOptionalPath(b.Index_path, "index")
+	copyOptionalPath(b.Signature_patterns_path, "signature_patterns")
+	copyOptionalPath(b.Stub_flags_path, "stub_flags")
 	copyOptionalPath(b.All_flags_path, "all_flags")
 }
 
@@ -852,9 +866,6 @@
 // specific properties.
 type prebuiltBootclasspathFragmentProperties struct {
 	Hidden_api struct {
-		// The path to the stub-flags.csv file created by the bootclasspath_fragment.
-		Stub_flags *string `android:"path"`
-
 		// The path to the annotation-flags.csv file created by the bootclasspath_fragment.
 		Annotation_flags *string `android:"path"`
 
@@ -864,6 +875,12 @@
 		// The path to the index.csv file created by the bootclasspath_fragment.
 		Index *string `android:"path"`
 
+		// The path to the signature-patterns.csv file created by the bootclasspath_fragment.
+		Signature_patterns *string `android:"path"`
+
+		// The path to the stub-flags.csv file created by the bootclasspath_fragment.
+		Stub_flags *string `android:"path"`
+
 		// The path to the all-flags.csv file created by the bootclasspath_fragment.
 		All_flags *string `android:"path"`
 	}
@@ -892,6 +909,12 @@
 
 // produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
 func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
+	pathForOptionalSrc := func(src *string) android.Path {
+		if src == nil {
+			return nil
+		}
+		return android.PathForModuleSrc(ctx, *src)
+	}
 	pathForSrc := func(property string, src *string) android.Path {
 		if src == nil {
 			ctx.PropertyErrorf(property, "is required but was not specified")
@@ -906,11 +929,12 @@
 
 	output := HiddenAPIOutput{
 		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
-			AnnotationFlagsPath: pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags),
-			MetadataPath:        pathForSrc("hidden_api.metadata", module.prebuiltProperties.Hidden_api.Metadata),
-			IndexPath:           pathForSrc("hidden_api.index", module.prebuiltProperties.Hidden_api.Index),
-			StubFlagsPath:       pathForSrc("hidden_api.stub_flags", module.prebuiltProperties.Hidden_api.Stub_flags),
-			AllFlagsPath:        pathForSrc("hidden_api.all_flags", module.prebuiltProperties.Hidden_api.All_flags),
+			AnnotationFlagsPath:   pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags),
+			MetadataPath:          pathForSrc("hidden_api.metadata", module.prebuiltProperties.Hidden_api.Metadata),
+			IndexPath:             pathForSrc("hidden_api.index", module.prebuiltProperties.Hidden_api.Index),
+			SignaturePatternsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Signature_patterns),
+			StubFlagsPath:         pathForSrc("hidden_api.stub_flags", module.prebuiltProperties.Hidden_api.Stub_flags),
+			AllFlagsPath:          pathForSrc("hidden_api.all_flags", module.prebuiltProperties.Hidden_api.All_flags),
 		},
 		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 3d0e155..d3de675 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -164,6 +164,7 @@
 		prepareForTestWithBootclasspathFragment,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "mycoveragestubs"),
+		FixtureConfigureApexBootJars("someapex:mybootlib"),
 		prepareWithBp,
 	)
 
@@ -186,6 +187,7 @@
 		prepareForTestWithBootclasspathFragment,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
+		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
 	).RunTestWithBp(t, `
 		bootclasspath_fragment {
 			name: "myfragment",
diff --git a/java/config/config.go b/java/config/config.go
index 273084c..30c6f91 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -69,8 +69,6 @@
 	pctx.StaticVariable("JavacHeapSize", "2048M")
 	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
 	pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
-	// TODO(b/181095653): remove duplicated flags.
-	pctx.StaticVariable("DexJavaFlags", "-XX:OnError='cat hs_err_pid%p.log' -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads -Xmx2G")
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
diff --git a/java/dex.go b/java/dex.go
index 6bf0143..667800f 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -84,19 +84,17 @@
 	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
 }
 
-func init() {
-	pctx.HostBinToolVariable("runWithTimeoutCmd", "run_with_timeout")
-	pctx.SourcePathVariable("jstackCmd", "${config.JavaToolchain}/jstack")
-}
-
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`mkdir -p $$(dirname $tmpJar) && ` +
+			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
+			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
+			"${config.Zip2ZipCmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
@@ -115,17 +113,16 @@
 			ExecStrategy: "${config.RED8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
+	}, []string{"outDir", "d8Flags", "zipFlags", "tmpJar"}, nil)
 
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
 			`mkdir -p $$(dirname ${outUsage}) && ` +
-			// TODO(b/181095653): remove R8 timeout and go back to config.R8Cmd.
-			`${runWithTimeoutCmd} -timeout 30m -on_timeout '${jstackCmd} $$PID' -- ` +
-			`$r8Template${config.JavaCmd} ${config.DexJavaFlags} -cp ${config.R8Jar} ` +
-			`com.android.tools.r8.compatproguard.CompatProguard -injars $in --output $outDir ` +
+			`mkdir -p $$(dirname $tmpJar) && ` +
+			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
+			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $tmpJar --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
 			`-printusage ${outUsage} ` +
@@ -136,10 +133,10 @@
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
-			"${config.R8Jar}",
+			"${config.R8Cmd}",
+			"${config.Zip2ZipCmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
-			"${runWithTimeoutCmd}",
 		},
 	}, map[string]*remoteexec.REParams{
 		"$r8Template": &remoteexec.REParams{
@@ -165,7 +162,7 @@
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
 	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
-		"r8Flags", "zipFlags"}, []string{"implicits"})
+		"r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string {
 	flags := d.dexProperties.Dxflags
@@ -282,6 +279,7 @@
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
+	tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", jarName)
 
 	zipFlags := "--ignore_missing_files"
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
@@ -309,6 +307,7 @@
 			"outUsage":    proguardUsage.String(),
 			"outUsageZip": proguardUsageZip.String(),
 			"outDir":      outDir.String(),
+			"tmpJar":      tmpJar.String(),
 		}
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
 			rule = r8RE
@@ -339,6 +338,7 @@
 				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags": zipFlags,
 				"outDir":   outDir.String(),
+				"tmpJar":   tmpJar.String(),
 			},
 		})
 	}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 86ab825..8e39f40 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -297,7 +297,7 @@
 //
 // The rule is initialized but not built so that the caller can modify it and select an appropriate
 // name.
-func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, moduleStubFlagsPaths android.Paths) {
+func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, stubFlagSubsets SignatureCsvSubsets) {
 	// Singleton rule which applies hiddenapi on all boot class path dex files.
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -317,7 +317,7 @@
 
 	// If no module stub flags paths are provided then this must be being called for a
 	// bootclasspath_fragment and not the whole platform_bootclasspath.
-	if moduleStubFlagsPaths == nil {
+	if stubFlagSubsets == nil {
 		// This is being run on a fragment of the bootclasspath.
 		command.Flag("--fragment")
 	}
@@ -342,8 +342,8 @@
 
 	// If there are stub flag files that have been generated by fragments on which this depends then
 	// use them to validate the stub flag file generated by the rules created by this method.
-	if len(moduleStubFlagsPaths) > 0 {
-		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, moduleStubFlagsPaths)
+	if len(stubFlagSubsets) > 0 {
+		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets)
 
 		// Add the file that indicates that the file generated by this is valid.
 		//
@@ -546,6 +546,20 @@
 	}
 }
 
+// StubFlagSubset returns a SignatureCsvSubset that contains a path to a stub-flags.csv file and a
+// path to a signature-patterns.csv file that defines a subset of the monolithic stub flags file,
+// i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared.
+func (i *HiddenAPIInfo) StubFlagSubset() SignatureCsvSubset {
+	return SignatureCsvSubset{i.StubFlagsPath, i.SignaturePatternsPath}
+}
+
+// FlagSubset returns a SignatureCsvSubset that contains a path to an all-flags.csv file and a
+// path to a signature-patterns.csv file that defines a subset of the monolithic flags file, i.e.
+// out/soong/hiddenapi/hiddenapi-flags.csv, against which it will be compared.
+func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset {
+	return SignatureCsvSubset{i.AllFlagsPath, i.SignaturePatternsPath}
+}
+
 var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{})
 
 // ModuleStubDexJars contains the stub dex jars provided by a single module.
@@ -782,6 +796,10 @@
 
 	// The path to the generated all-flags.csv file.
 	AllFlagsPath android.Path
+
+	// The path to the generated signature-patterns.txt file which defines the subset of the
+	// monolithic hidden API files provided in this.
+	SignaturePatternsPath android.Path
 }
 
 // bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
@@ -848,7 +866,7 @@
 // the annotationFlags.
 func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
 	outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
-	flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) {
+	flagFilesByCategory FlagFilesByCategory, flagSubsets SignatureCsvSubsets, generatedRemovedDexSignatures android.OptionalPath) {
 
 	// Create the rule that will generate the flag files.
 	tempPath := tempPathForRestat(ctx, outputPath)
@@ -877,8 +895,8 @@
 
 	// If there are flag files that have been generated by fragments on which this depends then use
 	// them to validate the flag file generated by the rules created by this method.
-	if len(allFlagsPaths) > 0 {
-		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, allFlagsPaths)
+	if len(flagSubsets) > 0 {
+		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets)
 
 		// Add the file that indicates that the file generated by this is valid.
 		//
@@ -890,20 +908,82 @@
 	rule.Build(name, desc)
 }
 
+// SignatureCsvSubset describes a subset of a monolithic flags file, i.e. either
+// out/soong/hiddenapi/hiddenapi-stub-flags.txt or out/soong/hiddenapi/hiddenapi-flags.csv
+type SignatureCsvSubset struct {
+	// The path to the CSV file containing hidden API flags.
+	//
+	// It has the dex member signature as the first column, with flags, one per column, in the
+	// subsequent columns.
+	CsvFile android.Path
+
+	// The path to the CSV file containing the signature patterns.
+	//
+	// It is a single column CSV file with the column containing a signature pattern.
+	SignaturePatternsFile android.Path
+}
+
+type SignatureCsvSubsets []SignatureCsvSubset
+
+func (s SignatureCsvSubsets) RelativeToTop() []string {
+	result := []string{}
+	for _, subset := range s {
+		result = append(result, fmt.Sprintf("%s:%s", subset.CsvFile.RelativeToTop(), subset.SignaturePatternsFile.RelativeToTop()))
+	}
+	return result
+}
+
+// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
+// patterns that will select a subset of the monolithic flags.
+func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path) android.Path {
+	patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv")
+	// Create a rule to validate the output from the following rule.
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("signature_patterns").
+		FlagWithInput("--flags ", flagsPath).
+		FlagWithOutput("--output ", patternsFile)
+	rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns")
+
+	return patternsFile
+}
+
+// buildRuleRemoveBlockedFlag creates a rule that will remove entries from the input path which
+// only have blocked flags. It will not remove entries that have blocked as well as other flags,
+// e.g. blocked,core-platform-api.
+func buildRuleRemoveBlockedFlag(ctx android.BuilderContext, name string, desc string, inputPath android.Path, filteredPath android.WritablePath) {
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		Text(`grep -vE "^[^,]+,blocked$"`).Input(inputPath).Text(">").Output(filteredPath).
+		// Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
+		// something and 1 (build failure) when it does not and 2 (when it encounters an error).
+		// However, while it is unlikely it is not an error if this does not find any matches. The
+		// following will only run if the grep does not find something and in that case it will treat
+		// an exit code of 1 as success and anything else as failure.
+		Text("|| test $? -eq 1")
+	rule.Build(name, desc)
+}
+
 // buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
 // by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
-func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, modularFilePaths android.Paths) android.WritablePath {
+func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets) android.WritablePath {
 	// The file which is used to record that the flags file is valid.
 	validFile := pathForValidation(ctx, monolithicFilePath)
 
 	// Create a rule to validate the output from the following rule.
 	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
+	command := rule.Command().
 		BuiltTool("verify_overlaps").
-		Input(monolithicFilePath).
-		Inputs(modularFilePaths).
-		// If validation passes then update the file that records that.
-		Text("&& touch").Output(validFile)
+		Input(monolithicFilePath)
+
+	for _, subset := range csvSubsets {
+		command.
+			Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile).
+			Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile)
+	}
+
+	// If validation passes then update the file that records that.
+	command.Text("&& touch").Output(validFile)
 	rule.Build(name+"Validation", desc+" validation")
 
 	return validFile
@@ -972,14 +1052,24 @@
 		encodedBootDexJarsByModule[name] = encodedDex
 	}
 
+	// Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
+	// compared against the monolithic stub flags.
+	filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
+	buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredStubFlags", "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV)
+
+	// Generate the filtered-flags.csv file which contains the filtered flags that will be compared
+	// against the monolithic flags.
+	filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
+	buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredFlags", "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV)
+
 	// Store the paths in the info for use by other modules and sdk snapshot generation.
 	output := HiddenAPIOutput{
 		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
-			StubFlagsPath:       stubFlagsCSV,
+			StubFlagsPath:       filteredStubFlagsCSV,
 			AnnotationFlagsPath: annotationFlagsCSV,
 			MetadataPath:        metadataCSV,
 			IndexPath:           indexCSV,
-			AllFlagsPath:        allFlagsCSV,
+			AllFlagsPath:        filteredFlagsCSV,
 		},
 		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index 404b4c1..5956e3c 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -29,9 +29,6 @@
 	// that category.
 	FlagsFilesByCategory FlagFilesByCategory
 
-	// The paths to the generated stub-flags.csv files.
-	StubFlagsPaths android.Paths
-
 	// The paths to the generated annotation-flags.csv files.
 	AnnotationFlagsPaths android.Paths
 
@@ -41,8 +38,13 @@
 	// The paths to the generated index.csv files.
 	IndexPaths android.Paths
 
-	// The paths to the generated all-flags.csv files.
-	AllFlagsPaths android.Paths
+	// The subsets of the monolithic hiddenapi-stubs-flags.txt file that are provided by each
+	// bootclasspath_fragment modules.
+	StubFlagSubsets SignatureCsvSubsets
+
+	// The subsets of the monolithic hiddenapi-flags.csv file that are provided by each
+	// bootclasspath_fragment modules.
+	FlagSubsets SignatureCsvSubsets
 
 	// The classes jars from the libraries on the platform bootclasspath.
 	ClassesJars android.Paths
@@ -80,11 +82,12 @@
 // append appends all the files from the supplied info to the corresponding files in this struct.
 func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) {
 	i.FlagsFilesByCategory.append(other.FlagFilesByCategory)
-	i.StubFlagsPaths = append(i.StubFlagsPaths, other.StubFlagsPath)
 	i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
 	i.MetadataPaths = append(i.MetadataPaths, other.MetadataPath)
 	i.IndexPaths = append(i.IndexPaths, other.IndexPath)
-	i.AllFlagsPaths = append(i.AllFlagsPaths, other.AllFlagsPath)
+
+	i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset())
+	i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset())
 }
 
 var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
diff --git a/java/java.go b/java/java.go
index b6e2a54..5bf3d79 100644
--- a/java/java.go
+++ b/java/java.go
@@ -248,13 +248,15 @@
 
 type usesLibraryDependencyTag struct {
 	dependencyTag
-	sdkVersion int // SDK version in which the library appared as a standalone library.
+	sdkVersion int  // SDK version in which the library appared as a standalone library.
+	optional   bool // If the dependency is optional or required.
 }
 
-func makeUsesLibraryDependencyTag(sdkVersion int) usesLibraryDependencyTag {
+func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDependencyTag {
 	return usesLibraryDependencyTag{
 		dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)},
 		sdkVersion:    sdkVersion,
+		optional:      optional,
 	}
 }
 
@@ -283,10 +285,11 @@
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	jniInstallTag           = installDependencyTag{name: "jni install"}
 	binaryInstallTag        = installDependencyTag{name: "binary install"}
-	usesLibTag              = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
-	usesLibCompat28Tag      = makeUsesLibraryDependencyTag(28)
-	usesLibCompat29Tag      = makeUsesLibraryDependencyTag(29)
-	usesLibCompat30Tag      = makeUsesLibraryDependencyTag(30)
+	usesLibReqTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false)
+	usesLibOptTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true)
+	usesLibCompat28OptTag   = makeUsesLibraryDependencyTag(28, true)
+	usesLibCompat29ReqTag   = makeUsesLibraryDependencyTag(29, false)
+	usesLibCompat30OptTag   = makeUsesLibraryDependencyTag(30, true)
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -1807,8 +1810,10 @@
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
-	if depTag == libTag || depTag == usesLibTag {
+	if depTag == libTag {
 		// Ok, propagate <uses-library> through non-static library dependencies.
+	} else if tag, ok := depTag.(usesLibraryDependencyTag); ok && tag.sdkVersion == dexpreopt.AnySdkVersion {
+		// Ok, propagate <uses-library> through non-compatibility <uses-library> dependencies.
 	} else if depTag == staticLibTag {
 		// Propagate <uses-library> through static library dependencies, unless it is a component
 		// library (such as stubs). Component libraries have a dependency on their SDK library,
@@ -1826,7 +1831,7 @@
 	// <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies
 	// from its CLC should be added to the current CLC.
 	if sdkLib != nil {
-		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib,
+		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false,
 			dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 3ff4c77..36baf7e 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -316,7 +316,7 @@
 
 	// Generate the monolithic stub-flags.csv file.
 	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
-	buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagsPaths)
+	buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagSubsets)
 
 	// Generate the annotation-flags.csv file from all the module annotations.
 	annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv")
@@ -329,7 +329,7 @@
 	allAnnotationFlagFiles := android.Paths{annotationFlags}
 	allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...)
 	allFlags := hiddenAPISingletonPaths(ctx).flags
-	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{})
+	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.FlagSubsets, android.OptionalPath{})
 
 	// Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations
 	// in the source code.
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 8e0618e..eeec504 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -156,8 +156,9 @@
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := result.ModuleForTests("qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
-		sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
-		android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs)
+		requiredSdkLibs, optionalSdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
+		android.AssertDeepEquals(t, "qux exports (required)", []string{"foo", "bar", "fred", "quuz"}, requiredSdkLibs)
+		android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs)
 	}
 }
 
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 7ceac41..30888c7 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -419,14 +419,7 @@
 		{"TARGET_COPY_OUT_TEST_HARNESS_RAMDISK", "test_harness_ramdisk"},
 		{"TARGET_COPY_OUT_ROOT", "root"},
 		{"TARGET_COPY_OUT_RECOVERY", "recovery"},
-		{"TARGET_COPY_OUT_VENDOR", "||VENDOR-PATH-PH||"},
 		{"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
-		{"TARGET_COPY_OUT_PRODUCT", "||PRODUCT-PATH-PH||"},
-		{"TARGET_COPY_OUT_PRODUCT_SERVICES", "||PRODUCT-PATH-PH||"},
-		{"TARGET_COPY_OUT_SYSTEM_EXT", "||SYSTEM_EXT-PATH-PH||"},
-		{"TARGET_COPY_OUT_ODM", "||ODM-PATH-PH||"},
-		{"TARGET_COPY_OUT_VENDOR_DLKM", "||VENDOR_DLKM-PATH-PH||"},
-		{"TARGET_COPY_OUT_ODM_DLKM", "||ODM_DLKM-PATH-PH||"},
 		// TODO(asmundak): to process internal config files, we need the following variables:
 		//    BOARD_CONFIG_VENDOR_PATH
 		//    TARGET_VENDOR
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index a14c7a4..ebaae28 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -259,7 +259,7 @@
 ifdef PRODUCT_NAME
 # Comment
 else
-  TARGET_COPY_OUT_VENDOR := foo
+  TARGET_COPY_OUT_RECOVERY := foo
 endif
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -270,7 +270,7 @@
     # Comment
     pass
   else:
-    # MK2RBC TRANSLATION ERROR: cannot set predefined variable TARGET_COPY_OUT_VENDOR to "foo", its value should be "||VENDOR-PATH-PH||"
+    # MK2RBC TRANSLATION ERROR: cannot set predefined variable TARGET_COPY_OUT_RECOVERY to "foo", its value should be "recovery"
     pass
   rblf.warning("product.mk", "partially successful conversion")
 `,
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 3470e51..be9e71e 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r416183b"
+	bindgenClangVersion = "clang-r428724"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/builder.go b/rust/builder.go
index a5b3ab9..426a569 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -269,6 +269,17 @@
 
 	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
 
+	if ctx.RustModule().compiler.CargoEnvCompat() {
+		if _, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
+			envVars = append(envVars, "CARGO_BIN_NAME="+strings.TrimSuffix(outputFile.Base(), outputFile.Ext()))
+		}
+		envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName())
+		pkgVersion := ctx.RustModule().compiler.CargoPkgVersion()
+		if pkgVersion != "" {
+			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
+		}
+	}
+
 	if flags.Clippy {
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
 		ctx.Build(pctx, android.BuildParams{
diff --git a/rust/compiler.go b/rust/compiler.go
index de59f39..d9e21ff 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -65,7 +65,11 @@
 )
 
 type BaseCompilerProperties struct {
-	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs)
+	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs).
+	// Only a single source file can be defined. Modules which generate source can be included by prefixing
+	// the module name with ":", for example ":libfoo_bindgen"
+	//
+	// If no source file is defined, a single generated source module can be defined to be used as the main source.
 	Srcs []string `android:"path,arch_variant"`
 
 	// name of the lint set that should be used to validate this module.
@@ -154,6 +158,14 @@
 	// linkage if all dependencies of the root binary module do not link against libstd\
 	// the same way.
 	Prefer_rlib *bool `android:"arch_variant"`
+
+	// Enables emitting certain Cargo environment variables. Only intended to be used for compatibility purposes.
+	// Will set CARGO_CRATE_NAME to the crate_name property's value.
+	// Will set CARGO_BIN_NAME to the output filename value without the extension.
+	Cargo_env_compat *bool
+
+	// If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value.
+	Cargo_pkg_version *string
 }
 
 type baseCompiler struct {
@@ -309,6 +321,14 @@
 	return android.OptionalPathForPath(compiler.cargoOutDir)
 }
 
+func (compiler *baseCompiler) CargoEnvCompat() bool {
+	return Bool(compiler.Properties.Cargo_env_compat)
+}
+
+func (compiler *baseCompiler) CargoPkgVersion() string {
+	return String(compiler.Properties.Cargo_pkg_version)
+}
+
 func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath {
 	return compiler.strippedOutputFile
 }
@@ -346,7 +366,9 @@
 	} else {
 		deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
 	}
-
+	if ctx.RustModule().StaticExecutable() {
+		deps.StaticLibs = append(deps.StaticLibs, "libunwind")
+	}
 	if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
 		deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
 	}
@@ -420,12 +442,18 @@
 			srcIndex = i
 		}
 	}
-	if numSrcs != 1 {
+	if numSrcs > 1 {
 		ctx.PropertyErrorf("srcs", incorrectSourcesError)
 	}
+
+	// If a main source file is not provided we expect only a single SourceProvider module to be defined
+	// within srcs, with the expectation that the first source it provides is the entry point.
 	if srcIndex != 0 {
 		ctx.PropertyErrorf("srcs", "main source file must be the first in srcs")
+	} else if numSrcs > 1 {
+		ctx.PropertyErrorf("srcs", "only a single generated source module can be defined without a main source file.")
 	}
+
 	paths := android.PathsForModuleSrc(ctx, srcs)
 	return paths[srcIndex], paths[1:]
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index c331b4c..f589b69 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -98,6 +98,30 @@
 		}`)
 }
 
+// Test environment vars for Cargo compat are set.
+func TestCargoCompat(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			cargo_env_compat: true,
+			cargo_pkg_version: "1.0.0"
+		}`)
+
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
+
+	if !strings.Contains(fizz.Args["envVars"], "CARGO_BIN_NAME=fizz") {
+		t.Fatalf("expected 'CARGO_BIN_NAME=fizz' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	}
+	if !strings.Contains(fizz.Args["envVars"], "CARGO_CRATE_NAME=foo") {
+		t.Fatalf("expected 'CARGO_CRATE_NAME=foo' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	}
+	if !strings.Contains(fizz.Args["envVars"], "CARGO_PKG_VERSION=1.0.0") {
+		t.Fatalf("expected 'CARGO_PKG_VERSION=1.0.0' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	}
+}
+
 func TestInstallDir(t *testing.T) {
 	ctx := testRust(t, `
 		rust_library_dylib {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index e527aea..926d2ac 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -12,6 +12,7 @@
 		"external/libchromeos-rs",
 		"external/minijail",
 		"external/rust",
+		"external/selinux/libselinux",
 		"external/vm_tools/p9",
 		"frameworks/native/libs/binder/rust",
 		"frameworks/proto_logging/stats",
diff --git a/rust/config/global.go b/rust/config/global.go
index c390711..e5b334d 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.53.0"
+	RustDefaultVersion = "1.54.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/coverage.go b/rust/coverage.go
index dac526a..050b811 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -55,7 +55,7 @@
 		flags.Coverage = true
 		coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
 		flags.RustFlags = append(flags.RustFlags,
-			"-Z instrument-coverage", "-g", "-C link-dead-code")
+			"-Z instrument-coverage", "-g")
 		flags.LinkFlags = append(flags.LinkFlags,
 			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 4b6c9d4..f3cd375 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -56,7 +56,7 @@
 	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
 	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
 
-	rustcCoverageFlags := []string{"-Z instrument-coverage", " -g ", "-C link-dead-code"}
+	rustcCoverageFlags := []string{"-Z instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
 		missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 18b2513..5fb56ff 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/fuzz"
 	"android/soong/rust/config"
 )
 
@@ -32,7 +33,7 @@
 type fuzzDecorator struct {
 	*binaryDecorator
 
-	fuzzPackagedModule cc.FuzzPackagedModule
+	fuzzPackagedModule fuzz.FuzzPackagedModule
 }
 
 var _ compiler = (*binaryDecorator)(nil)
@@ -96,7 +97,7 @@
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
 type rustFuzzPackager struct {
-	cc.FuzzPackager
+	fuzz.FuzzPackager
 }
 
 func rustFuzzPackagingFactory() android.Singleton {
@@ -105,7 +106,7 @@
 
 func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination.
-	archDirs := make(map[cc.ArchOs][]cc.FileToZip)
+	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
 	// List of individual fuzz targets.
 	s.FuzzTargets = make(map[string]bool)
@@ -117,7 +118,7 @@
 			return
 		}
 
-		if ok := cc.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
+		if ok := fuzz.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
 			return
 		}
 
@@ -133,16 +134,16 @@
 
 		archString := rustModule.Arch().ArchType.String()
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
-		archOs := cc.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
-		var files []cc.FileToZip
+		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
 		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
 		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// The executable.
-		files = append(files, cc.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
+		files = append(files, fuzz.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
@@ -150,7 +151,7 @@
 		}
 
 	})
-	s.CreateFuzzPackage(ctx, archDirs, cc.Rust)
+	s.CreateFuzzPackage(ctx, archDirs, fuzz.Rust, pctx)
 }
 
 func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
diff --git a/rust/project_json.go b/rust/project_json.go
index c28bc7b..faa7db5 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -54,7 +54,6 @@
 }
 
 type rustProjectJson struct {
-	Roots  []string           `json:"roots"`
 	Crates []rustProjectCrate `json:"crates"`
 }
 
@@ -248,9 +247,6 @@
 	idx := len(singleton.project.Crates)
 	singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps}
 	singleton.project.Crates = append(singleton.project.Crates, crate)
-	// rust-analyzer requires that all crates belong to at least one root:
-	// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
-	singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule))
 	return idx, true
 }
 
diff --git a/rust/rust.go b/rust/rust.go
index 80be496..4ceeef1 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -25,6 +25,7 @@
 	"android/soong/bloaty"
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
+	"android/soong/fuzz"
 	"android/soong/rust/config"
 )
 
@@ -123,7 +124,7 @@
 }
 
 type Module struct {
-	cc.FuzzModule
+	fuzz.FuzzModule
 
 	VendorProperties cc.VendorProperties
 
@@ -433,6 +434,12 @@
 	// copied. This is equivalent to Cargo's OUT_DIR variable.
 	CargoOutDir() android.OptionalPath
 
+	// CargoPkgVersion returns the value of the Cargo_pkg_version property.
+	CargoPkgVersion() string
+
+	// CargoEnvCompat returns whether Cargo environment variables should be used.
+	CargoEnvCompat() bool
+
 	inData() bool
 	install(ctx ModuleContext)
 	relativeInstallPath() string
diff --git a/scripts/get_clang_version.py b/scripts/get_clang_version.py
index 622fca1..f6efc5f 100755
--- a/scripts/get_clang_version.py
+++ b/scripts/get_clang_version.py
@@ -30,7 +30,7 @@
   with open(global_go) as infile:
     contents = infile.read()
 
-  regex_rev = r'\tClangDefaultVersion\s+= "clang-(?P<rev>r\d+[a-z]?\d?)"'
+  regex_rev = r'\tClangDefaultVersion\s+= "(?P<rev>clang-r\d+[a-z]?\d?)"'
   match_rev = re.search(regex_rev, contents)
   if match_rev is None:
     raise RuntimeError('Parsing clang info failed')
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index c50dc24..7ffda62 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -83,3 +83,60 @@
         },
     },
 }
+
+python_test_host {
+    name: "verify_overlaps_test",
+    main: "verify_overlaps_test.py",
+    srcs: [
+        "verify_overlaps.py",
+        "verify_overlaps_test.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+    test_options: {
+        unit_test: true,
+    },
+}
+
+python_binary_host {
+    name: "signature_patterns",
+    main: "signature_patterns.py",
+    srcs: ["signature_patterns.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+}
+
+python_test_host {
+    name: "signature_patterns_test",
+    main: "signature_patterns_test.py",
+    srcs: [
+        "signature_patterns.py",
+        "signature_patterns_test.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+    test_options: {
+        unit_test: true,
+    },
+}
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
new file mode 100755
index 0000000..a7c5bb4
--- /dev/null
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# 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.
+"""
+Generate a set of signature patterns from the modular flags generated by a
+bootclasspath_fragment that can be used to select a subset of monolithic flags
+against which the modular flags can be compared.
+"""
+
+import argparse
+import csv
+
+def dict_reader(input):
+    return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
+
+def produce_patterns_from_file(file):
+    with open(file, 'r') as f:
+        return produce_patterns_from_stream(f)
+
+def produce_patterns_from_stream(stream):
+    # Read in all the signatures into a list and remove member names.
+    patterns = set()
+    for row in dict_reader(stream):
+        signature = row['signature']
+        text = signature.removeprefix("L")
+        # Remove the class specific member signature
+        pieces = text.split(";->")
+        qualifiedClassName = pieces[0]
+            # Remove inner class names as they cannot be separated from the containing outer class.
+        pieces = qualifiedClassName.split("$", maxsplit=1)
+        pattern = pieces[0]
+        patterns.add(pattern)
+
+    patterns = list(patterns)
+    patterns.sort()
+    return patterns
+
+def main(args):
+    args_parser = argparse.ArgumentParser(description='Generate a set of signature patterns that select a subset of monolithic hidden API files.')
+    args_parser.add_argument('--flags', help='The stub flags file which contains an entry for every dex member')
+    args_parser.add_argument('--output', help='Generated signature prefixes')
+    args = args_parser.parse_args(args)
+
+    # Read in all the patterns into a list.
+    patterns = produce_patterns_from_file(args.flags)
+
+    # Write out all the patterns.
+    with open(args.output, 'w') as outputFile:
+        for pattern in patterns:
+            outputFile.write(pattern)
+            outputFile.write("\n")
+
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
new file mode 100755
index 0000000..0431f45
--- /dev/null
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Unit tests for signature_patterns.py."""
+import io
+import unittest
+
+from signature_patterns import *
+
+class TestGeneratedPatterns(unittest.TestCase):
+
+    def produce_patterns_from_string(self, csv):
+        with io.StringIO(csv) as f:
+            return produce_patterns_from_stream(f)
+
+    def test_generate(self):
+        patterns = self.produce_patterns_from_string('''
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+''')
+        expected = [
+            "java/lang/Character",
+            "java/lang/Object",
+            "java/lang/ProcessBuilder",
+        ]
+        self.assertEqual(expected, patterns)
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index bb0917e..6432bf1 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -19,51 +19,361 @@
 
 import argparse
 import csv
+import sys
+from itertools import chain
 
-args_parser = argparse.ArgumentParser(description='Verify that one set of hidden API flags is a subset of another.')
-args_parser.add_argument('all', help='All the flags')
-args_parser.add_argument('subsets', nargs=argparse.REMAINDER, help='Subsets of the flags')
-args = args_parser.parse_args()
+class InteriorNode:
+    """
+    An interior node in a trie.
 
+    Each interior node has a dict that maps from an element of a signature to
+    either another interior node or a leaf. Each interior node represents either
+    a package, class or nested class. Class members are represented by a Leaf.
+
+    Associating the set of flags [public-api] with the signature
+    "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
+    nodes to be created:
+    Node()
+    ^- package:java -> Node()
+       ^- package:lang -> Node()
+           ^- class:Object -> Node()
+              ^- member:String()Ljava/lang/String; -> Leaf([public-api])
+
+    Associating the set of flags [blocked,core-platform-api] with the signature
+    "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
+    will cause the following nodes to be created:
+    Node()
+    ^- package:java -> Node()
+       ^- package:lang -> Node()
+           ^- class:Character -> Node()
+              ^- class:UnicodeScript -> Node()
+                 ^- member:of(I)Ljava/lang/Character$UnicodeScript;
+                    -> Leaf([blocked,core-platform-api])
+
+    Attributes:
+        nodes: a dict from an element of the signature to the Node/Leaf
+        containing the next element/value.
+    """
+    def __init__(self):
+        self.nodes = {}
+
+    def signatureToElements(self, signature):
+        """
+        Split a signature or a prefix into a number of elements:
+        1. The packages (excluding the leading L preceding the first package).
+        2. The class names, from outermost to innermost.
+        3. The member signature.
+
+        e.g. Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+        will be broken down into these elements:
+        1. package:java
+        2. package:lang
+        3. class:Character
+        4. class:UnicodeScript
+        5. member:of(I)Ljava/lang/Character$UnicodeScript;
+        """
+        # Remove the leading L.
+        #  - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+        text = signature.removeprefix("L")
+        # Split the signature between qualified class name and the class member
+        # signature.
+        #  0 - java/lang/Character$UnicodeScript
+        #  1 - of(I)Ljava/lang/Character$UnicodeScript;
+        parts = text.split(";->")
+        member = parts[1:]
+        # Split the qualified class name into packages, and class name.
+        #  0 - java
+        #  1 - lang
+        #  2 - Character$UnicodeScript
+        elements = parts[0].split("/")
+        packages = elements[0:-1]
+        className = elements[-1]
+        if className == "*" or className == "**":
+            # Cannot specify a wildcard and target a specific member
+            if len(member) != 0:
+                raise Exception("Invalid signature %s: contains wildcard %s and member signature %s"
+                                % (signature, className, member[0]))
+            wildcard = [className]
+            # Assemble the parts into a single list, adding prefixes to identify
+            # the different parts.
+            #  0 - package:java
+            #  1 - package:lang
+            #  2 - *
+            return list(chain(map(lambda x : "package:" + x, packages),
+                              wildcard))
+        else:
+            # Split the class name into outer / inner classes
+            #  0 - Character
+            #  1 - UnicodeScript
+            classes = className.split("$")
+            # Assemble the parts into a single list, adding prefixes to identify
+            # the different parts.
+            #  0 - package:java
+            #  1 - package:lang
+            #  2 - class:Character
+            #  3 - class:UnicodeScript
+            #  4 - member:of(I)Ljava/lang/Character$UnicodeScript;
+            return list(chain(map(lambda x : "package:" + x, packages),
+                              map(lambda x : "class:" + x, classes),
+                              map(lambda x : "member:" + x, member)))
+
+    def add(self, signature, value):
+        """
+        Associate the value with the specific signature.
+        :param signature: the member signature
+        :param value: the value to associated with the signature
+        :return: n/a
+        """
+        # Split the signature into elements.
+        elements = self.signatureToElements(signature)
+        # Find the Node associated with the deepest class.
+        node = self
+        for element in elements[:-1]:
+            if element in node.nodes:
+                node = node.nodes[element]
+            else:
+                next = InteriorNode()
+                node.nodes[element] = next
+                node = next
+        # Add a Leaf containing the value and associate it with the member
+        # signature within the class.
+        lastElement = elements[-1]
+        if not lastElement.startswith("member:"):
+            raise Exception("Invalid signature: %s, does not identify a specific member" % signature)
+        if lastElement in node.nodes:
+            raise Exception("Duplicate signature: %s" % signature)
+        node.nodes[lastElement] = Leaf(value)
+
+    def getMatchingRows(self, pattern):
+        """
+        Get the values (plural) associated with the pattern.
+
+        e.g. If the pattern is a full signature then this will return a list
+        containing the value associated with that signature.
+
+        If the pattern is a class then this will return a list containing the
+        values associated with all members of that class.
+
+        If the pattern is a package then this will return a list containing the
+        values associated with all the members of all the classes in that
+        package and sub-packages.
+
+        If the pattern ends with "*" then the preceding part is treated as a
+        package and this will return a list containing the values associated
+        with all the members of all the classes in that package.
+
+        If the pattern ends with "**" then the preceding part is treated
+        as a package and this will return a list containing the values
+        associated with all the members of all the classes in that package and
+        all sub-packages.
+
+        :param pattern: the pattern which could be a complete signature or a
+        class, or package wildcard.
+        :return: an iterable containing all the values associated with the
+        pattern.
+        """
+        elements = self.signatureToElements(pattern)
+        node = self
+        # Include all values from this node and all its children.
+        selector = lambda x : True
+        lastElement = elements[-1]
+        if lastElement == "*" or lastElement == "**":
+            elements = elements[:-1]
+            if lastElement == "*":
+                # Do not include values from sub-packages.
+                selector = lambda x : not x.startswith("package:")
+        for element in elements:
+            if element in node.nodes:
+                node = node.nodes[element]
+            else:
+                return []
+        return chain.from_iterable(node.values(selector))
+
+    def values(self, selector):
+        """
+        :param selector: a function that can be applied to a key in the nodes
+        attribute to determine whether to return its values.
+        :return: A list of iterables of all the values associated with this
+        node and its children.
+        """
+        values = []
+        self.appendValues(values, selector)
+        return values
+
+    def appendValues(self, values, selector):
+        """
+        Append the values associated with this node and its children to the
+        list.
+
+        For each item (key, child) in nodes the child node's values are returned
+        if and only if the selector returns True when called on its key. A child
+        node's values are all the values associated with it and all its
+        descendant nodes.
+
+        :param selector: a function that can be applied to a key in the nodes
+        attribute to determine whether to return its values.
+        :param values: a list of a iterables of values.
+        """
+        for key, node in self.nodes.items():
+            if selector(key):
+                node.appendValues(values, lambda x : True)
+
+class Leaf:
+    """
+    A leaf of the trie
+
+    Attributes:
+        value: the value associated with this leaf.
+    """
+    def __init__(self, value):
+        self.value = value
+
+    def values(self, selector):
+        """
+        :return: A list of a list of the value associated with this node.
+        """
+        return [[self.value]]
+
+    def appendValues(self, values, selector):
+        """
+        Appends a list of the value associated with this node to the list.
+        :param values: a list of a iterables of values.
+        """
+        values.append([self.value])
 
 def dict_reader(input):
     return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
 
-# Read in all the flags into a dict indexed by signature
-allFlagsBySignature = {}
-with open(args.all, 'r') as allFlagsFile:
-    allFlagsReader = dict_reader(allFlagsFile)
-    for row in allFlagsReader:
+def read_flag_trie_from_file(file):
+    with open(file, 'r') as stream:
+        return read_flag_trie_from_stream(stream)
+
+def read_flag_trie_from_stream(stream):
+    trie = InteriorNode()
+    reader = dict_reader(stream)
+    for row in reader:
         signature = row['signature']
-        allFlagsBySignature[signature]=row
+        trie.add(signature, row)
+    return trie
 
-failed = False
-for subsetPath in args.subsets:
-    mismatchingSignatures = []
-    with open(subsetPath, 'r') as subsetFlagsFile:
-        subsetReader = dict_reader(subsetFlagsFile)
-        for row in subsetReader:
+def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, patternsFile):
+    """
+    Extract a subset of flags from the dict containing all the monolithic flags.
+
+    :param monolithicFlagsDict: the dict containing all the monolithic flags.
+    :param patternsFile: a file containing a list of signature patterns that
+    define the subset.
+    :return: the dict from signature to row.
+    """
+    with open(patternsFile, 'r') as stream:
+        return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream)
+
+def extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream):
+    """
+    Extract a subset of flags from the trie containing all the monolithic flags.
+
+    :param monolithicTrie: the trie containing all the monolithic flags.
+    :param stream: a stream containing a list of signature patterns that define
+    the subset.
+    :return: the dict from signature to row.
+    """
+    dict = {}
+    for pattern in stream:
+        pattern = pattern.rstrip()
+        rows = monolithicTrie.getMatchingRows(pattern)
+        for row in rows:
             signature = row['signature']
-            if signature in allFlagsBySignature:
-                allFlags = allFlagsBySignature.get(signature)
-                if allFlags != row:
-                    mismatchingSignatures.append((signature, row.get(None, []), allFlags.get(None, [])))
-            else:
-                mismatchingSignatures.append((signature, row.get(None, []), []))
+            dict[signature] = row
+    return dict
 
+def read_signature_csv_from_stream_as_dict(stream):
+    """
+    Read the csv contents from the stream into a dict. The first column is assumed to be the
+    signature and used as the key. The whole row is stored as the value.
 
-    if mismatchingSignatures:
-        failed = True
-        print("ERROR: Hidden API flags are inconsistent:")
-        print("< " + subsetPath)
-        print("> " + args.all)
-        for mismatch in mismatchingSignatures:
-            print()
-            print("< " + mismatch[0] + "," + ",".join(mismatch[1]))
-            if mismatch[2] != []:
-                print("> " + mismatch[0] + "," + ",".join(mismatch[2]))
-            else:
-                print("> " + mismatch[0] + " - missing")
+    :param stream: the csv contents to read
+    :return: the dict from signature to row.
+    """
+    dict = {}
+    reader = dict_reader(stream)
+    for row in reader:
+        signature = row['signature']
+        dict[signature] = row
+    return dict
 
-if failed:
-    sys.exit(1)
+def read_signature_csv_from_file_as_dict(csvFile):
+    """
+    Read the csvFile into a dict. The first column is assumed to be the
+    signature and used as the key. The whole row is stored as the value.
+
+    :param csvFile: the csv file to read
+    :return: the dict from signature to row.
+    """
+    with open(csvFile, 'r') as f:
+        return read_signature_csv_from_stream_as_dict(f)
+
+def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
+    """
+    Compare the signature flags between the two dicts.
+
+    :param monolithicFlagsDict: the dict containing the subset of the monolithic
+    flags that should be equal to the modular flags.
+    :param modularFlagsDict:the dict containing the flags produced by a single
+    bootclasspath_fragment module.
+    :return: list of mismatches., each mismatch is a tuple where the first item
+    is the signature, and the second and third items are lists of the flags from
+    modular dict, and monolithic dict respectively.
+    """
+    mismatchingSignatures = []
+    # Create a sorted set of all the signatures from both the monolithic and
+    # modular dicts.
+    allSignatures = sorted(set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
+    for signature in allSignatures:
+        monolithicRow = monolithicFlagsDict.get(signature, {})
+        monolithicFlags = monolithicRow.get(None, [])
+        if signature in modularFlagsDict:
+            modularRow = modularFlagsDict.get(signature, {})
+            modularFlags = modularRow.get(None, [])
+        else:
+            modularFlags = ["blocked"]
+        if monolithicFlags != modularFlags:
+            mismatchingSignatures.append((signature, modularFlags, monolithicFlags))
+    return mismatchingSignatures
+
+def main(argv):
+    args_parser = argparse.ArgumentParser(description='Verify that sets of hidden API flags are each a subset of the monolithic flag file.')
+    args_parser.add_argument('monolithicFlags', help='The monolithic flag file')
+    args_parser.add_argument('modularFlags', nargs=argparse.REMAINDER, help='Flags produced by individual bootclasspath_fragment modules')
+    args = args_parser.parse_args(argv[1:])
+
+    # Read in all the flags into the trie
+    monolithicFlagsPath = args.monolithicFlags
+    monolithicTrie = read_flag_trie_from_file(monolithicFlagsPath)
+
+    # For each subset specified on the command line, create dicts for the flags
+    # provided by the subset and the corresponding flags from the complete set
+    # of flags and compare them.
+    failed = False
+    for modularPair in args.modularFlags:
+        parts = modularPair.split(":")
+        modularFlagsPath = parts[0]
+        modularPatternsPath = parts[1]
+        modularFlagsDict = read_signature_csv_from_file_as_dict(modularFlagsPath)
+        monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, modularPatternsPath)
+        mismatchingSignatures = compare_signature_flags(monolithicFlagsSubsetDict, modularFlagsDict)
+        if mismatchingSignatures:
+            failed = True
+            print("ERROR: Hidden API flags are inconsistent:")
+            print("< " + modularFlagsPath)
+            print("> " + monolithicFlagsPath)
+            for mismatch in mismatchingSignatures:
+                signature = mismatch[0]
+                print()
+                print("< " + ",".join([signature]+ mismatch[1]))
+                print("> " + ",".join([signature]+ mismatch[2]))
+
+    if failed:
+        sys.exit(1)
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
new file mode 100755
index 0000000..00c0611
--- /dev/null
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Unit tests for verify_overlaps_test.py."""
+import io
+import unittest
+
+from verify_overlaps import *
+
+class TestSignatureToElements(unittest.TestCase):
+
+    def signatureToElements(self, signature):
+        return InteriorNode().signatureToElements(signature)
+
+    def test_signatureToElements_1(self):
+        expected = [
+            'package:java',
+            'package:lang',
+            'class:ProcessBuilder',
+            'class:Redirect',
+            'class:1',
+            'member:<init>()V',
+        ]
+        self.assertEqual(expected, self.signatureToElements(
+            "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V"))
+
+    def test_signatureToElements_2(self):
+        expected = [
+            'package:java',
+            'package:lang',
+            'class:Object',
+            'member:hashCode()I',
+        ]
+        self.assertEqual(expected, self.signatureToElements(
+            "Ljava/lang/Object;->hashCode()I"))
+
+    def test_signatureToElements_3(self):
+        expected = [
+            'package:java',
+            'package:lang',
+            'class:CharSequence',
+            'class:',
+            'class:ExternalSyntheticLambda0',
+            'member:<init>(Ljava/lang/CharSequence;)V',
+        ]
+        self.assertEqual(expected, self.signatureToElements(
+            "Ljava/lang/CharSequence$$ExternalSyntheticLambda0;"
+            "-><init>(Ljava/lang/CharSequence;)V"))
+
+class TestDetectOverlaps(unittest.TestCase):
+
+    def read_flag_trie_from_string(self, csv):
+        with io.StringIO(csv) as f:
+            return read_flag_trie_from_stream(f)
+
+    def read_signature_csv_from_string_as_dict(self, csv):
+        with io.StringIO(csv) as f:
+            return read_signature_csv_from_stream_as_dict(f)
+
+    def extract_subset_from_monolithic_flags_as_dict_from_string(self, monolithic, patterns):
+        with io.StringIO(patterns) as f:
+            return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithic, f)
+
+    extractInput = '''
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+Ljava/util/zip/ZipFile;-><clinit>()V,blocked
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,blocked
+Ljava/lang/Character;->serialVersionUID:J,sdk
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
+'''
+
+    def test_extract_subset_signature(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'Ljava/lang/Object;->hashCode()I'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        expected = {
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_class(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/Object'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        expected = {
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_outer_class(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/Character'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+            },
+            'Ljava/lang/Character;->serialVersionUID:J': {
+                None: ['sdk'],
+                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_nested_class(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/Character$UnicodeScript'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_package(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/*'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+            },
+            'Ljava/lang/Character;->serialVersionUID:J': {
+                None: ['sdk'],
+                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            },
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            },
+            'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_recursive_package(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'java/**'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+            },
+            'Ljava/lang/Character;->serialVersionUID:J': {
+                None: ['sdk'],
+                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            },
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            },
+            'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+            },
+            'Ljava/util/zip/ZipFile;-><clinit>()V': {
+                None: ['blocked'],
+                'signature': 'Ljava/util/zip/ZipFile;-><clinit>()V',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_invalid_pattern_wildcard_and_member(self):
+        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+        patterns = 'Ljava/lang/*;->hashCode()I'
+
+        with self.assertRaises(Exception) as context:
+            self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        self.assertTrue("contains wildcard * and member signature hashCode()I" in str(context.exception))
+
+    def test_read_trie_duplicate(self):
+        with self.assertRaises(Exception) as context:
+            self.read_flag_trie_from_string('''
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->hashCode()I,blocked
+''')
+        self.assertTrue("Duplicate signature: Ljava/lang/Object;->hashCode()I" in str(context.exception))
+
+    def test_read_trie_missing_member(self):
+        with self.assertRaises(Exception) as context:
+            self.read_flag_trie_from_string('''
+Ljava/lang/Object,public-api,system-api,test-api
+''')
+        self.assertTrue("Invalid signature: Ljava/lang/Object, does not identify a specific member" in str(context.exception))
+
+    def test_match(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+''')
+        modular = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+''')
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = []
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_overlapping_flags(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api
+''')
+        modular = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+''')
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['public-api', 'system-api', 'test-api'],
+                ['public-api'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+
+    def test_mismatch_monolithic_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+''')
+        modular = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+''')
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['public-api', 'system-api', 'test-api'],
+                ['blocked'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_modular_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+''')
+        modular = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+''')
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['blocked'],
+                ['public-api', 'system-api', 'test-api'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_match_treat_missing_from_modular_as_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('')
+        modular = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+''')
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['public-api', 'system-api', 'test-api'],
+                [],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_treat_missing_from_modular_as_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+''')
+        modular = {}
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->hashCode()I',
+                ['blocked'],
+                ['public-api', 'system-api', 'test-api'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_blocked_missing_from_modular(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('''
+Ljava/lang/Object;->hashCode()I,blocked
+''')
+        modular = {}
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = []
+        self.assertEqual(expected, mismatches)
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index c7ad798..bed2ecf 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -134,11 +134,12 @@
     image_name: "art",
     contents: ["mybootlib"],
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -161,11 +162,12 @@
     image_name: "art",
     contents: ["mysdk_mybootlib@current"],
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -185,11 +187,12 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
 		`),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
@@ -332,11 +335,12 @@
         stub_libs: ["mycoreplatform"],
     },
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -416,11 +420,12 @@
         stub_libs: ["mysdk_mycoreplatform@current"],
     },
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -494,11 +499,12 @@
 }
 		`),
 		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
 .intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
@@ -538,8 +544,16 @@
 			rule = module.Output("updatable-bcp-packages.txt")
 			expectedContents := `'mybootlib\nmyothersdklibrary\n'`
 			android.AssertStringEquals(t, "updatable-bcp-packages.txt", expectedContents, rule.Args["content"])
+
+			rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
+			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " snapshot/hiddenapi/filtered-flags.csv:snapshot/hiddenapi/signature-patterns.csv ")
 		}),
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
+			module := result.ModuleForTests("platform-bootclasspath", "android_common")
+			rule := module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
+			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv ")
+		}),
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
 	)
 }
@@ -552,6 +566,7 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"),
+		java.FixtureConfigureApexBootJars("someapex:mysdklibrary", "myotherapex:myotherlib"),
 		prepareForSdkTestWithApex,
 
 		// Some additional files needed for the myotherapex.
@@ -636,11 +651,12 @@
         },
     ],
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -838,11 +854,12 @@
         max_target_o_low_priority: ["hiddenapi/my-max-target-o-low-priority.txt"],
         blocked: ["hiddenapi/my-blocked.txt"],
         unsupported_packages: ["hiddenapi/my-unsupported-packages.txt"],
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -881,11 +898,12 @@
 my-max-target-o-low-priority.txt -> hiddenapi/my-max-target-o-low-priority.txt
 my-blocked.txt -> hiddenapi/my-blocked.txt
 my-unsupported-packages.txt -> hiddenapi/my-unsupported-packages.txt
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
 .intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
 .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 1258684..9342d75 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -144,7 +144,7 @@
   run_soong
   local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  local glob_deps_file=out/soong/.primary/globs/0.d
+  local glob_deps_file=out/soong/.bootstrap/globs/0.d
 
   if [ -e "$glob_deps_file" ]; then
     fail "Glob deps file unexpectedly written on first build"
@@ -526,18 +526,14 @@
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
 }
 
-function test_bp2build_generates_fake_ninja_file {
+function test_bp2build_generates_marker_file {
   setup
   create_mock_bazel
 
   run_bp2build
 
-  if [[ ! -f "./out/soong/build.ninja" ]]; then
-    fail "./out/soong/build.ninja was not generated"
-  fi
-
-  if ! grep "build nothing: phony" "./out/soong/build.ninja"; then
-    fail "missing phony nothing target in out/soong/build.ninja"
+  if [[ ! -f "./out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+    fail "Marker file was not generated"
   fi
 }
 
@@ -577,10 +573,10 @@
   setup
 
   GENERATE_BAZEL_FILES=1 run_soong
-  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   GENERATE_BAZEL_FILES=1 run_soong
-  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   if [[ "$mtime1" != "$mtime2" ]]; then
     fail "Output Ninja file changed on null build"
@@ -712,6 +708,41 @@
   grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
 }
 
+function test_bp2build_back_and_forth_null_build {
+  setup
+
+  run_soong
+  local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output Ninja file changed when switching to bp2build"
+  fi
+
+  local marker_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_soong
+  local output_mtime3=$(stat -c "%y" out/soong/build.ninja)
+  local marker_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  if [[ "$output_mtime1" != "$output_mtime3" ]]; then
+    fail "Output Ninja file changed when switching to regular build from bp2build"
+  fi
+  if [[ "$marker_mtime1" != "$marker_mtime2" ]]; then
+    fail "bp2build marker file changed when switching to regular build from bp2build"
+  fi
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
+  local marker_mtime3=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  if [[ "$output_mtime1" != "$output_mtime4" ]]; then
+    fail "Output Ninja file changed when switching back to bp2build"
+  fi
+  if [[ "$marker_mtime1" != "$marker_mtime3" ]]; then
+    fail "bp2build marker file changed when switching back to bp2build"
+  fi
+}
+
 test_smoke
 test_null_build
 test_null_build_after_docs
@@ -727,8 +758,9 @@
 test_dump_json_module_graph
 test_write_to_source_tree
 test_bp2build_smoke
-test_bp2build_generates_fake_ninja_file
+test_bp2build_generates_marker_file
 test_bp2build_null_build
+test_bp2build_back_and_forth_null_build
 test_bp2build_add_android_bp
 test_bp2build_add_to_glob
 test_bp2build_bazel_workspace_structure
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index e357710..9bd85a4 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -8,6 +8,46 @@
 
 readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
 
+function test_bp2build_null_build() {
+  setup
+  run_bp2build
+  local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_bp2build
+  local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output bp2build marker file changed on null build"
+  fi
+}
+
+test_bp2build_null_build
+
+function test_bp2build_null_build_with_globs() {
+  setup
+
+  mkdir -p foo/bar
+  cat > foo/bar/Android.bp <<'EOF'
+filegroup {
+    name: "globs",
+    srcs: ["*.txt"],
+  }
+EOF
+  touch foo/bar/a.txt foo/bar/b.txt
+
+  run_bp2build
+  local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_bp2build
+  local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output bp2build marker file changed on null build"
+  fi
+}
+
+test_bp2build_null_build_with_globs
+
 function test_bp2build_generates_all_buildfiles {
   setup
   create_mock_bazel
diff --git a/tests/lib.sh b/tests/lib.sh
index 1d9b8d4..813a9dd 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -113,6 +113,7 @@
 
   symlink_directory prebuilts/bazel
   symlink_directory prebuilts/jdk
+  symlink_directory external/bazel-skylib
 
   symlink_file WORKSPACE
   symlink_file BUILD
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 8399573..6304a11 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -6,3 +6,4 @@
 "$TOP/build/soong/tests/bootstrap_test.sh"
 "$TOP/build/soong/tests/mixed_mode_test.sh"
 "$TOP/build/soong/tests/bp2build_bazel_test.sh"
+"$TOP/build/soong/tests/soong_test.sh"
diff --git a/tests/soong_test.sh b/tests/soong_test.sh
new file mode 100755
index 0000000..905d708
--- /dev/null
+++ b/tests/soong_test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+# Tests of Soong functionality
+
+source "$(dirname "$0")/lib.sh"
+
+function test_m_clean_works {
+  setup
+
+  # Create a directory with files that cannot be removed
+  mkdir -p out/bad_directory_permissions
+  touch out/bad_directory_permissions/unremovable_file
+  # File permissions are fine but directory permissions are bad
+  chmod a+rwx out/bad_directory_permissions/unremovable_file
+  chmod a-rwx out/bad_directory_permissions
+
+  run_soong clean
+}
+
+test_m_clean_works
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 37940ba..3dc87f5 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -61,7 +61,6 @@
         "proc_sync.go",
         "rbe.go",
         "sandbox_config.go",
-        "signal.go",
         "soong.go",
         "test_build.go",
         "upload.go",
diff --git a/ui/build/build.go b/ui/build/build.go
index 1ed9014..d869bf0 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -238,6 +238,11 @@
 		ctx.Verboseln("Skipping use of Kati ninja as requested")
 		what = what &^ RunKatiNinja
 	}
+	if config.SkipSoong() {
+		ctx.Verboseln("Skipping use of Soong as requested")
+		what = what &^ RunSoong
+	}
+
 	if config.SkipNinja() {
 		ctx.Verboseln("Skipping Ninja as requested")
 		what = what &^ RunNinja
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 19b5690..65b91bc 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"fmt"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -46,9 +47,49 @@
 	}
 }
 
+// Based on https://stackoverflow.com/questions/28969455/how-to-properly-instantiate-os-filemode
+// Because Go doesn't provide a nice way to set bits on a filemode
+const (
+	FILEMODE_READ         = 04
+	FILEMODE_WRITE        = 02
+	FILEMODE_EXECUTE      = 01
+	FILEMODE_USER_SHIFT   = 6
+	FILEMODE_USER_READ    = FILEMODE_READ << FILEMODE_USER_SHIFT
+	FILEMODE_USER_WRITE   = FILEMODE_WRITE << FILEMODE_USER_SHIFT
+	FILEMODE_USER_EXECUTE = FILEMODE_EXECUTE << FILEMODE_USER_SHIFT
+)
+
+// Ensures that files and directories in the out dir can be deleted.
+// For example, Bazen can generate output directories where the write bit isn't set, causing 'm' clean' to fail.
+func ensureOutDirRemovable(ctx Context, config Config) {
+	err := filepath.WalkDir(config.OutDir(), func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if d.IsDir() {
+			info, err := d.Info()
+			if err != nil {
+				return err
+			}
+			// Equivalent to running chmod u+rwx on each directory
+			newMode := info.Mode() | FILEMODE_USER_READ | FILEMODE_USER_WRITE | FILEMODE_USER_EXECUTE
+			if err := os.Chmod(path, newMode); err != nil {
+				return err
+			}
+		}
+		// Continue walking the out dir...
+		return nil
+	})
+	if err != nil && !os.IsNotExist(err) {
+		// Display the error, but don't crash.
+		ctx.Println(err.Error())
+	}
+}
+
 // Remove everything under the out directory. Don't remove the out directory
 // itself in case it's a symlink.
 func clean(ctx Context, config Config) {
+	ensureOutDirRemovable(ctx, config)
 	removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
 	ctx.Println("Entire build directory removed.")
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index 918a956..956406d 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -49,6 +49,7 @@
 	skipConfig     bool
 	skipKati       bool
 	skipKatiNinja  bool
+	skipSoong      bool
 	skipNinja      bool
 	skipSoongTests bool
 
@@ -582,6 +583,8 @@
 		arg := strings.TrimSpace(args[i])
 		if arg == "showcommands" {
 			c.verbose = true
+		} else if arg == "--empty-ninja-file" {
+			c.emptyNinjaFile = true
 		} else if arg == "--skip-ninja" {
 			c.skipNinja = true
 		} else if arg == "--skip-make" {
@@ -596,6 +599,10 @@
 		} else if arg == "--soong-only" {
 			c.skipKati = true
 			c.skipKatiNinja = true
+		} else if arg == "--config-only" {
+			c.skipKati = true
+			c.skipKatiNinja = true
+			c.skipSoong = true
 		} else if arg == "--skip-config" {
 			c.skipConfig = true
 		} else if arg == "--skip-soong-tests" {
@@ -690,54 +697,6 @@
 	}
 }
 
-// Lunch configures the environment for a specific product similarly to the
-// `lunch` bash function.
-func (c *configImpl) Lunch(ctx Context, product, variant string) {
-	if variant != "eng" && variant != "userdebug" && variant != "user" {
-		ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
-	}
-
-	c.environ.Set("TARGET_PRODUCT", product)
-	c.environ.Set("TARGET_BUILD_VARIANT", variant)
-	c.environ.Set("TARGET_BUILD_TYPE", "release")
-	c.environ.Unset("TARGET_BUILD_APPS")
-	c.environ.Unset("TARGET_BUILD_UNBUNDLED")
-}
-
-// Tapas configures the environment to build one or more unbundled apps,
-// similarly to the `tapas` bash function.
-func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) {
-	if len(apps) == 0 {
-		apps = []string{"all"}
-	}
-	if variant == "" {
-		variant = "eng"
-	}
-
-	if variant != "eng" && variant != "userdebug" && variant != "user" {
-		ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
-	}
-
-	var product string
-	switch arch {
-	case "arm", "":
-		product = "aosp_arm"
-	case "arm64":
-		product = "aosm_arm64"
-	case "x86":
-		product = "aosp_x86"
-	case "x86_64":
-		product = "aosp_x86_64"
-	default:
-		ctx.Fatalf("Invalid architecture: %q", arch)
-	}
-
-	c.environ.Set("TARGET_PRODUCT", product)
-	c.environ.Set("TARGET_BUILD_VARIANT", variant)
-	c.environ.Set("TARGET_BUILD_TYPE", "release")
-	c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " "))
-}
-
 func (c *configImpl) Environment() *Environment {
 	return c.environ
 }
@@ -780,6 +739,14 @@
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) MainNinjaFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "build.ninja")
+}
+
+func (c *configImpl) Bp2BuildMarkerFile() string {
+	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+}
+
 func (c *configImpl) TempDir() string {
 	return shared.TempDirForOutDir(c.SoongOutDir())
 }
@@ -817,6 +784,10 @@
 	return c.skipKatiNinja
 }
 
+func (c *configImpl) SkipSoong() bool {
+	return c.skipSoong
+}
+
 func (c *configImpl) SkipNinja() bool {
 	return c.skipNinja
 }
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 190c955..8ef8c74 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -71,16 +71,11 @@
 // A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
 // probably be nicer to use a flag in bootstrap.Args instead.
 type BlueprintConfig struct {
-	srcDir           string
 	buildDir         string
 	ninjaBuildDir    string
 	debugCompilation bool
 }
 
-func (c BlueprintConfig) SrcDir() string {
-	return "."
-}
-
 func (c BlueprintConfig) BuildDir() string {
 	return c.buildDir
 }
@@ -114,17 +109,20 @@
 	}
 }
 
-func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
+func bootstrapBlueprint(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
 	var args bootstrap.Args
 
-	mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
 	bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
-	// .bootstrap/build.ninja "includes" .bootstrap/build-globs.ninja for incremental builds
-	// generate an empty glob before running any rule in .bootstrap/build.ninja
+	bp2buildGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.bp2build.ninja")
+
+	// The glob .ninja files are subninja'd. However, they are generated during
+	// the build itself so we write an empty file so that the subninja doesn't
+	// fail on clean builds
 	writeEmptyGlobFile(ctx, bootstrapGlobFile)
+	writeEmptyGlobFile(ctx, bp2buildGlobFile)
 	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
 
 	args.RunGoTests = !config.skipSoongTests
@@ -137,7 +135,7 @@
 	// The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
 	// Building soong_build does not require a glob file
 	// Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
-	args.GlobFile = ""
+	args.Subninjas = []string{bootstrapGlobFile, bp2buildGlobFile}
 	args.GeneratingPrimaryBuilder = true
 	args.EmptyNinjaFile = config.EmptyNinjaFile()
 
@@ -146,48 +144,51 @@
 		args.DelvePath = shared.ResolveDelveBinary()
 	}
 
-	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, bootstrapGlobFile, mainNinjaFile)
-	bp2BuildMarkerFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, config.MainNinjaFile())
 	mainSoongBuildInputs := []string{"Android.bp"}
 
-	if integratedBp2Build {
-		mainSoongBuildInputs = append(mainSoongBuildInputs, bp2BuildMarkerFile)
+	if config.bazelBuildMode() == mixedBuild {
+		mainSoongBuildInputs = append(mainSoongBuildInputs, config.Bp2BuildMarkerFile())
 	}
 
-	soongBuildArgs := make([]string, 0)
+	soongBuildArgs := []string{
+		"--globListDir", "globs",
+		"--globFile", bootstrapGlobFile,
+	}
+
 	soongBuildArgs = append(soongBuildArgs, commonArgs...)
 	soongBuildArgs = append(soongBuildArgs, environmentArgs(config, "")...)
 	soongBuildArgs = append(soongBuildArgs, "Android.bp")
 
 	mainSoongBuildInvocation := bootstrap.PrimaryBuilderInvocation{
 		Inputs:  mainSoongBuildInputs,
-		Outputs: []string{mainNinjaFile},
+		Outputs: []string{config.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")
+	bp2buildArgs := []string{
+		"--bp2build_marker", config.Bp2BuildMarkerFile(),
+		"--globListDir", "globs.bp2build",
+		"--globFile", bp2buildGlobFile,
+	}
 
-		bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
-			Inputs:  []string{"Android.bp"},
-			Outputs: []string{bp2BuildMarkerFile},
-			Args:    bp2buildArgs,
-		}
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{bp2buildInvocation}
-		if config.bazelBuildMode() == mixedBuild {
-			args.PrimaryBuilderInvocations = append(args.PrimaryBuilderInvocations, mainSoongBuildInvocation)
-		}
-	} else {
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}
+	bp2buildArgs = append(bp2buildArgs, commonArgs...)
+	bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
+	bp2buildArgs = append(bp2buildArgs, "Android.bp")
+
+	bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
+		Inputs:  []string{"Android.bp"},
+		Outputs: []string{config.Bp2BuildMarkerFile()},
+		Args:    bp2buildArgs,
+	}
+	args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
+		bp2buildInvocation,
+		mainSoongBuildInvocation,
 	}
 
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
-		srcDir:           os.Getenv("TOP"),
 		buildDir:         config.SoongOutDir(),
 		ninjaBuildDir:    config.OutDir(),
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
@@ -220,18 +221,16 @@
 	// unused variables were changed?
 	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
 
-	for _, n := range []string{".bootstrap", ".minibootstrap"} {
-		dir := filepath.Join(config.SoongOutDir(), n)
-		if err := os.MkdirAll(dir, 0755); err != nil {
-			ctx.Fatalf("Cannot mkdir " + dir)
-		}
+	dir := filepath.Join(config.SoongOutDir(), ".bootstrap")
+	if err := os.MkdirAll(dir, 0755); err != nil {
+		ctx.Fatalf("Cannot mkdir " + dir)
 	}
 
 	buildMode := config.bazelBuildMode()
 	integratedBp2Build := (buildMode == mixedBuild) || (buildMode == generateBuildFiles)
 
 	// This is done unconditionally, but does not take a measurable amount of time
-	bootstrapBlueprint(ctx, config, integratedBp2Build)
+	bootstrapBlueprint(ctx, config)
 
 	soongBuildEnv := config.Environment().Copy()
 	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
@@ -265,10 +264,10 @@
 		}
 	}()
 
-	runMicrofactory(ctx, config, ".minibootstrap/bpglob", "github.com/google/blueprint/bootstrap/bpglob",
+	runMicrofactory(ctx, config, ".bootstrap/bpglob", "github.com/google/blueprint/bootstrap/bpglob",
 		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
-	ninja := func(name, file string) {
+	ninja := func(name, ninjaFile string, targets ...string) {
 		ctx.BeginTrace(metrics.RunSoong, name)
 		defer ctx.EndTrace()
 
@@ -276,8 +275,7 @@
 		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 		defer nr.Close()
 
-		cmd := Command(ctx, config, "soong "+name,
-			config.PrebuiltBuildTool("ninja"),
+		ninjaArgs := []string{
 			"-d", "keepdepfile",
 			"-d", "stats",
 			"-o", "usesphonyoutputs=yes",
@@ -287,7 +285,12 @@
 			"-w", "missingoutfile=err",
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
-			"-f", filepath.Join(config.SoongOutDir(), file))
+			"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
+		}
+
+		ninjaArgs = append(ninjaArgs, targets...)
+		cmd := Command(ctx, config, "soong "+name,
+			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
 
 		var ninjaEnv Environment
 
@@ -299,8 +302,17 @@
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}
-	// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
-	ninja("bootstrap", ".bootstrap/build.ninja")
+
+	var target string
+
+	if config.bazelBuildMode() == generateBuildFiles {
+		target = config.Bp2BuildMarkerFile()
+	} else {
+		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
+		target = config.MainNinjaFile()
+	}
+
+	ninja("bootstrap", ".bootstrap/build.ninja", target)
 
 	var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
 	if shouldCollectBuildSoongMetrics(config) {
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index a910c06..57ceaba 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -65,7 +65,6 @@
 
 	outDir := config.OutDir()
 	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
-	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
 	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
 
@@ -89,7 +88,6 @@
 			continue
 		}
 		if strings.HasPrefix(line, bootstrapDir) ||
-			strings.HasPrefix(line, miniBootstrapDir) ||
 			strings.HasPrefix(line, modulePathsDir) ||
 			line == variablesFilePath ||
 			line == dexpreoptConfigFilePath ||
diff --git a/ui/signal/Android.bp b/ui/signal/Android.bp
new file mode 100644
index 0000000..08933a1
--- /dev/null
+++ b/ui/signal/Android.bp
@@ -0,0 +1,28 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-ui-signal",
+    pkgPath: "android/soong/ui/signal",
+    srcs: [
+        "signal.go",
+    ],
+    deps: [
+        "soong-ui-logger",
+    ],
+}
diff --git a/ui/build/signal.go b/ui/signal/signal.go
similarity index 99%
rename from ui/build/signal.go
rename to ui/signal/signal.go
index 6643e2f..4929a7b 100644
--- a/ui/build/signal.go
+++ b/ui/signal/signal.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package build
+package signal
 
 import (
 	"os"