Merge "Revert "Target Java 17""
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 87554a2..1353293 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -400,6 +400,9 @@
 		// not recursive due to conflicting workspace paths in tools/atest/bazel/rules
 		"tools/asuite/atest":/* recursive = */ false,
 		"tools/asuite/atest/bazel/reporter":/* recursive = */ true,
+
+		// TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack
+		"external/rust/crates/rustc-demangle-capi":/* recursive = */ false,
 	}
 
 	Bp2buildModuleAlwaysConvertList = []string{
@@ -669,6 +672,13 @@
 		// kotlin srcs in java libs
 		"CtsPkgInstallerConstants",
 		"kotlinx_atomicfu",
+
+		// kotlin srcs in java binary
+		"AnalyzerKt",
+		"trebuchet-core",
+
+		// kotlin srcs in android_library
+		"renderscript_toolkit",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -1318,8 +1328,6 @@
 		"libc_musl_sysroot",
 	}
 
-	Bp2buildCcLibraryStaticOnlyList = []string{}
-
 	MixedBuildsDisabledList = []string{
 		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
 
@@ -1362,6 +1370,30 @@
 		"prebuilt_kotlin-test",
 		// TODO(b/217750501) exclude_files property not supported
 		"prebuilt_currysrc_org.eclipse",
+
+		// TODO(b/266459895): re-enable libunwindstack
+		"libunwindstack",
+		"libunwindstack_stdout_log",
+		"libunwindstack_no_dex",
+		"libunwindstack_utils",
+		"unwind_reg_info",
+		"libunwindstack_local",
+		"unwind_for_offline",
+		"unwind",
+		"unwind_info",
+		"unwind_symbols",
+		"libc_malloc_debug",
+		"libfdtrack",
+		"mediaswcodec",
+		"libcodec2_hidl@1.0",
+		"libEGL",
+		"libstagefright_bufferqueue_helper_novndk",
+		"libGLESv2",
+		"libcodec2_hidl@1.1",
+		"libmedia_codecserviceregistrant",
+		"libcodec2_hidl@1.2",
+		"libutils_test",
+		"libutilscallstack",
 	}
 
 	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
@@ -1379,5 +1411,9 @@
 	StagingMixedBuildsEnabledList = []string{
 		"com.android.adbd",
 		"adbd_test",
+		"adb_crypto_test",
+		"adb_pairing_auth_test",
+		"adb_pairing_connection_test",
+		"adb_tls_connection_test",
 	}
 )
diff --git a/android/api_domain.go b/android/api_domain.go
index bdd4e6f..587ceae 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -32,17 +32,18 @@
 
 // TODO(b/246656800): Reconcile with android.SdkKind
 const (
-	PublicApi ApiSurface = iota
-	SystemApi
-	VendorApi
+	// API surface provided by platform and mainline modules to other mainline modules
+	ModuleLibApi ApiSurface = iota
+	PublicApi               // Aka NDK
+	VendorApi               // Aka LLNDK
 )
 
 func (a ApiSurface) String() string {
 	switch a {
+	case ModuleLibApi:
+		return "module-libapi"
 	case PublicApi:
 		return "publicapi"
-	case SystemApi:
-		return "systemapi"
 	case VendorApi:
 		return "vendorapi"
 	default:
diff --git a/android/bazel.go b/android/bazel.go
index 3731dfe..10e9251 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -66,8 +66,8 @@
 	//
 	// This is a bool pointer to support tristates: true, false, not set.
 	//
-	// To opt-in a module, set bazel_module: { bp2build_available: true }
-	// To opt-out a module, set bazel_module: { bp2build_available: false }
+	// To opt in a module, set bazel_module: { bp2build_available: true }
+	// To opt out a module, set bazel_module: { bp2build_available: false }
 	// To defer the default setting for the directory, do not set the value.
 	Bp2build_available *bool
 
@@ -126,7 +126,7 @@
 	// one with the single member called Soong_config_variables, which itself is
 	// a struct containing fields for each supported feature in that namespace.
 	//
-	// The reason for using an slice of interface{} is to support defaults
+	// The reason for using a slice of interface{} is to support defaults
 	// propagation of the struct pointers.
 	namespacedVariableProps() namespacedVariableProperties
 	setNamespacedVariableProps(props namespacedVariableProperties)
@@ -237,16 +237,6 @@
 
 	// Per-module denylist to always opt modules out of bp2build conversion.
 	moduleDoNotConvert map[string]bool
-
-	// Per-module denylist of cc_library modules to only generate the static
-	// variant if their shared variant isn't ready or buildable by Bazel.
-	ccLibraryStaticOnly map[string]bool
-}
-
-// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
-// generate a static version of itself based on the current global configuration.
-func (a Bp2BuildConversionAllowlist) GenerateCcLibraryStaticOnly(moduleName string) bool {
-	return a.ccLibraryStaticOnly[moduleName]
 }
 
 // NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
@@ -258,7 +248,6 @@
 		map[string]bool{},
 		map[string]bool{},
 		map[string]bool{},
-		map[string]bool{},
 	}
 }
 
@@ -322,18 +311,6 @@
 	return a
 }
 
-// SetCcLibraryStaticOnlyList copies the entries from ccLibraryStaticOnly into the allowlist
-func (a Bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) Bp2BuildConversionAllowlist {
-	if a.ccLibraryStaticOnly == nil {
-		a.ccLibraryStaticOnly = map[string]bool{}
-	}
-	for _, m := range ccLibraryStaticOnly {
-		a.ccLibraryStaticOnly[m] = true
-	}
-
-	return a
-}
-
 // ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
 // added to the build symlink forest based on the current global configuration.
 func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
@@ -365,8 +342,7 @@
 			SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
 			SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
 			SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
-			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
-			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList)
+			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList)
 	}).(Bp2BuildConversionAllowlist)
 }
 
@@ -375,30 +351,16 @@
 // method will also log whether this module is mixed build enabled for
 // metrics reporting.
 func MixedBuildsEnabled(ctx BaseModuleContext) bool {
-	mixedBuildEnabled := mixedBuildPossible(ctx)
+	module := ctx.Module()
+	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
+		ctx.Os() != Windows && // Windows toolchains are not currently supported.
+		module.Enabled() &&
+		convertedToBazel(ctx, module) &&
+		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name())
 	ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
 	return mixedBuildEnabled
 }
 
-// mixedBuildPossible returns true if a module is ready to be replaced by a
-// converted or handcrafted Bazel target.
-func mixedBuildPossible(ctx BaseModuleContext) bool {
-	if !ctx.Config().IsMixedBuildsEnabled() {
-		return false
-	}
-	if ctx.Os() == Windows {
-		// Windows toolchains are not currently supported.
-		return false
-	}
-	if !ctx.Module().Enabled() {
-		return false
-	}
-	if !convertedToBazel(ctx, ctx.Module()) {
-		return false
-	}
-	return ctx.Config().BazelContext.BazelAllowlisted(ctx.Module().Name())
-}
-
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
 func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
 	b, ok := module.(Bazelable)
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 40cc6a2..ac4ced8 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -152,7 +152,7 @@
 	// Note that this only implies "bazel mixed build" allowlisting. The caller
 	// should independently verify the module is eligible for Bazel handling
 	// (for example, that it is MixedBuildBuildable).
-	BazelAllowlisted(moduleName string) bool
+	IsModuleNameAllowed(moduleName string) bool
 
 	// Returns the bazel output base (the root directory for all bazel intermediate outputs).
 	OutputBase() string
@@ -181,7 +181,7 @@
 
 // A context object which tracks queued requests that need to be made to Bazel,
 // and their results after the requests have been made.
-type bazelContext struct {
+type mixedBuildBazelContext struct {
 	bazelRunner
 	paths        *bazelPaths
 	requests     map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
@@ -210,7 +210,7 @@
 	targetBuildVariant string
 }
 
-var _ BazelContext = &bazelContext{}
+var _ BazelContext = &mixedBuildBazelContext{}
 
 // A bazel context to use when Bazel is disabled.
 type noopBazelContext struct{}
@@ -233,27 +233,42 @@
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
-	result, _ := m.LabelToOutputFiles[label]
+	result, ok := m.LabelToOutputFiles[label]
+	if !ok {
+		return []string{}, fmt.Errorf("no target with label %q in LabelToOutputFiles", label)
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
-	result, _ := m.LabelToCcInfo[label]
+	result, ok := m.LabelToCcInfo[label]
+	if !ok {
+		return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label)
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
-	result, _ := m.LabelToPythonBinary[label]
+	result, ok := m.LabelToPythonBinary[label]
+	if !ok {
+		return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label)
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) {
-	result, _ := m.LabelToApexInfo[label]
+	result, ok := m.LabelToApexInfo[label]
+	if !ok {
+		return cquery.ApexInfo{}, fmt.Errorf("no target with label %q in LabelToApexInfo", label)
+	}
 	return result, nil
 }
 
 func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) {
-	result, _ := m.LabelToCcBinary[label]
+	result, ok := m.LabelToCcBinary[label]
+	if !ok {
+		return cquery.CcUnstrippedInfo{}, fmt.Errorf("no target with label %q in LabelToCcBinary", label)
+	}
 	return result, nil
 }
 
@@ -261,7 +276,7 @@
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) BazelAllowlisted(_ string) bool {
+func (m MockBazelContext) IsModuleNameAllowed(_ string) bool {
 	return true
 }
 
@@ -277,14 +292,14 @@
 
 var _ BazelContext = MockBazelContext{}
 
-func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (bazelCtx *mixedBuildBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
 	key := makeCqueryKey(label, requestType, cfgKey)
 	bazelCtx.requestMutex.Lock()
 	defer bazelCtx.requestMutex.Unlock()
 	bazelCtx.requests[key] = true
 }
 
-func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+func (bazelCtx *mixedBuildBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
 	key := makeCqueryKey(label, cquery.GetOutputFiles, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		bazelOutput := strings.TrimSpace(rawString)
@@ -294,7 +309,7 @@
 	return nil, fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+func (bazelCtx *mixedBuildBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
 	key := makeCqueryKey(label, cquery.GetCcInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		bazelOutput := strings.TrimSpace(rawString)
@@ -303,7 +318,7 @@
 	return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+func (bazelCtx *mixedBuildBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
 	key := makeCqueryKey(label, cquery.GetPythonBinary, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		bazelOutput := strings.TrimSpace(rawString)
@@ -312,7 +327,7 @@
 	return "", fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
+func (bazelCtx *mixedBuildBazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
 	key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString))
@@ -320,7 +335,7 @@
 	return cquery.ApexInfo{}, fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (bazelCtx *bazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
+func (bazelCtx *mixedBuildBazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
 	key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
 		return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString))
@@ -361,7 +376,7 @@
 	return ""
 }
 
-func (n noopBazelContext) BazelAllowlisted(_ string) bool {
+func (n noopBazelContext) IsModuleNameAllowed(_ string) bool {
 	return false
 }
 
@@ -396,12 +411,6 @@
 			enabledModules[enabledAdHocModule] = true
 		}
 	case BazelDevMode:
-		// Don't use partially-converted cc_library targets in mixed builds,
-		// since mixed builds would generally rely on both static and shared
-		// variants of a cc_library.
-		for staticOnlyModule := range GetBp2BuildAllowList().ccLibraryStaticOnly {
-			disabledModules[staticOnlyModule] = true
-		}
 		addToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
 	default:
 		panic("Expected BazelProdMode, BazelStagingMode, or BazelDevMode")
@@ -475,7 +484,7 @@
 		targetProduct = c.DeviceProduct()
 	}
 
-	return &bazelContext{
+	return &mixedBuildBazelContext{
 		bazelRunner:           &builtinBazelRunner{},
 		paths:                 &paths,
 		requests:              make(map[cqueryKey]bool),
@@ -491,7 +500,7 @@
 	return p.metricsDir
 }
 
-func (context *bazelContext) BazelAllowlisted(moduleName string) bool {
+func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string) bool {
 	if context.bazelDisabledModules[moduleName] {
 		return false
 	}
@@ -618,7 +627,7 @@
 
 }
 
-func (context *bazelContext) mainBzlFileContents() []byte {
+func (context *mixedBuildBazelContext) mainBzlFileContents() []byte {
 	// TODO(cparsons): Define configuration transitions programmatically based
 	// on available archs.
 	contents := `
@@ -687,7 +696,7 @@
 	return []byte(productReplacer.Replace(contents))
 }
 
-func (context *bazelContext) mainBuildFileContents() []byte {
+func (context *mixedBuildBazelContext) mainBuildFileContents() []byte {
 	// TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
 	// architecture mapping.
 	formatString := `
@@ -751,10 +760,10 @@
 
 // Returns the file contents of the buildroot.cquery file that should be used for the cquery
 // expression in order to obtain information about buildroot and its dependencies.
-// The contents of this file depend on the bazelContext's requests; requests are enumerated
+// The contents of this file depend on the mixedBuildBazelContext's requests; requests are enumerated
 // and grouped by their request type. The data retrieved for each label depends on its
 // request type.
-func (context *bazelContext) cqueryStarlarkFileContents() []byte {
+func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte {
 	requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
 	for val := range context.requests {
 		cqueryId := getCqueryId(val)
@@ -912,7 +921,7 @@
 
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
-func (context *bazelContext) InvokeBazel(config Config, ctx *Context) error {
+func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx *Context) error {
 	if ctx != nil {
 		ctx.EventHandler.Begin("bazel")
 		defer ctx.EventHandler.End("bazel")
@@ -939,7 +948,7 @@
 	return nil
 }
 
-func (context *bazelContext) runCquery(ctx *Context) error {
+func (context *mixedBuildBazelContext) runCquery(ctx *Context) error {
 	if ctx != nil {
 		ctx.EventHandler.Begin("cquery")
 		defer ctx.EventHandler.End("cquery")
@@ -994,7 +1003,7 @@
 	return nil
 }
 
-func (context *bazelContext) runAquery(config Config, ctx *Context) error {
+func (context *mixedBuildBazelContext) runAquery(config Config, ctx *Context) error {
 	if ctx != nil {
 		ctx.EventHandler.Begin("aquery")
 		defer ctx.EventHandler.End("aquery")
@@ -1032,7 +1041,7 @@
 	return err
 }
 
-func (context *bazelContext) generateBazelSymlinks(ctx *Context) error {
+func (context *mixedBuildBazelContext) generateBazelSymlinks(ctx *Context) error {
 	if ctx != nil {
 		ctx.EventHandler.Begin("symlinks")
 		defer ctx.EventHandler.End("symlinks")
@@ -1044,15 +1053,15 @@
 	return err
 }
 
-func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+func (context *mixedBuildBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
 	return context.buildStatements
 }
 
-func (context *bazelContext) AqueryDepsets() []bazel.AqueryDepset {
+func (context *mixedBuildBazelContext) AqueryDepsets() []bazel.AqueryDepset {
 	return context.depsets
 }
 
-func (context *bazelContext) OutputBase() string {
+func (context *mixedBuildBazelContext) OutputBase() string {
 	return context.paths.outputBase
 }
 
@@ -1126,10 +1135,11 @@
 		// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
 		// because this would cause circular dependency. So, until we move aquery processing
 		// to the 'android' package, we need to handle special cases here.
-		if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
+		switch buildStatement.Mnemonic {
+		case "FileWrite", "SourceSymlinkManifest":
 			out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
 			WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents)
-		} else if buildStatement.Mnemonic == "SymlinkTree" {
+		case "SymlinkTree":
 			// build-runfiles arguments are the manifest file and the target directory
 			// where it creates the symlink tree according to this manifest (and then
 			// writes the MANIFEST file to it).
@@ -1148,7 +1158,7 @@
 					"outDir": outDir,
 				},
 			})
-		} else {
+		default:
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
 	}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 10bbf31..013e19c 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -189,7 +189,7 @@
 	return actual
 }
 
-func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
+func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
 	t.Helper()
 	p := bazelPaths{
 		soongOutDir:  t.TempDir(),
@@ -201,7 +201,7 @@
 		bazelCommandResults[aqueryCommand] = ""
 	}
 	runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
-	return &bazelContext{
+	return &mixedBuildBazelContext{
 		bazelRunner: runner,
 		paths:       &p,
 		requests:    map[cqueryKey]bool{},
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index e151521..bad7baf 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -461,11 +461,6 @@
 	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
 }
 
-// ModuleFromBazelLabel reverses the logic in bp2buildModuleLabel
-func ModuleFromBazelLabel(label string) string {
-	return strings.Split(label, ":")[1]
-}
-
 // BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
 type BazelOutPath struct {
 	OutputPath
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 7b38b6a..87b2c8f 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -417,11 +417,6 @@
 			t.Errorf("bp2build module do not convert of %s: expected: true, got: %v", k, allowlist.moduleDoNotConvert[k])
 		}
 	}
-	for _, k := range allowlists.Bp2buildCcLibraryStaticOnlyList {
-		if !allowlist.ccLibraryStaticOnly[k] {
-			t.Errorf("bp2build cc library static only of %s: expected: true, got: %v", k, allowlist.ccLibraryStaticOnly[k])
-		}
-	}
 }
 
 func TestShouldKeepExistingBuildFileForDir(t *testing.T) {
diff --git a/android/config.go b/android/config.go
index d5ed883..c305114 100644
--- a/android/config.go
+++ b/android/config.go
@@ -521,27 +521,33 @@
 		config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	}
 
-	if cmdArgs.SymlinkForestMarker != "" {
-		config.BuildMode = SymlinkForest
-	} else if cmdArgs.Bp2buildMarker != "" {
-		config.BuildMode = Bp2build
-	} else if cmdArgs.BazelQueryViewDir != "" {
-		config.BuildMode = GenerateQueryView
-	} else if cmdArgs.BazelApiBp2buildDir != "" {
-		config.BuildMode = ApiBp2build
-	} else if cmdArgs.ModuleGraphFile != "" {
-		config.BuildMode = GenerateModuleGraph
-	} else if cmdArgs.DocFile != "" {
-		config.BuildMode = GenerateDocFile
-	} else if cmdArgs.BazelModeDev {
-		config.BuildMode = BazelDevMode
-	} else if cmdArgs.BazelMode {
-		config.BuildMode = BazelProdMode
-	} else if cmdArgs.BazelModeStaging {
-		config.BuildMode = BazelStagingMode
-	} else {
-		config.BuildMode = AnalysisNoBazel
+	setBuildMode := func(arg string, mode SoongBuildMode) {
+		if arg != "" {
+			if config.BuildMode != AnalysisNoBazel {
+				fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
+				os.Exit(1)
+			}
+			config.BuildMode = mode
+		}
 	}
+	setBazelMode := func(arg bool, argName string, mode SoongBuildMode) {
+		if arg {
+			if config.BuildMode != AnalysisNoBazel {
+				fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", argName)
+				os.Exit(1)
+			}
+			config.BuildMode = mode
+		}
+	}
+	setBuildMode(cmdArgs.SymlinkForestMarker, SymlinkForest)
+	setBuildMode(cmdArgs.Bp2buildMarker, Bp2build)
+	setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView)
+	setBuildMode(cmdArgs.BazelApiBp2buildDir, ApiBp2build)
+	setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
+	setBuildMode(cmdArgs.DocFile, GenerateDocFile)
+	setBazelMode(cmdArgs.BazelModeDev, "--bazel-mode-dev", BazelDevMode)
+	setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
+	setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
 
 	config.BazelContext, err = NewBazelContext(config)
 	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
@@ -583,32 +589,28 @@
 	c.mockBpList = blueprint.MockModuleListFile
 }
 
+// TODO(b/265062549): Add a field to our collected (and uploaded) metrics which
+// describes a reason that we fell back to non-mixed builds.
 // Returns true if "Bazel builds" is enabled. In this mode, part of build
 // analysis is handled by Bazel.
 func (c *config) IsMixedBuildsEnabled() bool {
 	globalMixedBuildsSupport := c.Once(OnceKey{"globalMixedBuildsSupport"}, func() interface{} {
 		if c.productVariables.DeviceArch != nil && *c.productVariables.DeviceArch == "riscv64" {
-			fmt.Fprintln(os.Stderr, "unsupported device arch 'riscv64' for Bazel: falling back to non-mixed build")
 			return false
 		}
 		if c.IsEnvTrue("GLOBAL_THINLTO") {
-			fmt.Fprintln(os.Stderr, "unsupported env var GLOBAL_THINLTO for Bazel: falling back to non-mixed build")
 			return false
 		}
 		if len(c.productVariables.SanitizeHost) > 0 {
-			fmt.Fprintln(os.Stderr, "unsupported product var SanitizeHost for Bazel: falling back to non-mixed build")
 			return false
 		}
 		if len(c.productVariables.SanitizeDevice) > 0 {
-			fmt.Fprintln(os.Stderr, "unsupported product var SanitizeDevice for Bazel: falling back to non-mixed build")
 			return false
 		}
 		if len(c.productVariables.SanitizeDeviceDiag) > 0 {
-			fmt.Fprintln(os.Stderr, "unsupported product var SanitizeDeviceDiag for Bazel: falling back to non-mixed build")
 			return false
 		}
 		if len(c.productVariables.SanitizeDeviceArch) > 0 {
-			fmt.Fprintln(os.Stderr, "unsupported product var SanitizeDeviceArch for Bazel: falling back to non-mixed build")
 			return false
 		}
 		return true
@@ -1498,6 +1500,10 @@
 	return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
 }
 
+func (c *config) ApexTrimEnabled() bool {
+	return Bool(c.productVariables.TrimmedApex)
+}
+
 func (c *config) EnforceSystemCertificate() bool {
 	return Bool(c.productVariables.EnforceSystemCertificate)
 }
diff --git a/android/testing.go b/android/testing.go
index 29af71f..fc39a9c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1125,6 +1125,11 @@
 	config.katiEnabled = true
 }
 
+func SetTrimmedApexEnabledForTests(config Config) {
+	config.productVariables.TrimmedApex = new(bool)
+	*config.productVariables.TrimmedApex = true
+}
+
 func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
 	t.Helper()
 	var p AndroidMkEntriesProvider
@@ -1145,7 +1150,7 @@
 	var p AndroidMkDataProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkDataProvider); !ok {
-		t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+		t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name())
 	}
 	data := p.AndroidMk()
 	data.fillInData(ctx, mod)
diff --git a/android/util.go b/android/util.go
index a0f7160..947af69 100644
--- a/android/util.go
+++ b/android/util.go
@@ -136,6 +136,38 @@
 	return IndexList(s, list) != -1
 }
 
+func setFromList[T comparable](l []T) map[T]bool {
+	m := make(map[T]bool, len(l))
+	for _, t := range l {
+		m[t] = true
+	}
+	return m
+}
+
+// ListSetDifference checks if the two lists contain the same elements. It returns
+// a boolean which is true if there is a difference, and then returns lists of elements
+// that are in l1 but not l2, and l2 but not l1.
+func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
+	listsDiffer := false
+	diff1 := []T{}
+	diff2 := []T{}
+	m1 := setFromList(l1)
+	m2 := setFromList(l2)
+	for t := range m1 {
+		if _, ok := m2[t]; !ok {
+			diff1 = append(diff1, t)
+			listsDiffer = true
+		}
+	}
+	for t := range m2 {
+		if _, ok := m1[t]; !ok {
+			diff2 = append(diff2, t)
+			listsDiffer = true
+		}
+	}
+	return listsDiffer, diff1, diff2
+}
+
 // Returns true if the given string s is prefixed with any string in the given prefix list.
 func HasAnyPrefix(s string, prefixList []string) bool {
 	for _, prefix := range prefixList {
diff --git a/android/variable.go b/android/variable.go
index 9725895..e714fc4 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -380,6 +380,7 @@
 
 	Ndk_abis *bool `json:",omitempty"`
 
+	TrimmedApex                  *bool `json:",omitempty"`
 	Flatten_apex                 *bool `json:",omitempty"`
 	ForceApexSymlinkOptimization *bool `json:",omitempty"`
 	CompressedApex               *bool `json:",omitempty"`
@@ -502,6 +503,7 @@
 		Malloc_zero_contents:         boolPtr(true),
 		Malloc_pattern_fill_contents: boolPtr(false),
 		Safestack:                    boolPtr(false),
+		TrimmedApex:                  boolPtr(false),
 
 		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
 		ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
@@ -702,124 +704,9 @@
 		}
 	}
 
-	productConfigProperties.zeroValuesForNamespacedVariables()
-
 	return productConfigProperties
 }
 
-// zeroValuesForNamespacedVariables ensures that selects that contain __only__
-// conditions default values have zero values set for the other non-default
-// values for that select statement.
-//
-// If the ProductConfigProperties map contains these items, as parsed from the .bp file:
-//
-//	library_linking_strategy: {
-//	    prefer_static: {
-//	        static_libs: [
-//	            "lib_a",
-//	            "lib_b",
-//	        ],
-//	    },
-//	    conditions_default: {
-//	        shared_libs: [
-//	            "lib_a",
-//	            "lib_b",
-//	        ],
-//	    },
-//	},
-//
-// Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
-// Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
-//
-// We need to add this:
-//
-// Shared_libs {Library_linking_strategy ANDROID prefer_static} []
-//
-// so that the following gets generated for the "dynamic_deps" attribute,
-// instead of putting lib_a and lib_b directly into dynamic_deps without a
-// select:
-//
-//	dynamic_deps = select({
-//	    "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
-//	    "//conditions:default": [
-//	        "//foo/bar:lib_a",
-//	        "//foo/bar:lib_b",
-//	    ],
-//	}),
-func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
-	// A map of product config properties to the zero values of their respective
-	// property value.
-	zeroValues := make(map[ProductConfigProperty]interface{})
-
-	// A map of prop names (e.g. cflags) to product config properties where the
-	// (prop name, ProductConfigProperty) tuple contains a non-conditions_default key.
-	//
-	// e.g.
-	//
-	// prefer_static: {
-	//     static_libs: [
-	//         "lib_a",
-	//         "lib_b",
-	//     ],
-	// },
-	// conditions_default: {
-	//     shared_libs: [
-	//         "lib_a",
-	//         "lib_b",
-	//     ],
-	// },
-	//
-	// The tuple of ("static_libs", prefer_static) would be in this map.
-	hasNonDefaultValue := make(map[string]map[ProductConfigProperty]bool)
-
-	// Iterate over all added soong config variables.
-	for propName, v := range *props {
-		for p, intf := range v {
-			if p.Namespace == "" {
-				// If there's no namespace, this isn't a soong config variable,
-				// i.e. this is a product variable. product variables have no
-				// conditions_defaults, so skip them.
-				continue
-			}
-			if p.FullConfig == bazel.ConditionsDefaultConfigKey {
-				// Skip conditions_defaults.
-				continue
-			}
-			if hasNonDefaultValue[propName] == nil {
-				hasNonDefaultValue[propName] = make(map[ProductConfigProperty]bool)
-				hasNonDefaultValue[propName][p] = false
-			}
-			// Create the zero value of the variable.
-			if _, exists := zeroValues[p]; !exists {
-				zeroValue := reflect.Zero(reflect.ValueOf(intf).Type()).Interface()
-				if zeroValue == nil {
-					panic(fmt.Errorf("Expected non-nil zero value for product/config variable %+v\n", intf))
-				}
-				zeroValues[p] = zeroValue
-			}
-			hasNonDefaultValue[propName][p] = true
-		}
-	}
-
-	for propName := range *props {
-		for p, zeroValue := range zeroValues {
-			// Ignore variables that already have a non-default value for that axis
-			if exists, _ := hasNonDefaultValue[propName][p]; !exists {
-				// fmt.Println(propName, p.Namespace, p.Name, p.FullConfig, zeroValue)
-				// Insert the zero value for this propname + product config value.
-				props.AddProductConfigProperty(
-					propName,
-					p.Namespace,
-					p.Name,
-					p.FullConfig,
-					zeroValue,
-					bazel.NoConfigAxis,
-				)
-			}
-		}
-	}
-}
-
 func (p *ProductConfigProperties) AddProductConfigProperty(
 	propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
 	if (*p)[propertyName] == nil {
@@ -945,14 +832,16 @@
 
 		for j := 0; j < variableValue.NumField(); j++ {
 			property := variableValue.Field(j)
+			// e.g. Asflags, Cflags, Enabled, etc.
+			propertyName := variableValue.Type().Field(j).Name
+			// config can also be "conditions_default".
+			config := proptools.PropertyNameForField(propertyName)
+
 			// If the property wasn't set, no need to pass it along
 			if property.IsZero() {
 				continue
 			}
 
-			// e.g. Asflags, Cflags, Enabled, etc.
-			propertyName := variableValue.Type().Field(j).Name
-
 			if v, ok := maybeExtractConfigVarProp(property); ok {
 				// The field is a struct, which is used by:
 				// 1) soong_config_string_variables
@@ -972,13 +861,14 @@
 				//     static_libs: ...
 				// }
 				field := v
+				// Iterate over fields of this struct prop.
 				for k := 0; k < field.NumField(); k++ {
-					// Iterate over fields of this struct prop.
-					if field.Field(k).IsZero() {
+					// For product variables, zero values are irrelevant; however, for soong config variables,
+					// empty values are relevant because there can also be a conditions default which is not
+					// applied for empty variables.
+					if field.Field(k).IsZero() && namespace == "" {
 						continue
 					}
-					// config can also be "conditions_default".
-					config := proptools.PropertyNameForField(propertyName)
 					actualPropertyName := field.Type().Field(k).Name
 
 					productConfigProperties.AddProductConfigProperty(
diff --git a/android/visibility.go b/android/visibility.go
index b209599..5955133 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -155,7 +155,11 @@
 }
 
 func isAncestor(p1 string, p2 string) bool {
-	return strings.HasPrefix(p2+"/", p1+"/")
+	// Equivalent to strings.HasPrefix(p2+"/", p1+"/"), but without the string copies
+	// The check for a trailing slash is so that we don't consider sibling
+	// directories with common prefixes to be ancestors, e.g. "fooo/bar" should not be
+	// a descendant of "foo".
+	return strings.HasPrefix(p2, p1) && (len(p2) == len(p1) || p2[len(p1)] == '/')
 }
 
 func (r subpackagesRule) String() string {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index b76f6bd..7babd45 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -309,7 +309,7 @@
 		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
 		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
 	}
-	android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.requiredDeps, required)
+	android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
 	android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
 	android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
 }
diff --git a/apex/apex.go b/apex/apex.go
index e99823b..a9c8afc 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -80,6 +80,7 @@
 	ctx.BottomUp("apex", apexMutator).Parallel()
 	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
 	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
 	// Register after apex_info mutator so that it can use ApexVariationName
 	ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
 }
@@ -123,10 +124,6 @@
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
-	// The minimum SDK version that this APEX must support at minimum. This is usually set to
-	// the SDK version that the APEX was first introduced.
-	Min_sdk_version *string
-
 	// Whether this APEX is considered updatable or not. When set to true, this will enforce
 	// additional rules for making sure that the APEX is truly updatable. To be updatable,
 	// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -389,6 +386,13 @@
 	// conditions, e.g., target device needs to support APEX compression, are also fulfilled.
 	// Default: false.
 	Compressible *bool
+
+	// Trim against a specific Dynamic Common Lib APEX
+	Trim_against *string
+
+	// The minimum SDK version that this APEX must support at minimum. This is usually set to
+	// the SDK version that the APEX was first introduced.
+	Min_sdk_version *string
 }
 
 type apexBundle struct {
@@ -439,8 +443,8 @@
 	// GenerateAndroidBuildActions.
 	filesInfo []apexFile
 
-	// List of other module names that should be installed when this APEX gets installed.
-	requiredDeps []string
+	// List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
+	makeModulesToInstall []string
 
 	///////////////////////////////////////////////////////////////////////////////////////////
 	// Outputs (final and intermediates)
@@ -488,8 +492,6 @@
 	// Optional list of lint report zip files for apexes that contain java or app modules
 	lintReports android.Paths
 
-	prebuiltFileToDelete string
-
 	isCompressed bool
 
 	// Path of API coverage generate file
@@ -675,6 +677,7 @@
 	androidAppTag   = &dependencyTag{name: "androidApp", payload: true}
 	bpfTag          = &dependencyTag{name: "bpf", payload: true}
 	certificateTag  = &dependencyTag{name: "certificate"}
+	dclaTag         = &dependencyTag{name: "dcla"}
 	executableTag   = &dependencyTag{name: "executable", payload: true}
 	fsTag           = &dependencyTag{name: "filesystem", payload: true}
 	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
@@ -908,6 +911,33 @@
 	}
 }
 
+func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
+	if !mctx.Config().ApexTrimEnabled() {
+		return
+	}
+	if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
+		commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+		mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
+	} else if o, ok := mctx.Module().(*OverrideApex); ok {
+		for _, p := range o.GetProperties() {
+			properties, ok := p.(*overridableProperties)
+			if !ok {
+				continue
+			}
+			if properties.Trim_against != nil {
+				commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+				mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
+			}
+		}
+	}
+}
+
+type DCLAInfo struct {
+	ProvidedLibs []string
+}
+
+var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
+
 type ApexBundleInfo struct {
 	Contents *android.ApexContents
 }
@@ -1035,6 +1065,12 @@
 		child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
 		return true
 	})
+
+	if a.dynamic_common_lib_apex() {
+		mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+			ProvidedLibs: a.properties.Native_shared_libs,
+		})
+	}
 }
 
 type ApexInfoMutator interface {
@@ -1531,6 +1567,19 @@
 	return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
 }
 
+// See the list of libs to trim
+func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
+	dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
+	if len(dclaModules) > 1 {
+		panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
+	}
+	if len(dclaModules) > 0 {
+		DCLAInfo := ctx.OtherModuleProvider(dclaModules[0], DCLAInfoProvider).(DCLAInfo)
+		return DCLAInfo.ProvidedLibs
+	}
+	return []string{}
+}
+
 // These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
 // members) can be sanitized, either forcibly, or by the global configuration. For some of the
 // sanitizers, extra dependencies can be forcibly added as well.
@@ -1657,7 +1706,7 @@
 	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
 }
 
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile {
 	dirInApex := "bin"
 	fileToCopy := py.HostToolPath().Path()
 	return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
@@ -1922,11 +1971,9 @@
 	a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
 	a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
 
-	// Ensure ApexInfo.RequiresLibs are installed as part of a bundle build
-	for _, bazelLabel := range outputs.RequiresLibs {
-		// convert Bazel label back to Soong module name
-		a.requiredDeps = append(a.requiredDeps, android.ModuleFromBazelLabel(bazelLabel))
-	}
+	// Ensure ApexMkInfo.install_to_system make module names are installed as
+	// part of a bundled build.
+	a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...)
 
 	apexType := a.properties.ApexType
 	switch apexType {
@@ -2025,7 +2072,7 @@
 			a.primaryApexType = true
 
 			if ctx.Config().InstallExtraFlattenedApexes() {
-				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+				a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix)
 			}
 		}
 	case zipApex:
@@ -2147,7 +2194,7 @@
 			case *cc.Module:
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
 				return true // track transitive dependencies
-			case *python.Module:
+			case *python.PythonBinaryModule:
 				if ch.HostToolPath().Valid() {
 					vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
 				}
@@ -2177,7 +2224,7 @@
 
 			vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
 			for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
-				a.requiredDeps = append(a.requiredDeps, makeModuleName)
+				a.makeModulesToInstall = append(a.makeModulesToInstall, makeModuleName)
 			}
 			return true
 		case sscpfTag:
@@ -2295,12 +2342,6 @@
 			} else {
 				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
 			}
-		case android.PrebuiltDepTag:
-			// If the prebuilt is force disabled, remember to delete the prebuilt file
-			// that might have been installed in the previous builds
-			if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
-				a.prebuiltFileToDelete = prebuilt.InstallFilename()
-			}
 		}
 		return false
 	}
@@ -2340,11 +2381,14 @@
 				//
 				// Always include if we are a host-apex however since those won't have any
 				// system libraries.
-				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
+				//
+				// Skip the dependency in unbundled builds where the device image is not
+				// being built.
+				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
 					// we need a module name for Make
 					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
-					if !android.InList(name, a.requiredDeps) {
-						a.requiredDeps = append(a.requiredDeps, name)
+					if !android.InList(name, a.makeModulesToInstall) {
+						a.makeModulesToInstall = append(a.makeModulesToInstall, name)
 					}
 				}
 				vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
@@ -2479,7 +2523,6 @@
 	}
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 2) traverse the dependency tree to collect apexFile structs from them.
-
 	// Collect the module directory for IDE info in java/jdeps.go.
 	a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
 
@@ -2837,7 +2880,7 @@
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
-	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
 	if minApiLevel.IsNone() {
 		return ""
 	}
@@ -3426,7 +3469,7 @@
 	Key                   bazel.LabelAttribute
 	Certificate           bazel.LabelAttribute  // used when the certificate prop is a module
 	Certificate_name      bazel.StringAttribute // used when the certificate prop is a string
-	Min_sdk_version       *string
+	Min_sdk_version       bazel.StringAttribute
 	Updatable             bazel.BoolAttribute
 	Installable           bazel.BoolAttribute
 	Binaries              bazel.LabelListAttribute
@@ -3445,6 +3488,10 @@
 	Native_shared_libs_64 bazel.LabelListAttribute
 }
 
+const (
+	minSdkVersionPropName = "Min_sdk_version"
+)
+
 // ConvertWithBp2build performs bp2build conversion of an apex
 func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	// We only convert apex and apex_test modules at this time
@@ -3483,11 +3530,19 @@
 		fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.File_contexts))
 	}
 
+	productVariableProps := android.ProductVariableProperties(ctx)
 	// TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
 	// given it's coming via config, we probably don't want to put it in here.
-	var minSdkVersion *string
-	if a.properties.Min_sdk_version != nil {
-		minSdkVersion = a.properties.Min_sdk_version
+	var minSdkVersion bazel.StringAttribute
+	if a.overridableProperties.Min_sdk_version != nil {
+		minSdkVersion.SetValue(*a.overridableProperties.Min_sdk_version)
+	}
+	if props, ok := productVariableProps[minSdkVersionPropName]; ok {
+		for c, p := range props {
+			if val, ok := p.(*string); ok {
+				minSdkVersion.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
+			}
+		}
 	}
 
 	var keyLabelAttribute bazel.LabelAttribute
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 33fce7c..31e848e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3329,17 +3329,14 @@
 	// non-APEX variant does not have __ANDROID_APEX__ defined
 	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
 
-	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=10000")
 
-	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=29")
 
 	// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
 	// each variant defines additional macros to distinguish which apex variant it is built for
@@ -3348,19 +3345,17 @@
 	mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
-	// recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+	// recovery variant does not set __ANDROID_APEX__
 	mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
 
 	// non-APEX variant does not have __ANDROID_APEX__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
-	// recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+	// recovery variant does not set __ANDROID_APEX__
 	mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
 }
 
 func TestHeaderLibsDependency(t *testing.T) {
@@ -5721,7 +5716,7 @@
 		}),
 	)
 	ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	ensureListContains(t, ab.requiredDeps, "myapex.flattened")
+	ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened")
 	mk := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
@@ -9302,7 +9297,7 @@
 	`)
 
 	bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+	bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo")
 	data := android.AndroidMkDataForTest(t, ctx, bundle)
 	var builder strings.Builder
 	data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
@@ -9310,7 +9305,7 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
 
 	flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-	flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+	flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo")
 	flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
 	var flattenedBuilder strings.Builder
 	flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
@@ -9554,7 +9549,7 @@
 func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
 	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
 	for _, dep := range deps {
-		android.AssertStringListContains(t, "", a.requiredDeps, dep)
+		android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
 	}
 }
 
@@ -9562,7 +9557,7 @@
 func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
 	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
 	for _, dep := range deps {
-		android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+		android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
 	}
 }
 
@@ -9867,3 +9862,70 @@
 	libcCoreVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared").Module()
 	android.AssertBoolEquals(t, "core variant should link against source libc", true, hasDep(libfooCoreVariant, libcCoreVariant))
 }
+
+func TestTrimmedApex(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo","libbaz"],
+			min_sdk_version: "29",
+			trim_against: "mydcla",
+    }
+		apex {
+			name: "mydcla",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo","libbar"],
+			min_sdk_version: "29",
+			file_contexts: ":myapex-file_contexts",
+			dynamic_common_lib_apex: true,
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libc"],
+			apex_available: ["myapex","mydcla"],
+			min_sdk_version: "29",
+		}
+		cc_library {
+			name: "libbar",
+			shared_libs: ["libc"],
+			apex_available: ["myapex","mydcla"],
+			min_sdk_version: "29",
+		}
+		cc_library {
+			name: "libbaz",
+			shared_libs: ["libc"],
+			apex_available: ["myapex","mydcla"],
+			min_sdk_version: "29",
+		}
+		cc_api_library {
+			name: "libc",
+			src: "libc.so",
+			min_sdk_version: "29",
+			recovery_available: true,
+		}
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libc",
+			],
+			header_libs: [],
+		}
+		`
+	ctx := testApex(t, bp)
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.MaybeRule("apexRule")
+	if apexRule.Rule == nil {
+		t.Errorf("Expecting regular apex rule but a non regular apex rule found")
+	}
+
+	ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
+	trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("TrimmedApexRule")
+	libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
+	android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
+	android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
+	android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
+}
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 01afa52..2f2b61e 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -42,6 +42,7 @@
 				OutputBaseDir: outputBaseDir,
 				LabelToApexInfo: map[string]cquery.ApexInfo{
 					"//:foo": cquery.ApexInfo{
+						// ApexInfo Starlark provider.
 						SignedOutput:           "signed_out.apex",
 						SignedCompressedOutput: "signed_out.capex",
 						UnsignedOutput:         "unsigned_out.apex",
@@ -56,6 +57,9 @@
 						// unused
 						PackageName:  "pkg_name",
 						ProvidesLibs: []string{"a", "b"},
+
+						// ApexMkInfo Starlark provider
+						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
 					},
 				},
 			}
@@ -111,7 +115,12 @@
 	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
 	}
-	if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+	// make modules to be installed to system
+	if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+		t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+	}
+	if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
 	}
 }
@@ -212,6 +221,7 @@
 				OutputBaseDir: outputBaseDir,
 				LabelToApexInfo: map[string]cquery.ApexInfo{
 					"//:foo": cquery.ApexInfo{
+						// ApexInfo Starlark provider
 						SignedOutput:          "signed_out.apex",
 						UnsignedOutput:        "unsigned_out.apex",
 						BundleKeyInfo:         []string{"public_key", "private_key"},
@@ -225,8 +235,12 @@
 						// unused
 						PackageName:  "pkg_name",
 						ProvidesLibs: []string{"a", "b"},
+
+						// ApexMkInfo Starlark provider
+						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
 					},
 					"//:override_foo": cquery.ApexInfo{
+						// ApexInfo Starlark provider
 						SignedOutput:          "override_signed_out.apex",
 						UnsignedOutput:        "override_unsigned_out.apex",
 						BundleKeyInfo:         []string{"override_public_key", "override_private_key"},
@@ -240,6 +254,9 @@
 						// unused
 						PackageName:  "override_pkg_name",
 						ProvidesLibs: []string{"a", "b"},
+
+						// ApexMkInfo Starlark provider
+						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
 					},
 				},
 			}
@@ -295,7 +312,12 @@
 	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
 	}
-	if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+	// make modules to be installed to system
+	if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+		t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+	}
+	if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
 		t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
 	}
 }
diff --git a/apex/builder.go b/apex/builder.go
index 3b9cac0..4331d3e 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -40,6 +40,8 @@
 	pctx.Import("android/soong/java")
 	pctx.HostBinToolVariable("apexer", "apexer")
 	pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
+	pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
+
 	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
 	// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
 	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
@@ -146,6 +148,34 @@
 	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
 		"opt_flags", "manifest", "is_DCLA")
 
+	TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
+		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+			`(. ${out}.copy_commands) && ` +
+			`APEXER_TOOL_PATH=${tool_path} ` +
+			`${apexer_with_trim_preprocessing} ` +
+			`--apexer ${apexer} ` +
+			`--canned_fs_config ${canned_fs_config} ` +
+			`--manifest ${manifest} ` +
+			`--libs_to_trim ${libs_to_trim} ` +
+			`${image_dir} ` +
+			`${out} ` +
+			`-- ` +
+			`--include_build_info ` +
+			`--force ` +
+			`--payload_type image ` +
+			`--key ${key} ` +
+			`--file_contexts ${file_contexts} ` +
+			`${opt_flags} `,
+		CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
+			"${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
+			"${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
+			"prebuilts/sdk/current/public/android.jar"},
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "APEX ${image_dir} => ${out}",
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+		"opt_flags", "manifest", "libs_to_trim")
+
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
 			`(. ${out}.copy_commands) && ` +
@@ -706,6 +736,24 @@
 					"opt_flags":        strings.Join(optFlags, " "),
 				},
 			})
+		} else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        TrimmedApexRule,
+				Implicits:   implicitInputs,
+				Output:      unsignedOutputFile,
+				Description: "apex (" + apexType.name() + ")",
+				Args: map[string]string{
+					"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+					"image_dir":        imageDir.String(),
+					"copy_commands":    strings.Join(copyCommands, " && "),
+					"manifest":         a.manifestPbOut.String(),
+					"file_contexts":    fileContexts.String(),
+					"canned_fs_config": cannedFsConfig.String(),
+					"key":              a.privateKeyFile.String(),
+					"opt_flags":        strings.Join(optFlags, " "),
+					"libs_to_trim":     strings.Join(a.libs_to_trim(ctx), ","),
+				},
+			})
 		} else {
 			ctx.Build(pctx, android.BuildParams{
 				Rule:        apexRule,
@@ -1133,7 +1181,7 @@
 	if a.properties.Canned_fs_config != nil {
 		cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config))
 	}
-	cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
+	cmd.Text(") | LC_ALL=C sort ").FlagWithOutput("> ", cannedFsConfig)
 	builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
 
 	return cannedFsConfig.OutputPath
diff --git a/apex/vndk.go b/apex/vndk.go
index ef3e5e1..80560cf 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -65,8 +65,23 @@
 		}
 
 		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
+
 		// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
 		ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
+
+		apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+		if err != nil {
+			mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+			return
+		}
+
+		targets := mctx.MultiTargets()
+		if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
+			// Disable VNDK apexes for VNDK versions less than the minimum supported API level for the primary
+			// architecture.
+			ab.Disable()
+		}
+
 	}
 }
 
diff --git a/bazel/aquery.go b/bazel/aquery.go
index bc823b3..80cf70a 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -118,12 +118,11 @@
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
 // less readable Bazel structures (depset and path fragment).
 type aqueryArtifactHandler struct {
-	// Switches to true if any depset contains only `bazelToolsDependencySentinel`
-	bazelToolsDependencySentinelNeeded bool
 	// Maps depset id to AqueryDepset, a representation of depset which is
 	// post-processed for middleman artifact handling, unhandled artifact
 	// dropping, content hashing, etc.
 	depsetIdToAqueryDepset map[depsetId]AqueryDepset
+	emptyDepsetIds         map[depsetId]struct{}
 	// Maps content hash to AqueryDepset.
 	depsetHashToAqueryDepset map[string]AqueryDepset
 
@@ -145,9 +144,6 @@
 // The file name of py3wrapper.sh, which is used by py_binary targets.
 const py3wrapperFileName = "/py3wrapper.sh"
 
-// A file to be put into depsets that are otherwise empty
-const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
-
 func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
 	m := map[K]V{}
 	for _, v := range values {
@@ -192,6 +188,7 @@
 		depsetIdToAqueryDepset:         map[depsetId]AqueryDepset{},
 		depsetHashToAqueryDepset:       map[string]AqueryDepset{},
 		depsetHashToArtifactPathsCache: map[string][]string{},
+		emptyDepsetIds:                 make(map[depsetId]struct{}, 0),
 		artifactIdToPath:               artifactIdToPath,
 	}
 
@@ -208,16 +205,16 @@
 
 // Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
 // depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (*AqueryDepset, error) {
 	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
-		return aqueryDepset, nil
+		return &aqueryDepset, nil
 	}
 	transitiveDepsetIds := depset.TransitiveDepSetIds
 	var directArtifactPaths []string
 	for _, artifactId := range depset.DirectArtifactIds {
 		path, pathExists := a.artifactIdToPath[artifactId]
 		if !pathExists {
-			return AqueryDepset{}, fmt.Errorf("undefined input artifactId %d", artifactId)
+			return nil, fmt.Errorf("undefined input artifactId %d", artifactId)
 		}
 		// Filter out any inputs which are universally dropped, and swap middleman
 		// artifacts with their corresponding depsets.
@@ -226,6 +223,7 @@
 			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
 		} else if strings.HasSuffix(path, py3wrapperFileName) ||
 			strings.HasPrefix(path, "../bazel_tools") {
+			continue
 			// Drop these artifacts.
 			// See go/python-binary-host-mixed-build for more details.
 			// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
@@ -241,20 +239,23 @@
 	for _, childDepsetId := range transitiveDepsetIds {
 		childDepset, exists := depsetIdToDepset[childDepsetId]
 		if !exists {
-			return AqueryDepset{}, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+			if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
+				continue
+			} else {
+				return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+			}
 		}
-		childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset)
-		if err != nil {
-			return AqueryDepset{}, err
+		if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
+			return nil, err
+		} else if childAqueryDepset == nil {
+			continue
+		} else {
+			childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
 		}
-		childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
 	}
 	if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
-		// We could omit this depset altogether but that requires cleanup on
-		// transitive dependents.
-		// As a simpler alternative, we use this sentinel file as a dependency.
-		directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
-		a.bazelToolsDependencySentinelNeeded = true
+		a.emptyDepsetIds[depset.Id] = struct{}{}
+		return nil, nil
 	}
 	aqueryDepset := AqueryDepset{
 		ContentHash:            depsetContentHash(directArtifactPaths, childDepsetHashes),
@@ -263,7 +264,7 @@
 	}
 	a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
 	a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
-	return aqueryDepset, nil
+	return &aqueryDepset, nil
 }
 
 // getInputPaths flattens the depsets of the given IDs and returns all transitive
@@ -392,14 +393,6 @@
 	}
 
 	var buildStatements []BuildStatement
-	if aqueryHandler.bazelToolsDependencySentinelNeeded {
-		buildStatements = append(buildStatements, BuildStatement{
-			Command:     fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
-			OutputPaths: []string{bazelToolsDependencySentinel},
-			Mnemonic:    bazelToolsDependencySentinel,
-		})
-	}
-
 	for _, actionEntry := range aqueryResult.Actions {
 		if shouldSkipAction(actionEntry) {
 			continue
@@ -484,7 +477,9 @@
 	var hashes []string
 	for _, depsetId := range inputDepsetIds {
 		if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
-			return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+			if _, empty := a.emptyDepsetIds[depsetId]; !empty {
+				return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", depsetId)
+			}
 		} else {
 			hashes = append(hashes, aqueryDepset.ContentHash)
 		}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 2eacafa..4d1503e 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -227,7 +227,7 @@
 		return
 	}
 	_, _, err = AqueryBuildStatements(data)
-	assertError(t, err, "undefined input depsetId 2")
+	assertError(t, err, "undefined (not even empty) input depsetId 2")
 }
 
 func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
@@ -584,13 +584,18 @@
    { "id": 60, "label": ".."}
  ]
 }`
+	/* depsets
+	       1111  2222
+	       /  \   |
+	../dep2    ../bazel_tools/dep1
+	*/
 	data, err := JsonToActionGraphContainer(inputString)
 	if err != nil {
 		t.Error(err)
 		return
 	}
 	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
-	if len(actualDepsets) != 2 {
+	if len(actualDepsets) != 1 {
 		t.Errorf("expected 1 depset but found %#v", actualDepsets)
 		return
 	}
@@ -624,6 +629,82 @@
 	}
 }
 
+func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
+	const inputString = `{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 10 },
+   { "id": 2, "path_fragment_id": 20 },
+   { "id": 3, "path_fragment_id": 30 }],
+ "dep_set_of_files": [{
+   "id": 1111,
+   "transitive_dep_set_ids": [2222]
+ }, {
+   "id": 2222,
+   "direct_artifact_ids": [3]
+ }, {
+   "id": 3333,
+   "direct_artifact_ids": [3]
+ }, {
+   "id": 4444,
+   "transitive_dep_set_ids": [3333]
+ }],
+ "actions": [{
+   "target_id": 100,
+   "action_key": "x",
+   "input_dep_set_ids": [1111, 4444],
+   "mnemonic": "x",
+   "arguments": ["bogus", "command"],
+   "output_ids": [2],
+   "primary_output_id": 1
+ }],
+ "path_fragments": [
+   { "id": 10, "label": "input" },
+   { "id": 20, "label": "output" },
+   { "id": 30, "label": "dep", "parent_id": 50 },
+   { "id": 50, "label": "bazel_tools", "parent_id": 60 },
+   { "id": 60, "label": ".."}
+ ]
+}`
+	/* depsets
+	    1111    4444
+	     ||      ||
+	    2222    3333
+	      |      |
+	../bazel_tools/dep
+	Note: in dep_set_of_files:
+	  1111 appears BEFORE its dependency,2222 while
+	  4444 appears AFTER its dependency 3333
+	and this test shows that that order doesn't affect empty depset pruning
+	*/
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
+	if len(actualDepsets) != 0 {
+		t.Errorf("expected 0 depsets but found %#v", actualDepsets)
+		return
+	}
+
+	expectedBuildStatement := BuildStatement{
+		Command:     "bogus command",
+		OutputPaths: []string{"output"},
+		Mnemonic:    "x",
+	}
+	buildStatementFound := false
+	for _, actualBuildStatement := range actualBuildStatements {
+		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+			buildStatementFound = true
+			break
+		}
+	}
+	if !buildStatementFound {
+		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+		return
+	}
+}
+
 func TestMiddlemenAction(t *testing.T) {
 	const inputString = `
 {
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 2b8753b..4680256 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -343,8 +343,8 @@
 }
 
 func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
-	if ca.configurationType < other.configurationType {
-		return true
+	if ca.configurationType == other.configurationType {
+		return ca.subType < other.subType
 	}
-	return ca.subType < other.subType
+	return ca.configurationType < other.configurationType
 }
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 81c60d9..6654191 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,7 +14,14 @@
 	GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
 )
 
+type CcAndroidMkInfo struct {
+	LocalStaticLibs      []string
+	LocalWholeStaticLibs []string
+	LocalSharedLibs      []string
+}
+
 type CcInfo struct {
+	CcAndroidMkInfo
 	OutputFiles          []string
 	CcObjectFiles        []string
 	CcSharedLibraryFiles []string
@@ -180,20 +187,33 @@
 if abi_diff_info:
   abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
 
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+    androidmk_info = p[androidmk_tag]
+    local_static_libs = androidmk_info.local_static_libs
+    local_whole_static_libs = androidmk_info.local_whole_static_libs
+    local_shared_libs = androidmk_info.local_shared_libs
+
 return json_encode({
-	"OutputFiles": outputFiles,
-	"CcObjectFiles": ccObjectFiles,
-	"CcSharedLibraryFiles": sharedLibraries,
-	"CcStaticLibraryFiles": staticLibraries,
-	"Includes": includes,
-	"SystemIncludes": system_includes,
-	"Headers": headers,
-	"RootStaticArchives": rootStaticArchives,
-	"RootDynamicLibraries": rootSharedLibraries,
-	"TidyFiles": tidy_files,
-	"TocFile": toc_file,
-	"UnstrippedOutput": unstripped,
-	"AbiDiffFiles": abi_diff_files,
+    "OutputFiles": outputFiles,
+    "CcObjectFiles": ccObjectFiles,
+    "CcSharedLibraryFiles": sharedLibraries,
+    "CcStaticLibraryFiles": staticLibraries,
+    "Includes": includes,
+    "SystemIncludes": system_includes,
+    "Headers": headers,
+    "RootStaticArchives": rootStaticArchives,
+    "RootDynamicLibraries": rootSharedLibraries,
+    "TidyFiles": tidy_files,
+    "TocFile": toc_file,
+    "UnstrippedOutput": unstripped,
+    "AbiDiffFiles": abi_diff_files,
+    "LocalStaticLibs": [l for l in local_static_libs],
+    "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+    "LocalSharedLibs": [l for l in local_shared_libs],
 })`
 
 }
@@ -237,6 +257,10 @@
 if info.signed_compressed_output:
     signed_compressed_output = info.signed_compressed_output.path
 
+mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
+if not mk_info:
+  fail("%s did not provide ApexMkInfo" % id_string)
+
 return json_encode({
     "signed_output": info.signed_output.path,
     "signed_compressed_output": signed_compressed_output,
@@ -251,10 +275,12 @@
     "backing_libs": info.backing_libs.path,
     "bundle_file": info.base_with_config_zip.path,
     "installed_files": info.installed_files.path,
+    "make_modules_to_install": mk_info.make_modules_to_install,
 })`
 }
 
 type ApexInfo struct {
+	// From the ApexInfo provider
 	SignedOutput           string   `json:"signed_output"`
 	SignedCompressedOutput string   `json:"signed_compressed_output"`
 	UnsignedOutput         string   `json:"unsigned_output"`
@@ -268,6 +294,9 @@
 	BackingLibs            string   `json:"backing_libs"`
 	BundleFile             string   `json:"bundle_file"`
 	InstalledFiles         string   `json:"installed_files"`
+
+	// From the ApexMkInfo provider
+	MakeModulesToInstall []string `json:"make_modules_to_install"`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -289,15 +318,32 @@
 }
 
 func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string {
-	return `unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+	return `
 p = providers(target)
 output_path = target.files.to_list()[0].path
+
 unstripped = output_path
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
 if unstripped_tag in p:
-    unstripped = p[unstripped_tag].unstripped.files.to_list()[0].path
+    unstripped_info = p[unstripped_tag]
+    unstripped = unstripped_info.unstripped.files.to_list()[0].path
+
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+    androidmk_info = p[androidmk_tag]
+    local_static_libs = androidmk_info.local_static_libs
+    local_whole_static_libs = androidmk_info.local_whole_static_libs
+    local_shared_libs = androidmk_info.local_shared_libs
+
 return json_encode({
     "OutputFile":  output_path,
     "UnstrippedOutput": unstripped,
+    "LocalStaticLibs": [l for l in local_static_libs],
+    "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+    "LocalSharedLibs": [l for l in local_shared_libs],
 })
 `
 }
@@ -312,6 +358,7 @@
 }
 
 type CcUnstrippedInfo struct {
+	CcAndroidMkInfo
 	OutputFile       string
 	UnstrippedOutput string
 }
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 1d30535..7003ce1 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -177,9 +177,11 @@
 	"backing_libs":"path/to/backing.txt",
 	"bundle_file": "dir/bundlefile.zip",
 	"installed_files":"path/to/installed-files.txt",
-	"provides_native_libs":[]
+	"provides_native_libs":[],
+	"make_modules_to_install": ["foo","bar"]
 }`,
 			expectedOutput: ApexInfo{
+				// ApexInfo
 				SignedOutput:      "my.apex",
 				UnsignedOutput:    "my.apex.unsigned",
 				RequiresLibs:      []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
@@ -191,6 +193,9 @@
 				BackingLibs:       "path/to/backing.txt",
 				BundleFile:        "dir/bundlefile.zip",
 				InstalledFiles:    "path/to/installed-files.txt",
+
+				// ApexMkInfo
+				MakeModulesToInstall: []string{"foo", "bar"},
 			},
 		},
 	}
diff --git a/bazel/properties.go b/bazel/properties.go
index 76450dc..f4acd26 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -73,6 +73,16 @@
 	}
 }
 
+func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
 // MakeLabelListFromTargetNames creates a LabelList from unqualified target names
 // This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
 func MakeLabelListFromTargetNames(targetNames []string) LabelList {
@@ -152,7 +162,7 @@
 		ll.Includes = append(ll.Includes, other.Includes...)
 	}
 	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
-		ll.Excludes = append(other.Excludes, other.Excludes...)
+		ll.Excludes = append(ll.Excludes, other.Excludes...)
 	}
 }
 
@@ -412,13 +422,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
-	for k := range la.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(la.ConfigurableValues)
 }
 
 // MakeLabelAttribute turns a string into a LabelAttribute
@@ -608,13 +612,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
-	for k := range ba.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(ba.ConfigurableValues)
 }
 
 // labelListSelectValues supports config-specific label_list typed Bazel attribute values.
@@ -761,13 +759,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
-	for k := range lla.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(lla.ConfigurableValues)
 }
 
 // Append all values, including os and arch specific ones, from another
@@ -814,6 +806,16 @@
 	return false
 }
 
+// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
+func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
+	for _, values := range lla.ConfigurableValues[axis] {
+		if !values.IsNil() {
+			return true
+		}
+	}
+	return false
+}
+
 // IsEmpty returns true if the attribute has no values under any configuration.
 func (lla LabelListAttribute) IsEmpty() bool {
 	if len(lla.Value.Includes) > 0 {
@@ -878,7 +880,7 @@
 			// then remove all config-specific excludes
 			allLabels := baseLabels.deepCopy()
 			allLabels.Append(val)
-			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
 		}
 
 		// After going through all configs, delete the duplicates in the config
@@ -1080,14 +1082,10 @@
 	if cs[axis] == nil {
 		cs[axis] = make(stringSelectValues)
 	}
-	var v = ""
-	if str != nil {
-		v = *str
-	}
-	cs[axis][config] = v
+	cs[axis][config] = str
 }
 
-type stringSelectValues map[string]string
+type stringSelectValues map[string]*string
 
 // HasConfigurableValues returns true if the attribute contains axis-specific string values.
 func (sa StringAttribute) HasConfigurableValues() bool {
@@ -1099,6 +1097,11 @@
 	return false
 }
 
+// SetValue sets the base, non-configured value for the Label
+func (sa *StringAttribute) SetValue(value string) {
+	sa.SetSelectValue(NoConfigAxis, "", &value)
+}
+
 // SetSelectValue set a value for a bazel select for the given axis, config and value.
 func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
 	axis.validateConfig(config)
@@ -1123,7 +1126,7 @@
 		return sa.Value
 	case arch, os, osArch, productVariables:
 		if v, ok := sa.ConfigurableValues[axis][config]; ok {
-			return &v
+			return v
 		} else {
 			return nil
 		}
@@ -1134,13 +1137,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
-	for k := range sa.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(sa.ConfigurableValues)
 }
 
 // Collapse reduces the configurable axes of the string attribute to a single axis.
@@ -1154,7 +1151,7 @@
 	_, containsProductVariables := axisTypes[productVariables]
 	if containsProductVariables {
 		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
+			return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
 		}
 	}
 	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
@@ -1181,7 +1178,7 @@
 				}
 			}
 		}
-		// All os_arch values are now set. Clear os and arch axes.
+		/// All os_arch values are now set. Clear os and arch axes.
 		delete(sa.ConfigurableValues, ArchConfigurationAxis)
 		delete(sa.ConfigurableValues, OsConfigurationAxis)
 		// Verify post-condition; this should never fail, provided no additional
@@ -1189,6 +1186,21 @@
 		if len(sa.ConfigurableValues) > 1 {
 			panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
 		}
+	} else if containsProductVariables {
+		usedBaseValue := false
+		for a, configToProp := range sa.ConfigurableValues {
+			if a.configurationType == productVariables {
+				for c, p := range configToProp {
+					if p == nil {
+						sa.SetSelectValue(a, c, sa.Value)
+						usedBaseValue = true
+					}
+				}
+			}
+		}
+		if usedBaseValue {
+			sa.Value = nil
+		}
 	}
 	return nil
 }
@@ -1327,13 +1339,7 @@
 
 // SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
 func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
-	for k := range sla.ConfigurableValues {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
+	return SortedConfigurationAxes(sla.ConfigurableValues)
 }
 
 // DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
@@ -1367,6 +1373,9 @@
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
 // Starlark string.format compatible tag for productVariable.
 func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
+	if len(slice) == 0 {
+		return slice, false
+	}
 	ret := make([]string, 0, len(slice))
 	changesMade := false
 	for _, s := range slice {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 8729381..cf03eb5 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -231,6 +231,7 @@
 				"all_include",
 				"arm_exclude",
 				"android_exclude",
+				"product_config_exclude",
 			},
 			[]string{"all_exclude"},
 		),
@@ -251,10 +252,10 @@
 				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
 				"b":                        makeLabelList([]string{"b_val"}, []string{}),
 				"c":                        makeLabelList([]string{"c_val"}, []string{}),
-				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
+				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
 			},
 			ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
-				"a": makeLabelList([]string{}, []string{"not_in_value"}),
+				"a": makeLabelList([]string{}, []string{"product_config_exclude"}),
 			},
 		},
 	}
@@ -287,6 +288,10 @@
 			"c":                        makeLabels("c_val"),
 			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
 		},
+		ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+			"a":                        nilLabels,
+			ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
+		},
 	}
 	for _, axis := range attr.SortedConfigurationAxes() {
 		if _, ok := expectedConfiguredIncludes[axis]; !ok {
diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go
index df7cced..0cda5dd 100644
--- a/bp2build/aar_conversion_test.go
+++ b/bp2build/aar_conversion_test.go
@@ -133,7 +133,41 @@
 						"exports": `[":static_import_dep"]`,
 					},
 				),
+				MakeNeverlinkDuplicateTarget("android_library", "TestImport"),
 			},
 		},
 	)
 }
+
+func TestConvertAndroidLibraryKotlin(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library with .kt srcs and common_srcs attribute",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Filesystem: map[string]string{
+			"AndroidManifest.xml": "",
+		},
+		Blueprint: `
+android_library {
+        name: "TestLib",
+        srcs: ["a.java", "b.kt"],
+        common_srcs: ["c.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+					"common_srcs":    `["c.kt"]`,
+					"manifest":       `"AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+			MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+		}})
+}
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 2b35521..067e34f 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -28,6 +28,7 @@
 
 func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("java_library", java.LibraryFactory)
 }
 
 func TestMinimalAndroidApp(t *testing.T) {
@@ -200,3 +201,29 @@
 			}),
 		}})
 }
+
+func TestAndroidAppLibs(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app with libs",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "foo",
+				libs: ["barLib"]
+}
+java_library{
+       name: "barLib",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+			MakeBazelTarget("android_binary", "foo", AttrNameToString{
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `[]`,
+				"deps":           `[":barLib-neverlink"]`,
+			}),
+		}})
+}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 5dc9612..86b9b27 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -45,17 +45,27 @@
 	bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
+	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionDirFiles(ctx, res.metrics))
+
+	return &res.metrics
+}
+
+// Wrapper function that will be responsible for all files in soong_injection directory
+// This includes
+// 1. config value(s) that are hardcoded in Soong
+// 2. product_config variables
+func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) []BazelFile {
+	var ret []BazelFile
+
 	productConfigFiles, err := CreateProductConfigFiles(ctx)
 	if err != nil {
 		fmt.Printf("ERROR: %s", err.Error())
 		os.Exit(1)
 	}
-
-	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
-	writeFiles(ctx, soongInjectionDir, productConfigFiles)
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
-
-	return &res.metrics
+	ret = append(ret, productConfigFiles...)
+	ret = append(ret, soongInjectionFiles(ctx.Config(), metrics)...)
+	return ret
 }
 
 // Get the output directory and create it if it doesn't exist.
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index e343a05..3eec439 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -97,6 +97,8 @@
 build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
 build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
 build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
+build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
+build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index f924d00..c11a50d 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1806,6 +1806,11 @@
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Blueprint: soongCcLibraryPreamble + `
 cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
     name: "target_linux_bionic_empty",
     target: {
         linux_bionic: {
@@ -1816,7 +1821,10 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", AttrNameToString{
-			"system_dynamic_deps": `[]`,
+			"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
 		}),
 	},
 	)
@@ -1829,6 +1837,11 @@
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Blueprint: soongCcLibraryPreamble + `
 cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
     name: "target_bionic_empty",
     target: {
         bionic: {
@@ -1839,12 +1852,68 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", AttrNameToString{
-			"system_dynamic_deps": `[]`,
+			"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
 		}),
 	},
 	)
 }
 
+func TestCcLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library system_shared_lib empty for musl variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "target_musl_empty",
+    target: {
+        musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("target_musl_empty", AttrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library system_shared_lib empty for linux_musl variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "target_linux_musl_empty",
+    target: {
+        linux_musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("target_linux_musl_empty", AttrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	})
+}
 func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library system_shared_libs set for shared and root",
@@ -2756,7 +2825,7 @@
 	expectedBazelTargets := []string{
 		MakeBazelTarget(
 			"cc_api_library_headers",
-			"libfoo.systemapi.headers",
+			"libfoo.module-libapi.headers",
 			AttrNameToString{
 				"export_includes": `["dir1"]`,
 			}),
@@ -2773,18 +2842,18 @@
 				"api":          `"libfoo.map.txt"`,
 				"library_name": `"libfoo"`,
 				"api_surfaces": `[
-        "systemapi",
+        "module-libapi",
         "vendorapi",
     ]`,
 				"hdrs": `[
-        ":libfoo.systemapi.headers",
+        ":libfoo.module-libapi.headers",
         ":libfoo.vendorapi.headers",
     ]`,
 			}),
 	}
 	RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
 		Blueprint:            bp,
-		Description:          "cc API contributions to systemapi and vendorapi",
+		Description:          "cc API contributions to module-libapi and vendorapi",
 		ExpectedBazelTargets: expectedBazelTargets,
 	})
 }
@@ -2803,8 +2872,8 @@
 				stubs: {symbol_file: "a.map.txt"},
 			}`,
 			expectedApi:         `"a.map.txt"`,
-			expectedApiSurfaces: `["systemapi"]`,
-			description:         "Library that contributes to systemapi",
+			expectedApiSurfaces: `["module-libapi"]`,
+			description:         "Library that contributes to module-libapi",
 		},
 		{
 			bp: `
@@ -2825,10 +2894,10 @@
 			}`,
 			expectedApi: `"a.map.txt"`,
 			expectedApiSurfaces: `[
-        "systemapi",
+        "module-libapi",
         "vendorapi",
     ]`,
-			description: "Library that contributes to systemapi and vendorapi",
+			description: "Library that contributes to module-libapi and vendorapi",
 		},
 	}
 	for _, testCase := range testCases {
@@ -3368,23 +3437,23 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
     })`,
 				"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
     })`,
 				"local_includes": `["."]`,
 			}),
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
     })`,
 				"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
-        "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
     })`,
 				"local_includes": `["."]`,
 			}),
@@ -3414,16 +3483,16 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
     })`,
 				"dynamic_deps": `select({
-        "//build/bazel/rules/apex:non_apex": [":baz__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":baz__BP2BUILD__MISSING__DEP"],
     })`,
 				"deps": `select({
-        "//build/bazel/rules/apex:non_apex": [":abc__BP2BUILD__MISSING__DEP"],
-        "//conditions:default": [],
+        "//build/bazel/rules/apex:in_apex": [],
+        "//conditions:default": [":abc__BP2BUILD__MISSING__DEP"],
     })`,
 				"local_includes": `["."]`,
 			}),
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 767f4ad..d5256f6 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1310,6 +1310,11 @@
 	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
 		Description: "cc_library_static system_shared_lib empty for bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_bionic_empty",
     target: {
@@ -1322,7 +1327,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{
-				"system_dynamic_deps": `[]`,
+				"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
 	})
@@ -1336,6 +1344,11 @@
 	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
 		Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_linux_bionic_empty",
     target: {
@@ -1348,6 +1361,63 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{
+				"system_dynamic_deps": `select({
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static system_shared_lib empty for musl variant",
+		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "target_musl_empty",
+    target: {
+        musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "target_musl_empty", AttrNameToString{
+				"system_dynamic_deps": `[]`,
+			}),
+		},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static system_shared_lib empty for linux_musl variant",
+		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+		name: "libc_musl",
+		bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "target_linux_musl_empty",
+    target: {
+        linux_musl: {
+            system_shared_libs: [],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "target_linux_musl_empty", AttrNameToString{
 				"system_dynamic_deps": `[]`,
 			}),
 		},
@@ -1359,6 +1429,11 @@
 		Description: "cc_library_static system_shared_libs set for bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble +
 			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_bionic",
     target: {
@@ -1374,6 +1449,7 @@
 				"system_dynamic_deps": `select({
         "//build/bazel/platforms/os:android": [":libc"],
         "//build/bazel/platforms/os:linux_bionic": [":libc"],
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
         "//conditions:default": [],
     })`,
 			}),
@@ -1387,6 +1463,11 @@
 		Blueprint: soongCcLibraryStaticPreamble +
 			simpleModuleDoNotConvertBp2build("cc_library", "libc") +
 			simpleModuleDoNotConvertBp2build("cc_library", "libm") + `
+cc_library {
+	name: "libc_musl",
+	bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "target_linux_bionic",
     system_shared_libs: ["libc"],
@@ -1402,6 +1483,7 @@
 			MakeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{
 				"system_dynamic_deps": `[":libc"] + select({
         "//build/bazel/platforms/os:linux_bionic": [":libm"],
+        "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
         "//conditions:default": [],
     })`,
 			}),
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1377c6b..eab84e1 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -58,6 +58,7 @@
     exclude_srcs: ["a/b/exclude.c"],
     sdk_version: "current",
     min_sdk_version: "29",
+	crt: true,
 }
 `,
 		ExpectedBazelTargets: []string{
@@ -76,6 +77,7 @@
 				"system_dynamic_deps": `[]`,
 				"sdk_version":         `"current"`,
 				"min_sdk_version":     `"29"`,
+				"crt":                 "True",
 			}),
 		},
 	})
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 987c903..2a0a78e 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -28,10 +28,13 @@
 			ret[selectKey] = reflect.ValueOf(strs)
 		}
 	}
+
 	// if there is a select, use the base value as the conditions default value
 	if len(ret) > 0 {
-		ret[bazel.ConditionsDefaultSelectKey] = value
-		value = reflect.Zero(value.Type())
+		if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok {
+			ret[bazel.ConditionsDefaultSelectKey] = value
+			value = reflect.Zero(value.Type())
+		}
 	}
 
 	return value, []selects{ret}
@@ -157,6 +160,12 @@
 // select statements.
 func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
 	var value reflect.Value
+	// configurableAttrs is the list of individual select statements to be
+	// concatenated together. These select statements should be along different
+	// axes. For example, one element may be
+	// `select({"//color:red": "one", "//color:green": "two"})`, and the second
+	// element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
+	// These selects should be sorted by axis identifier.
 	var configurableAttrs []selects
 	var prepend bool
 	var defaultSelectValue *string
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index e15dd59..c43fbd8 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -6,6 +6,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/cc"
 	cc_config "android/soong/cc/config"
 	java_config "android/soong/java/config"
 
@@ -20,14 +21,16 @@
 	Contents string
 }
 
-func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
+// PRIVATE: Use CreateSoongInjectionDirFiles instead
+func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
 	var files []BazelFile
 
 	files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
 
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
-	files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+	files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+	files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
 
 	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
 	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index dfc7f0b..b9c06bc 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -84,7 +84,7 @@
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
 	testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
-	files := CreateSoongInjectionFiles(testConfig, CreateCodegenMetrics())
+	files := soongInjectionFiles(testConfig, CreateCodegenMetrics())
 
 	expectedFilePaths := []bazelFilepath{
 		{
@@ -101,7 +101,11 @@
 		},
 		{
 			dir:      "cc_toolchain",
-			basename: "constants.bzl",
+			basename: "config_constants.bzl",
+		},
+		{
+			dir:      "cc_toolchain",
+			basename: "sanitizer_constants.bzl",
 		},
 		{
 			dir:      "java_toolchain",
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index e8551e5..46105c7 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -134,3 +134,153 @@
 		},
 	})
 }
+
+func TestJavaBinaryHostKotlinSrcs(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with srcs, libs.",
+		Filesystem:  testFs,
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java", "b.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_kt"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinCommonSrcs(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with common_srcs",
+		Filesystem:  testFs,
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java"],
+    common_srcs: ["b.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+				"srcs":        `["a.java"]`,
+				"common_srcs": `["b.kt"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_kt"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinWithResourceDir(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with srcs, libs, resource dir  .",
+		Filesystem: map[string]string{
+			"test.mf":        "Main-Class: com.android.test.MainClass",
+			"res/a.res":      "",
+			"res/dir1/b.res": "",
+		},
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java", "b.kt"],
+    java_resource_dirs: ["res"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_kt"]`,
+				"resources": `[
+        "res/a.res",
+        "res/dir1/b.res",
+    ]`,
+				"resource_strip_prefix": `"res"`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaBinaryHostKotlinWithResources(t *testing.T) {
+	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+		Description: "java_binary_host with srcs, libs, resources.",
+		Filesystem: map[string]string{
+			"test.mf":   "Main-Class: com.android.test.MainClass",
+			"res/a.res": "",
+			"res/b.res": "",
+		},
+		Blueprint: `java_binary_host {
+    name: "java-binary-host",
+    manifest: "test.mf",
+    srcs: ["a.java", "b.kt"],
+    java_resources: ["res/a.res", "res/b.res"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"resources": `[
+        "res/a.res",
+        "res/b.res",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+				"main_class":   `"com.android.test.MainClass"`,
+				"runtime_deps": `[":java-binary-host_kt"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 93a6174..0784f4b 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -715,3 +715,43 @@
 		},
 	})
 }
+
+func TestJavaLibraryArchVariantLibs(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with arch variant libs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java"],
+    libs: ["java-lib-2"],
+    target: {
+        android: {
+            libs: ["java-lib-3"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+	java_library{
+		name: "java-lib-2",
+}
+
+	java_library{
+		name: "java-lib-3",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+				"srcs": `["a.java"]`,
+				"deps": `[":java-lib-2-neverlink"] + select({
+        "//build/bazel/platforms/os:android": [":java-lib-3-neverlink"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+			MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
+			MakeBazelTarget("java_library", "java-lib-3", AttrNameToString{}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-3"),
+		},
+	})
+}
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
index d9049d4..8c6337b 100644
--- a/bp2build/java_plugin_conversion_test.go
+++ b/bp2build/java_plugin_conversion_test.go
@@ -60,7 +60,7 @@
         "//conditions:default": [],
     })`,
 				"deps": `[
-        ":java-lib-1",
+        ":java-lib-1-neverlink",
         ":java-lib-2",
     ]`,
 				"srcs": `[
@@ -101,7 +101,7 @@
         "//conditions:default": [],
     })`,
 				"deps": `[
-        ":java-lib-1",
+        ":java-lib-1-neverlink",
         ":java-lib-2",
     ]`,
 			}),
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index bd21629..7e29fac 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -148,11 +148,26 @@
 func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) {
 	metrics.serialized.Events = append(metrics.serialized.Events, event)
 }
+
 func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
 	metrics.serialized.UnconvertedModuleCount += 1
 	metrics.serialized.TotalModuleTypeCount[moduleType] += 1
 }
 
+func (metrics *CodegenMetrics) SetSymlinkCount(n uint64) {
+	if m := metrics.serialized.WorkspaceSymlinkCount; m != 0 {
+		fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceSymlinkCount of %d", m)
+	}
+	metrics.serialized.WorkspaceSymlinkCount = n
+}
+
+func (metrics *CodegenMetrics) SetMkDirCount(n uint64) {
+	if m := metrics.serialized.WorkspaceMkDirCount; m != 0 {
+		fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceDirCount of %d", m)
+	}
+	metrics.serialized.WorkspaceMkDirCount = n
+}
+
 func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
 	return metrics.serialized.HandCraftedModuleCount +
 		metrics.serialized.GeneratedModuleCount +
@@ -173,6 +188,10 @@
 )
 
 func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
+	//a package module has empty name
+	if moduleType == "package" {
+		return
+	}
 	// Undo prebuilt_ module name prefix modifications
 	moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
 	metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index dcd1f85..ba42f34 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -32,6 +32,7 @@
 	android.RegisterSoongConfigModuleBuildComponents(ctx)
 
 	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+	ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 }
 
 func TestErrorInBpFileDoesNotPanic(t *testing.T) {
@@ -192,6 +193,7 @@
     copts = select({
         "//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
         "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+        "//build/bazel/product_variables:acme__board__soc_c": [],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }),
     local_includes = ["."],
@@ -210,7 +212,7 @@
 
 soong_config_string_variable {
 	name: "board",
-	values: ["soc_a", "soc_b", "soc_c"],
+	values: ["soc_a", "soc_b", "soc_c", "soc_d"],
 }
 
 soong_config_module_type {
@@ -263,6 +265,7 @@
     copts = select({
         "//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
         "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+        "//build/bazel/product_variables:acme__board__soc_c": [],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }) + select({
         "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
@@ -279,7 +282,7 @@
 	bp := `
 soong_config_string_variable {
 	name: "board",
-	values: ["soc_a", "soc_b", "soc_c"],
+	values: ["soc_a", "soc_b", "soc_c", "soc_d"],
 }
 
 soong_config_module_type {
@@ -332,11 +335,13 @@
     copts = select({
         "//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
         "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+        "//build/bazel/product_variables:acme__board__soc_c": [],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }),
     implementation_deps = select({
         "//build/bazel/product_variables:acme__board__soc_a": ["//foo/bar:soc_a_dep"],
         "//build/bazel/product_variables:acme__board__soc_b": ["//foo/bar:soc_b_dep"],
+        "//build/bazel/product_variables:acme__board__soc_c": [],
         "//conditions:default": ["//foo/bar:soc_default_static_dep"],
     }),
     local_includes = ["."],
@@ -604,6 +609,234 @@
 )`}})
 }
 
+func TestSoongConfigModuleType_Defaults_UseBaselineValueForStringProp(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_custom",
+    module_type: "custom",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: [
+        "string_literal_prop",
+    ],
+}
+
+library_linking_strategy_custom {
+    name: "foo",
+    string_literal_prop: "29",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+              string_literal_prop: "30",
+            },
+        },
+    },
+}`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem:                 map[string]string{},
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("custom", "foo", AttrNameToString{
+				"string_literal_prop": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "29",
+        "//conditions:default": "30",
+    })`,
+			}),
+		},
+	})
+}
+
+func TestSoongConfigModuleType_UnsetConditions(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: [
+        "shared_libs",
+        "static_libs",
+    ],
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_lib_a_defaults",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+                shared_libs: [
+                    "lib_a",
+                ],
+            },
+        },
+    },
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_merged_defaults",
+    defaults: ["library_linking_strategy_lib_a_defaults"],
+    host_supported: true,
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+                shared_libs: [
+                    "lib_b",
+                ],
+            },
+        },
+    },
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary",
+    srcs: ["library_linking_strategy.cc"],
+    defaults: ["library_linking_strategy_merged_defaults"],
+    include_build_directory: false,
+}`
+
+	otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
+`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem: map[string]string{
+			"foo/bar/Android.bp": otherDeps,
+		},
+		ExpectedBazelTargets: []string{`cc_binary(
+    name = "library_linking_strategy_sample_binary",
+    dynamic_deps = select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": [
+            "//foo/bar:lib_b",
+            "//foo/bar:lib_a",
+        ],
+    }),
+    srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_UnsetConditionsExcludeLibs(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: ["shared_libs"],
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_lib_a_defaults",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+                shared_libs: [
+                    "lib_a",
+                ],
+            },
+        },
+    },
+}
+
+library_linking_strategy_cc_defaults {
+    name: "library_linking_strategy_merged_defaults",
+    defaults: ["library_linking_strategy_lib_a_defaults"],
+    host_supported: true,
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+                shared_libs: [
+                    "lib_b",
+                    "lib_c",
+                ],
+            },
+        },
+    },
+    exclude_shared_libs: ["lib_a"],
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary",
+    defaults: ["library_linking_strategy_merged_defaults"],
+    include_build_directory: false,
+}
+
+cc_binary {
+    name: "library_linking_strategy_sample_binary_with_excludes",
+    defaults: ["library_linking_strategy_merged_defaults"],
+    exclude_shared_libs: ["lib_c"],
+    include_build_directory: false,
+}`
+
+	otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_c", bazel_module: { bp2build_available: false } }
+`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem: map[string]string{
+			"foo/bar/Android.bp": otherDeps,
+		},
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary", AttrNameToString{
+				"dynamic_deps": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": [
+            "//foo/bar:lib_b",
+            "//foo/bar:lib_c",
+        ],
+    })`,
+			}),
+			MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary_with_excludes", AttrNameToString{
+				"dynamic_deps": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//conditions:default": ["//foo/bar:lib_b"],
+    })`,
+			}),
+		}})
+}
+
 func TestSoongConfigModuleType_Defaults(t *testing.T) {
 	bp := `
 soong_config_string_variable {
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 81ec7ee..37188f1 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -22,6 +22,7 @@
 	"os"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"sync"
 	"sync/atomic"
 
@@ -46,9 +47,10 @@
 	topdir  string // $TOPDIR
 
 	// State
-	wg    sync.WaitGroup
-	depCh chan string
-	okay  atomic.Bool // Whether the forest was successfully constructed
+	wg           sync.WaitGroup
+	depCh        chan string
+	mkdirCount   atomic.Uint64
+	symlinkCount atomic.Uint64
 }
 
 // A simple thread pool to limit concurrency on system calls.
@@ -274,22 +276,26 @@
 		}
 	}
 
-	allEntries := make(map[string]struct{})
+	allEntries := make([]string, 0, len(srcDirMap)+len(buildFilesMap))
 	for n := range srcDirMap {
-		allEntries[n] = struct{}{}
+		allEntries = append(allEntries, n)
 	}
-
 	for n := range buildFilesMap {
-		allEntries[n] = struct{}{}
+		if _, ok := srcDirMap[n]; !ok {
+			allEntries = append(allEntries, n)
+		}
 	}
+	// Tests read the error messages generated, so ensure their order is deterministic
+	sort.Strings(allEntries)
 
 	err := os.MkdirAll(shared.JoinPath(context.topdir, forestDir), 0777)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Cannot mkdir '%s': %s\n", forestDir, err)
 		os.Exit(1)
 	}
+	context.mkdirCount.Add(1)
 
-	for f := range allEntries {
+	for _, f := range allEntries {
 		if f[0] == '.' {
 			continue // Ignore dotfiles
 		}
@@ -318,6 +324,7 @@
 		if instructionsChild != nil && instructionsChild.excluded {
 			if bExists {
 				symlinkIntoForest(context.topdir, forestChild, buildFilesChild)
+				context.symlinkCount.Add(1)
 			}
 			continue
 		}
@@ -334,6 +341,7 @@
 			} else {
 				// Not in the source tree, symlink BUILD file
 				symlinkIntoForest(context.topdir, forestChild, buildFilesChild)
+				context.symlinkCount.Add(1)
 			}
 		} else if !bExists {
 			if sDir && instructionsChild != nil {
@@ -344,6 +352,7 @@
 			} else {
 				// Not in the build file tree, symlink source tree, carry on
 				symlinkIntoForest(context.topdir, forestChild, srcChild)
+				context.symlinkCount.Add(1)
 			}
 		} else if sDir && bDir {
 			// Both are directories. Descend.
@@ -360,14 +369,14 @@
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
 					srcBuildFile, generatedBuildFile, err)
-				context.okay.Store(false)
+				os.Exit(1)
 			}
 		} else {
 			// Both exist and one is a file. This is an error.
 			fmt.Fprintf(os.Stderr,
 				"Conflict in workspace symlink tree creation: both '%s' and '%s' exist and exactly one is a directory\n",
 				srcChild, buildFilesChild)
-			context.okay.Store(false)
+			os.Exit(1)
 		}
 	}
 }
@@ -417,19 +426,19 @@
 	wg.Wait()
 }
 
-// Creates a symlink forest by merging the directory tree at "buildFiles" and
+// PlantSymlinkForest Creates a symlink forest by merging the directory tree at "buildFiles" and
 // "srcDir" while excluding paths listed in "exclude". Returns the set of paths
 // under srcDir on which readdir() had to be called to produce the symlink
 // forest.
-func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) []string {
+func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) (deps []string, mkdirCount, symlinkCount uint64) {
 	context := &symlinkForestContext{
-		verbose: verbose,
-		topdir:  topdir,
-		depCh:   make(chan string),
+		verbose:      verbose,
+		topdir:       topdir,
+		depCh:        make(chan string),
+		mkdirCount:   atomic.Uint64{},
+		symlinkCount: atomic.Uint64{},
 	}
 
-	context.okay.Store(true)
-
 	removeParallel(shared.JoinPath(topdir, forest))
 
 	instructions := instructionsFromExcludePathList(exclude)
@@ -440,14 +449,9 @@
 		close(context.depCh)
 	}()
 
-	deps := make([]string, 0)
 	for dep := range context.depCh {
 		deps = append(deps, dep)
 	}
 
-	if !context.okay.Load() {
-		os.Exit(1)
-	}
-
-	return deps
+	return deps, context.mkdirCount.Load(), context.symlinkCount.Load()
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 1f9874c..92a9bf1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -258,6 +258,7 @@
 }
 
 func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
+	t.Helper()
 	if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
 		t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
 			description, expectedCount, expectedContents, actualCount, actualTargets)
@@ -453,6 +454,14 @@
 			}
 		}
 	}
+	productVariableProps := android.ProductVariableProperties(ctx)
+	if props, ok := productVariableProps["String_literal_prop"]; ok {
+		for c, p := range props {
+			if val, ok := p.(*string); ok {
+				strAttr.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
+			}
+		}
+	}
 
 	paths.ResolveExcludes()
 
diff --git a/cc/Android.bp b/cc/Android.bp
index 8860f78..5fd9afe 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -52,7 +52,6 @@
         "vndk.go",
         "vndk_prebuilt.go",
 
-        "cflag_artifacts.go",
         "cmakelists.go",
         "compdb.go",
         "compiler.go",
diff --git a/cc/api_level.go b/cc/api_level.go
index fdff5cb..a5571f3 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,9 @@
 	"android/soong/android"
 )
 
-func minApiForArch(ctx android.EarlyModuleContext,
+// MinApiLevelForArch returns the ApiLevel for the Android version that
+// first supported the architecture.
+func MinApiForArch(ctx android.EarlyModuleContext,
 	arch android.ArchType) android.ApiLevel {
 
 	switch arch {
@@ -38,7 +40,7 @@
 func nativeApiLevelFromUser(ctx android.BaseModuleContext,
 	raw string) (android.ApiLevel, error) {
 
-	min := minApiForArch(ctx, ctx.Arch().ArchType)
+	min := MinApiForArch(ctx, ctx.Arch().ArchType)
 	if raw == "minimum" {
 		return min, nil
 	}
diff --git a/cc/binary.go b/cc/binary.go
index 54c1abc..2f8ee7f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -591,6 +591,8 @@
 	outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 	handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+	handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
 }
 
 func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
diff --git a/cc/bp2build.go b/cc/bp2build.go
index d331d89..6c5505a 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -91,12 +91,12 @@
 	}
 	archVariantProps := m.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
 	for axis, configToProps := range archVariantProps {
-		for config, _props := range configToProps {
+		for cfg, _props := range configToProps {
 			if archProps, ok := _props.(*BaseCompilerProperties); ok {
 				archDisabledSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_disabled_srcs)
-				moduleAttrs.Tidy_disabled_srcs.SetSelectValue(axis, config, archDisabledSrcs)
+				moduleAttrs.Tidy_disabled_srcs.SetSelectValue(axis, cfg, archDisabledSrcs)
 				archTimeoutSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_timeout_srcs)
-				moduleAttrs.Tidy_timeout_srcs.SetSelectValue(axis, config, archTimeoutSrcs)
+				moduleAttrs.Tidy_timeout_srcs.SetSelectValue(axis, cfg, archTimeoutSrcs)
 			}
 		}
 	}
@@ -205,14 +205,14 @@
 
 func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
-		for config, props := range configToProps {
-			parseFunc(axis, config, props)
+		for cfg, props := range configToProps {
+			parseFunc(axis, cfg, props)
 		}
 	}
 }
 
 // Parses properties common to static and shared libraries. Also used for prebuilt libraries.
-func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, _ *libraryDecorator, isStatic bool) staticOrSharedAttributes {
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
@@ -669,8 +669,8 @@
 
 	ret := &bazel.LabelAttribute{}
 	for _, axis := range ca.asmSrcs.SortedConfigurationAxes() {
-		for config := range ca.asmSrcs.ConfigurableValues[axis] {
-			ret.SetSelectValue(axis, config, bazel.Label{Label: ":" + m.Name() + "_yasm"})
+		for cfg := range ca.asmSrcs.ConfigurableValues[axis] {
+			ret.SetSelectValue(axis, cfg, bazel.Label{Label: ":" + m.Name() + "_yasm"})
 		}
 	}
 	return ret
@@ -690,8 +690,8 @@
 			if _, ok := axisToConfigs[axis]; !ok {
 				axisToConfigs[axis] = map[string]bool{}
 			}
-			for config, _ := range configMap {
-				axisToConfigs[axis][config] = true
+			for cfg := range configMap {
+				axisToConfigs[axis][cfg] = true
 			}
 		}
 	}
@@ -702,50 +702,56 @@
 	compilerAttrs := compilerAttributes{}
 	linkerAttrs := linkerAttributes{}
 
-	for axis, configs := range axisToConfigs {
-		for config, _ := range configs {
+	// Iterate through these axes in a deterministic order. This is required
+	// because processing certain dependencies may result in concatenating
+	// elements along other axes. (For example, processing NoConfig may result
+	// in elements being added to InApex). This is thus the only way to ensure
+	// that the order of entries in each list is in a predictable order.
+	for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
+		configs := axisToConfigs[axis]
+		for cfg := range configs {
 			var allHdrs []string
-			if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
+			if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
 				allHdrs = baseCompilerProps.Generated_headers
 				if baseCompilerProps.Lex != nil {
-					compilerAttrs.lexopts.SetSelectValue(axis, config, baseCompilerProps.Lex.Flags)
+					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
 				}
-				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
+				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, cfg, baseCompilerProps)
 			}
 
 			var exportHdrs []string
 
-			if baseLinkerProps, ok := archVariantLinkerProps[axis][config].(*BaseLinkerProperties); ok {
+			if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
 				exportHdrs = baseLinkerProps.Export_generated_headers
 
-				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, config, baseLinkerProps)
+				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, cfg, baseLinkerProps)
 			}
 			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
-			implementationHdrs.SetSelectValue(axis, config, headers.implementation)
-			compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
+			implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
+			compilerAttrs.hdrs.SetSelectValue(axis, cfg, headers.export)
 
 			exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
-			compilerAttrs.includes.Includes.SetSelectValue(axis, config, exportIncludes)
-			compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, config, exportAbsoluteIncludes)
+			compilerAttrs.includes.Includes.SetSelectValue(axis, cfg, exportIncludes)
+			compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, cfg, exportAbsoluteIncludes)
 
 			includes, absoluteIncludes := includesFromLabelList(headers.implementation)
-			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
+			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, cfg)
 			currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
 
-			compilerAttrs.absoluteIncludes.SetSelectValue(axis, config, currAbsoluteIncludes)
+			compilerAttrs.absoluteIncludes.SetSelectValue(axis, cfg, currAbsoluteIncludes)
 
-			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, config)
+			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, cfg)
 			currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
 
-			compilerAttrs.localIncludes.SetSelectValue(axis, config, currIncludes)
+			compilerAttrs.localIncludes.SetSelectValue(axis, cfg, currIncludes)
 
-			if libraryProps, ok := archVariantLibraryProperties[axis][config].(*LibraryProperties); ok {
+			if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
 				if axis == bazel.NoConfigAxis {
 					compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
-					compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
+					compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, libraryProps.Stubs.Versions)
 				}
 				if suffix := libraryProps.Suffix; suffix != nil {
-					compilerAttrs.suffix.SetSelectValue(axis, config, suffix)
+					compilerAttrs.suffix.SetSelectValue(axis, cfg, suffix)
 				}
 			}
 		}
@@ -813,6 +819,8 @@
 	features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
 	features.DeduplicateAxesFromBase()
 
+	addMuslSystemDynamicDeps(ctx, linkerAttrs)
+
 	return baseAttributes{
 		compilerAttrs,
 		linkerAttrs,
@@ -823,6 +831,16 @@
 	}
 }
 
+// As a workaround for b/261657184, we are manually adding the default value
+// of system_dynamic_deps for the linux_musl os.
+// TODO: Solve this properly
+func addMuslSystemDynamicDeps(ctx android.Bp2buildMutatorContext, attrs linkerAttributes) {
+	systemDynamicDeps := attrs.systemDynamicDeps.SelectValue(bazel.OsConfigurationAxis, "linux_musl")
+	if attrs.systemDynamicDeps.HasAxisSpecificValues(bazel.OsConfigurationAxis) && systemDynamicDeps.IsNil() {
+		attrs.systemDynamicDeps.SetSelectValue(bazel.OsConfigurationAxis, "linux_musl", android.BazelLabelForModuleDeps(ctx, config.MuslDefaultSharedLibraries))
+	}
+}
+
 type fdoProfileAttributes struct {
 	Absolute_path_profile string
 }
@@ -967,34 +985,22 @@
 
 // resolveTargetApex re-adds the shared and static libs in target.apex.exclude_shared|static_libs props to non-apex variant
 // since all libs are already excluded by default
-func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, isBinary bool, props *BaseLinkerProperties) {
-	sharedLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
-		ctx,
-		true,
-		props.Target.Apex.Exclude_shared_libs,
-		props.Export_shared_lib_headers,
-		bazelLabelForSharedDeps,
-	)
-	dynamicDeps := la.dynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	implDynamicDeps := la.implementationDynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	(&dynamicDeps).Append(sharedLibsForNonApex.export)
-	(&implDynamicDeps).Append(sharedLibsForNonApex.implementation)
-	la.dynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, dynamicDeps)
-	la.implementationDynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDynamicDeps)
+func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, props *BaseLinkerProperties) {
+	excludeSharedLibs := bazelLabelForSharedDeps(ctx, props.Target.Apex.Exclude_shared_libs)
+	sharedExcludes := bazel.LabelList{Excludes: excludeSharedLibs.Includes}
+	sharedExcludesLabelList := bazel.LabelListAttribute{}
+	sharedExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, sharedExcludes)
 
-	staticLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
-		ctx,
-		!isBinary,
-		props.Target.Apex.Exclude_static_libs,
-		props.Export_static_lib_headers,
-		bazelLabelForSharedDeps,
-	)
-	deps := la.deps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	implDeps := la.implementationDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
-	(&deps).Append(staticLibsForNonApex.export)
-	(&implDeps).Append(staticLibsForNonApex.implementation)
-	la.deps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, deps)
-	la.implementationDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDeps)
+	la.dynamicDeps.Append(sharedExcludesLabelList)
+	la.implementationDynamicDeps.Append(sharedExcludesLabelList)
+
+	excludeStaticLibs := bazelLabelForStaticDeps(ctx, props.Target.Apex.Exclude_static_libs)
+	staticExcludes := bazel.LabelList{Excludes: excludeStaticLibs.Includes}
+	staticExcludesLabelList := bazel.LabelListAttribute{}
+	staticExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, staticExcludes)
+
+	la.deps.Append(staticExcludesLabelList)
+	la.implementationDeps.Append(staticExcludesLabelList)
 }
 
 func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
@@ -1028,8 +1034,7 @@
 		ctx,
 		!isBinary,
 		staticLibs,
-		// Exclude static libs in Exclude_static_libs and Target.Apex.Exclude_static_libs props
-		append(props.Exclude_static_libs, props.Target.Apex.Exclude_static_libs...),
+		props.Exclude_static_libs,
 		props.Export_static_lib_headers,
 		bazelLabelForStaticDepsExcludes,
 	)
@@ -1068,14 +1073,13 @@
 		ctx,
 		!isBinary,
 		sharedLibs,
-		// Exclude shared libs in Exclude_shared_libs and Target.Apex.Exclude_shared_libs props
-		append(props.Exclude_shared_libs, props.Target.Apex.Exclude_shared_libs...),
+		props.Exclude_shared_libs,
 		props.Export_shared_lib_headers,
 		bazelLabelForSharedDepsExcludes,
 	)
 	la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
 	la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
-	la.resolveTargetApexProp(ctx, isBinary, props)
+	la.resolveTargetApexProp(ctx, props)
 
 	if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
 		// If a dependency in la.implementationDynamicDeps has stubs, its stub variant should be
@@ -1213,7 +1217,7 @@
 	for name, dep := range productVarToDepFields {
 		props, exists := productVariableProps[name]
 		excludeProps, excludesExists := productVariableProps[dep.excludesField]
-		// if neither an include or excludes property exists, then skip it
+		// if neither an include nor excludes property exists, then skip it
 		if !exists && !excludesExists {
 			continue
 		}
@@ -1352,7 +1356,7 @@
 
 func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
 	label := android.BazelModuleLabel(ctx, m)
-	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(m.Name()) {
+	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary {
 		return BazelLabelNameForStaticModule(label)
 	}
 	return label
diff --git a/cc/builder.go b/cc/builder.go
index 0629406..fef00d4 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -519,6 +519,13 @@
 	cppflags += " ${config.NoOverrideGlobalCflags}"
 	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
 
+	if flags.toolchain.Is64Bit() {
+		cflags += " ${config.NoOverride64GlobalCflags}"
+		toolingCflags += " ${config.NoOverride64GlobalCflags}"
+		cppflags += " ${config.NoOverride64GlobalCflags}"
+		toolingCppflags += " ${config.NoOverride64GlobalCflags}"
+	}
+
 	modulePath := android.PathForModuleSrc(ctx).String()
 	if android.IsThirdPartyPath(modulePath) {
 		cflags += " ${config.NoOverrideExternalGlobalCflags}"
diff --git a/cc/cc.go b/cc/cc.go
index cb425c3..c33c5e3 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,6 +28,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
@@ -1445,6 +1446,8 @@
 }
 
 func InstallToBootstrap(name string, config android.Config) bool {
+	// NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
+	// if this list is updated.
 	if name == "libclang_rt.hwasan" {
 		return true
 	}
@@ -1859,6 +1862,10 @@
 var (
 	mixedBuildSupportedCcTest = []string{
 		"adbd_test",
+		"adb_crypto_test",
+		"adb_pairing_auth_test",
+		"adb_pairing_connection_test",
+		"adb_tls_connection_test",
 	}
 )
 
@@ -1866,7 +1873,8 @@
 // in any of the --bazel-mode(s). This filters at the module level and takes
 // precedence over the allowlists in allowlists/allowlists.go.
 func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
-	if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) {
+	_, isForTesting := ctx.Config().BazelContext.(android.MockBazelContext)
+	if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) && !isForTesting {
 		// Per-module rollout of mixed-builds for cc_test modules.
 		return false
 	}
@@ -2081,6 +2089,12 @@
 	}
 }
 
+func (c *Module) setAndroidMkVariablesFromCquery(info cquery.CcAndroidMkInfo) {
+	c.Properties.AndroidMkSharedLibs = info.LocalSharedLibs
+	c.Properties.AndroidMkStaticLibs = info.LocalStaticLibs
+	c.Properties.AndroidMkWholeStaticLibs = info.LocalWholeStaticLibs
+}
+
 func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
 	if c.cachedToolchain == nil {
 		c.cachedToolchain = config.FindToolchainWithContext(ctx)
@@ -2212,6 +2226,13 @@
 		if err != nil {
 			ctx.PropertyErrorf("min_sdk_version", err.Error())
 		}
+
+		// Raise the minSdkVersion to the minimum supported for the architecture.
+		minApiForArch := MinApiForArch(ctx, m.Target().Arch.ArchType)
+		if apiLevel.LessThan(minApiForArch) {
+			apiLevel = minApiForArch
+		}
+
 		return []blueprint.Variation{
 			{Mutator: "sdk", Variation: "sdk"},
 			{Mutator: "version", Variation: apiLevel.String()},
@@ -3693,7 +3714,7 @@
 	// This allows introducing new architectures in the platform that
 	// need to be included in apexes that normally require an older
 	// min_sdk_version.
-	minApiForArch := minApiForArch(ctx, c.Target().Arch.ArchType)
+	minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
 	if sdkVersion.LessThan(minApiForArch) {
 		sdkVersion = minApiForArch
 	}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 6dfd395..62adfd3 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -25,6 +25,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/bazel/cquery"
 )
 
 func TestMain(m *testing.M) {
@@ -3054,6 +3055,253 @@
 	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
 }
 
+func TestLibDepAndroidMkExportInMixedBuilds(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "static_dep",
+		}
+		cc_library {
+			name: "whole_static_dep",
+		}
+		cc_library {
+			name: "shared_dep",
+		}
+		cc_library {
+			name: "lib",
+			bazel_module: { label: "//:lib" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+		cc_test {
+			name: "test",
+			bazel_module: { label: "//:test" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+			gtest: false,
+		}
+		cc_binary {
+			name: "binary",
+			bazel_module: { label: "//:binary" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+		cc_library_headers {
+			name: "lib_headers",
+			bazel_module: { label: "//:lib_headers" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+		cc_prebuilt_library {
+			name: "lib_prebuilt",
+			bazel_module: { label: "//:lib_prebuilt" },
+			static_libs: ["static_dep"],
+			whole_static_libs: ["whole_static_dep"],
+			shared_libs: ["shared_dep"],
+		}
+	`
+
+	testCases := []struct {
+		name          string
+		moduleName    string
+		variant       string
+		androidMkInfo cquery.CcAndroidMkInfo
+	}{
+		{
+			name:       "shared lib",
+			moduleName: "lib",
+			variant:    "android_arm64_armv8-a_shared",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "static lib",
+			moduleName: "lib",
+			variant:    "android_arm64_armv8-a_static",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_test arm64",
+			moduleName: "test",
+			variant:    "android_arm64_armv8-a",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_test arm",
+			moduleName: "test",
+			variant:    "android_arm_armv7-a-neon",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_binary",
+			moduleName: "binary",
+			variant:    "android_arm64_armv8-a",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "cc_library_headers",
+			moduleName: "lib_headers",
+			variant:    "android_arm64_armv8-a",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "prebuilt lib static",
+			moduleName: "lib_prebuilt",
+			variant:    "android_arm64_armv8-a_static",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+		{
+			name:       "prebuilt lib shared",
+			moduleName: "lib_prebuilt",
+			variant:    "android_arm64_armv8-a_shared",
+			androidMkInfo: cquery.CcAndroidMkInfo{
+				LocalStaticLibs:      []string{"static_dep"},
+				LocalWholeStaticLibs: []string{"whole_static_dep"},
+				LocalSharedLibs:      []string{"shared_dep"},
+			},
+		},
+	}
+
+	outputBaseDir := "out/bazel"
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				prepareForCcTest,
+				android.FixtureModifyConfig(func(config android.Config) {
+					config.BazelContext = android.MockBazelContext{
+						OutputBaseDir: outputBaseDir,
+						LabelToCcInfo: map[string]cquery.CcInfo{
+							"//:lib": cquery.CcInfo{
+								CcAndroidMkInfo:      tc.androidMkInfo,
+								RootDynamicLibraries: []string{""},
+							},
+							"//:lib_bp2build_cc_library_static": cquery.CcInfo{
+								CcAndroidMkInfo:    tc.androidMkInfo,
+								RootStaticArchives: []string{""},
+							},
+							"//:lib_headers": cquery.CcInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+								OutputFiles:     []string{""},
+							},
+							"//:lib_prebuilt": cquery.CcInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+							"//:lib_prebuilt_bp2build_cc_library_static": cquery.CcInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+						},
+						LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+							"//:test": cquery.CcUnstrippedInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+							"//:binary": cquery.CcUnstrippedInfo{
+								CcAndroidMkInfo: tc.androidMkInfo,
+							},
+						},
+					}
+				}),
+			).RunTestWithBp(t, bp)
+			ctx := result.TestContext
+
+			module := ctx.ModuleForTests(tc.moduleName, tc.variant).Module().(*Module)
+			entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+			if !reflect.DeepEqual(module.Properties.AndroidMkStaticLibs, tc.androidMkInfo.LocalStaticLibs) {
+				t.Errorf("incorrect static_libs"+
+					"\nactual:   %v"+
+					"\nexpected: %v",
+					module.Properties.AndroidMkStaticLibs,
+					tc.androidMkInfo.LocalStaticLibs,
+				)
+			}
+			staticDepsDiffer, missingStaticDeps, additionalStaticDeps := android.ListSetDifference(
+				entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+				tc.androidMkInfo.LocalStaticLibs,
+			)
+			if staticDepsDiffer {
+				t.Errorf(
+					"expected LOCAL_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+					tc.androidMkInfo.LocalStaticLibs,
+					entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+					missingStaticDeps,
+					additionalStaticDeps,
+				)
+			}
+
+			if !reflect.DeepEqual(module.Properties.AndroidMkWholeStaticLibs, tc.androidMkInfo.LocalWholeStaticLibs) {
+				t.Errorf("expected module.Properties.AndroidMkWholeStaticLibs to be %q, but was %q",
+					tc.androidMkInfo.LocalWholeStaticLibs,
+					module.Properties.AndroidMkWholeStaticLibs,
+				)
+			}
+			wholeStaticDepsDiffer, missingWholeStaticDeps, additionalWholeStaticDeps := android.ListSetDifference(
+				entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+				tc.androidMkInfo.LocalWholeStaticLibs,
+			)
+			if wholeStaticDepsDiffer {
+				t.Errorf(
+					"expected LOCAL_WHOLE_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+					tc.androidMkInfo.LocalWholeStaticLibs,
+					entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+					missingWholeStaticDeps,
+					additionalWholeStaticDeps,
+				)
+			}
+
+			if !reflect.DeepEqual(module.Properties.AndroidMkSharedLibs, tc.androidMkInfo.LocalSharedLibs) {
+				t.Errorf("incorrect shared_libs"+
+					"\nactual:   %v"+
+					"\nexpected: %v",
+					module.Properties.AndroidMkSharedLibs,
+					tc.androidMkInfo.LocalSharedLibs,
+				)
+			}
+			sharedDepsDiffer, missingSharedDeps, additionalSharedDeps := android.ListSetDifference(
+				entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+				tc.androidMkInfo.LocalSharedLibs,
+			)
+			if sharedDepsDiffer {
+				t.Errorf(
+					"expected LOCAL_SHARED_LIBRARIES to be %q but was %q; missing %q; extra %q",
+					tc.androidMkInfo.LocalSharedLibs,
+					entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+					missingSharedDeps,
+					additionalSharedDeps,
+				)
+			}
+		})
+	}
+}
+
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
@@ -4483,6 +4731,39 @@
 
 }
 
+func TestAddnoOverride64GlobalCflags(t *testing.T) {
+	t.Parallel()
+	ctx := testCc(t, `
+		cc_library_shared {
+			name: "libclient",
+			srcs: ["foo.c"],
+			shared_libs: ["libfoo#1"],
+		}
+
+		cc_library_shared {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			export_shared_lib_headers: ["libbar"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+				versions: ["1", "2", "3"],
+			},
+		}
+
+		cc_library_shared {
+			name: "libbar",
+			export_include_dirs: ["include/libbar"],
+			srcs: ["foo.c"],
+		}`)
+
+	cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+
+	if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") {
+		t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags)
+	}
+}
+
 func TestCcBuildBrokenClangProperty(t *testing.T) {
 	t.Parallel()
 	tests := []struct {
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
deleted file mode 100644
index be46fc0..0000000
--- a/cc/cflag_artifacts.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package cc
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-)
-
-func init() {
-	android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
-}
-
-var (
-	TrackedCFlags = []string{
-		"-Wall",
-		"-Werror",
-		"-Wextra",
-		"-Wthread-safety",
-		"-O3",
-	}
-
-	TrackedCFlagsDir = []string{
-		"device/google/",
-		"vendor/google/",
-	}
-)
-
-const FileBP = 50
-
-// Stores output files.
-type cflagArtifactsText struct {
-	interOutputs map[string]android.WritablePaths
-	outputs      android.WritablePaths
-}
-
-// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
-// filter.
-func allowedDir(subdir string) bool {
-	subdir += "/"
-	return android.HasAnyPrefix(subdir, TrackedCFlagsDir)
-}
-
-func (s *cflagArtifactsText) genFlagFilename(flag string) string {
-	return fmt.Sprintf("module_cflags%s.txt", flag)
-}
-
-// incrementFile is used to generate an output path object with the passed in flag
-// and part number.
-// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
-func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
-	flag string, part int) (string, android.OutputPath) {
-
-	filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
-	filepath := android.PathForOutput(ctx, "cflags", filename)
-	s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
-	return filename, filepath
-}
-
-// GenCFlagArtifactParts is used to generate the build rules which produce the
-// intermediary files for each desired C Flag artifact
-// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
-func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
-	flag string, using bool, modules []string, part int) int {
-
-	cleanedName := strings.Replace(flag, "=", "_", -1)
-	filename, filepath := s.incrementFile(ctx, cleanedName, part)
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().Textf("rm -f %s", filepath.String())
-
-	if using {
-		rule.Command().
-			Textf("echo '# Modules using %s'", flag).
-			FlagWithOutput(">> ", filepath)
-	} else {
-		rule.Command().
-			Textf("echo '# Modules not using %s'", flag).
-			FlagWithOutput(">> ", filepath)
-	}
-
-	length := len(modules)
-
-	if length == 0 {
-		rule.Build(filename, "gen "+filename)
-		part++
-	}
-
-	// Following loop splits the module list for each tracked C Flag into
-	// chunks of length FileBP (file breakpoint) and generates a partial artifact
-	// (intermediary file) build rule for each split.
-	moduleShards := android.ShardStrings(modules, FileBP)
-	for index, shard := range moduleShards {
-		rule.Command().
-			Textf("for m in %s; do echo $m",
-				strings.Join(proptools.ShellEscapeList(shard), " ")).
-			FlagWithOutput(">> ", filepath).
-			Text("; done")
-		rule.Build(filename, "gen "+filename)
-
-		if index+1 != len(moduleShards) {
-			filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
-			rule = android.NewRuleBuilder(pctx, ctx)
-			rule.Command().Textf("rm -f %s", filepath.String())
-		}
-	}
-
-	return part + len(moduleShards)
-}
-
-// GenCFlagArtifacts is used to generate build rules which combine the
-// intermediary files of a specific tracked flag into a single C Flag artifact
-// for each tracked flag.
-// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
-func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
-	// Scans through s.interOutputs and creates a build rule for each tracked C
-	// Flag that concatenates the associated intermediary file into a single
-	// artifact.
-	for _, flag := range TrackedCFlags {
-		// Generate build rule to combine related intermediary files into a
-		// C Flag artifact
-		rule := android.NewRuleBuilder(pctx, ctx)
-		filename := s.genFlagFilename(flag)
-		outputpath := android.PathForOutput(ctx, "cflags", filename)
-		rule.Command().
-			Text("cat").
-			Inputs(s.interOutputs[flag].Paths()).
-			FlagWithOutput("> ", outputpath)
-		rule.Build(filename, "gen "+filename)
-		s.outputs = append(s.outputs, outputpath)
-	}
-}
-
-func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
-	modulesWithCFlag := make(map[string][]string)
-
-	// Scan through all modules, selecting the ones that are part of the filter,
-	// and then storing into a map which tracks whether or not tracked C flag is
-	// used or not.
-	ctx.VisitAllModules(func(module android.Module) {
-		if ccModule, ok := module.(*Module); ok {
-			if allowedDir(ctx.ModuleDir(ccModule)) {
-				cflags := ccModule.flags.Local.CFlags
-				cppflags := ccModule.flags.Local.CppFlags
-				module := fmt.Sprintf("%s:%s (%s)",
-					ctx.BlueprintFile(ccModule),
-					ctx.ModuleName(ccModule),
-					ctx.ModuleSubDir(ccModule))
-				for _, flag := range TrackedCFlags {
-					if inList(flag, cflags) || inList(flag, cppflags) {
-						modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
-					} else {
-						modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
-					}
-				}
-			}
-		}
-	})
-
-	// Traversing map and setting up rules to produce intermediary files which
-	// contain parts of each expected C Flag artifact.
-	for _, flag := range TrackedCFlags {
-		sort.Strings(modulesWithCFlag[flag])
-		part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
-		sort.Strings(modulesWithCFlag["!"+flag])
-		s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
-	}
-
-	// Combine intermediary files into a single C Flag artifact.
-	s.GenCFlagArtifacts(ctx)
-}
-
-func cflagArtifactsTextFactory() android.Singleton {
-	return &cflagArtifactsText{
-		interOutputs: make(map[string]android.WritablePaths),
-	}
-}
-
-func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
-}
diff --git a/cc/compiler.go b/cc/compiler.go
index a751754..88985b6 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -416,11 +416,6 @@
 
 	if ctx.apexVariationName() != "" {
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
-		if ctx.Device() {
-			flags.Global.CommonFlags = append(flags.Global.CommonFlags,
-				fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
-					ctx.apexSdkVersion().FinalOrFutureInt()))
-		}
 	}
 
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/cc/config/global.go b/cc/config/global.go
index e78839b..d557c0b 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -247,6 +247,8 @@
 		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
 	}
 
+	noOverride64GlobalCflags = []string{}
+
 	noOverrideExternalGlobalCflags = []string{
 		// http://b/148815709
 		"-Wno-sizeof-array-div",
@@ -369,10 +371,6 @@
 			flags = append(flags, "-Wno-unused-command-line-argument")
 		}
 
-		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
-			flags = append(flags, llvmNextExtraCommonGlobalCflags...)
-		}
-
 		if ctx.Config().IsEnvTrue("ALLOW_UNKNOWN_WARNING_OPTION") {
 			flags = append(flags, "-Wno-error=unknown-warning-option")
 		}
@@ -388,8 +386,18 @@
 		return strings.Join(deviceGlobalCflags, " ")
 	})
 
+	// Export the static default NoOverrideGlobalCflags to Bazel.
+	exportedVars.ExportStringList("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+	pctx.VariableFunc("NoOverrideGlobalCflags", func(ctx android.PackageVarContext) string {
+		flags := noOverrideGlobalCflags
+		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+			flags = append(noOverrideGlobalCflags, llvmNextExtraCommonGlobalCflags...)
+		}
+		return strings.Join(flags, " ")
+	})
+
+	exportedVars.ExportStringListStaticVariable("NoOverride64GlobalCflags", noOverride64GlobalCflags)
 	exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
-	exportedVars.ExportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
 	exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
 	exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
 	exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 1180da4..d55a13d 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -40,6 +40,8 @@
 		"-cert-err33-c",
 		// http://b/241125373
 		"-bugprone-unchecked-optional-access",
+		// http://b/265438407
+		"-misc-use-anonymous-namespace",
 	}
 
 	// Some clang-tidy checks are included in some tidy_checks_as_errors lists,
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 740405e..93aa82e 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -112,7 +112,7 @@
 	muslCrtBeginSharedBinary, muslCrtEndSharedBinary   = []string{"libc_musl_crtbegin_dynamic"}, []string{"libc_musl_crtend"}
 	muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
 
-	muslDefaultSharedLibraries = []string{"libc_musl"}
+	MuslDefaultSharedLibraries = []string{"libc_musl"}
 )
 
 const (
@@ -331,7 +331,7 @@
 func (toolchainMusl) CrtEndSharedBinary() []string    { return muslCrtEndSharedBinary }
 func (toolchainMusl) CrtEndSharedLibrary() []string   { return muslCrtEndSharedLibrary }
 
-func (toolchainMusl) DefaultSharedLibraries() []string { return muslDefaultSharedLibraries }
+func (toolchainMusl) DefaultSharedLibraries() []string { return MuslDefaultSharedLibraries }
 
 func (toolchainMusl) Cflags() string {
 	return "${config.LinuxMuslCflags}"
diff --git a/cc/library.go b/cc/library.go
index 787de44..b644728 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -292,14 +292,6 @@
 }
 
 func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
-	// For some cc_library modules, their static variants are ready to be
-	// converted, but not their shared variants. For these modules, delegate to
-	// the cc_library_static bp2build converter temporarily instead.
-	if android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
-		sharedOrStaticLibraryBp2Build(ctx, m, true)
-		return
-	}
-
 	sharedAttrs := bp2BuildParseSharedProps(ctx, m)
 	staticAttrs := bp2BuildParseStaticProps(ctx, m)
 	baseAttributes := bp2BuildParseBaseProps(ctx, m)
@@ -413,12 +405,12 @@
 	sharedTargetAttrs.Suffix = compilerAttrs.suffix
 
 	for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
-		for config, props := range configToProps {
+		for cfg, props := range configToProps {
 			if props, ok := props.(*LibraryProperties); ok {
 				if props.Inject_bssl_hash != nil {
 					// This is an edge case applies only to libcrypto
 					if m.Name() == "libcrypto" || m.Name() == "libcrypto_for_testing" {
-						sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, config, props.Inject_bssl_hash)
+						sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, cfg, props.Inject_bssl_hash)
 					} else {
 						ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
 					}
@@ -483,10 +475,10 @@
 func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
 	apiSurfaces := make([]string, 0)
 	apiHeaders := make([]string, 0)
-	// systemapi (non-null `stubs` property)
+	// module-libapi for apexes (non-null `stubs` property)
 	if module.HasStubsVariants() {
-		apiSurfaces = append(apiSurfaces, android.SystemApi.String())
-		apiIncludes := getSystemApiIncludes(ctx, module)
+		apiSurfaces = append(apiSurfaces, android.ModuleLibApi.String())
+		apiIncludes := getModuleLibApiIncludes(ctx, module)
 		if !apiIncludes.isEmpty() {
 			createApiHeaderTarget(ctx, apiIncludes)
 			apiHeaders = append(apiHeaders, apiIncludes.name)
@@ -502,8 +494,8 @@
 		}
 	}
 	// create a target only if this module contributes to an api surface
-	// TODO: Currently this does not distinguish systemapi-only headers and vendrorapi-only headers
-	// TODO: Update so that systemapi-only headers do not get exported to vendorapi (and vice-versa)
+	// TODO: Currently this does not distinguish modulelibapi-only headers and vendrorapi-only headers
+	// TODO: Update so that modulelibapi-only headers do not get exported to vendorapi (and vice-versa)
 	if len(apiSurfaces) > 0 {
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "cc_api_contribution",
@@ -535,8 +527,8 @@
 	linker := module.linker.(*libraryDecorator)
 	if llndkApi := linker.Properties.Llndk.Symbol_file; llndkApi != nil {
 		apiFile = llndkApi
-	} else if systemApi := linker.Properties.Stubs.Symbol_file; systemApi != nil {
-		apiFile = systemApi
+	} else if moduleLibApi := linker.Properties.Stubs.Symbol_file; moduleLibApi != nil {
+		apiFile = moduleLibApi
 	} else {
 		ctx.ModuleErrorf("API surface library does not have any API file")
 	}
@@ -574,7 +566,8 @@
 	includes.attrs.Deps.Append(lla)
 }
 
-func getSystemApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+// includes provided to the module-lib API surface. This API surface is used by apexes.
+func getModuleLibApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
 	flagProps := c.library.(*libraryDecorator).flagExporter.Properties
 	linkProps := c.library.(*libraryDecorator).baseLinker.Properties
 	includes := android.FirstUniqueStrings(flagProps.Export_include_dirs)
@@ -587,7 +580,7 @@
 	}
 
 	return apiIncludes{
-		name: c.Name() + ".systemapi.headers",
+		name: c.Name() + ".module-libapi.headers",
 		attrs: bazelCcApiLibraryHeadersAttributes{
 			bazelCcLibraryHeadersAttributes: attrs,
 		},
@@ -944,6 +937,8 @@
 		// implementation.
 		i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
 	}
+
+	handler.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
 }
 
 func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
@@ -1034,7 +1029,7 @@
 	return ret
 }
 
-func GlobGeneratedHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
+func GlobGeneratedHeadersForSnapshot(_ android.ModuleContext, paths android.Paths) android.Paths {
 	ret := android.Paths{}
 	for _, header := range paths {
 		// TODO(b/148123511): remove exportedDeps after cleaning up genrule
@@ -1247,6 +1242,10 @@
 		// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
 		// systemapi, respectively. The former is for symbols defined in platform libraries
 		// and the latter is for symbols defined in APEXes.
+		// A single library can contain either # apex or # systemapi, but not both.
+		// The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
+		// However, having this distinction helps guard accidental
+		// promotion or demotion of API and also helps the API review process b/191371676
 		var flag string
 		if ctx.Module().(android.ApexModule).NotInPlatform() {
 			flag = "--apex"
@@ -1900,7 +1899,7 @@
 	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/master/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
 
 	library.sourceAbiDiff(ctx, referenceDump, baseName, prevVersion,
-		isLlndkOrNdk, /* allowExtensions */ true, sourceVersion, errorMessage)
+		isLlndkOrNdk, true /* allowExtensions */, sourceVersion, errorMessage)
 }
 
 func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
@@ -1909,7 +1908,7 @@
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
 
-	library.sourceAbiDiff(ctx, referenceDump, baseName, /* nameExt */ "",
+	library.sourceAbiDiff(ctx, referenceDump, baseName, "",
 		isLlndkOrNdk, allowExtensions, "current", errorMessage)
 }
 
@@ -1920,7 +1919,7 @@
 	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
 
 	library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
-		isLlndkOrNdk, /* allowExtensions */ false, "current", errorMessage)
+		isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
 }
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 4d38068..6440ee2 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -88,6 +88,8 @@
 	// validation will fail. For now, set this to an empty list.
 	// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
 	h.library.collectedSnapshotHeaders = android.Paths{}
+
+	h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
 }
 
 // cc_library_headers contains a set of c/c++ headers which are imported by
@@ -169,7 +171,7 @@
 	// For API export, create a top-level arch-agnostic target and list the arch-specific targets as its deps
 
 	// arch-agnostic includes
-	apiIncludes := getSystemApiIncludes(ctx, module)
+	apiIncludes := getModuleLibApiIncludes(ctx, module)
 	// arch and os specific includes
 	archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
 	for _, arch := range allArches { // sorted iteration
@@ -186,7 +188,7 @@
 	}
 
 	if !apiIncludes.isEmpty() {
-		// override the name from <mod>.systemapi.headers --> <mod>.contribution
+		// override the name from <mod>.module-libapi.headers --> <mod>.contribution
 		apiIncludes.name = android.ApiContributionTargetName(module.Name())
 		createApiHeaderTarget(ctx, apiIncludes)
 	}
diff --git a/cc/library_stub.go b/cc/library_stub.go
index d21df51..08a5eb6 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -205,6 +205,14 @@
 				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
 					d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
 					variantMod.exportProperties.Export_include_dirs...)
+
+				// Export headers as system include dirs if specified. Mostly for libc
+				if Bool(variantMod.exportProperties.Export_headers_as_system) {
+					d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
+						d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
+						d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+				}
 			}
 		}
 	}
diff --git a/cc/linker.go b/cc/linker.go
index 625d89c..371d78d 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -15,10 +15,10 @@
 package cc
 
 import (
-	"fmt"
-
 	"android/soong/android"
 	"android/soong/cc/config"
+	"fmt"
+	"path/filepath"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -270,8 +270,7 @@
 type baseLinker struct {
 	Properties        BaseLinkerProperties
 	dynamicProperties struct {
-		RunPaths   []string `blueprint:"mutated"`
-		BuildStubs bool     `blueprint:"mutated"`
+		BuildStubs bool `blueprint:"mutated"`
 	}
 
 	sanitize *sanitize
@@ -281,13 +280,8 @@
 	linker.Properties.Ldflags = append(linker.Properties.Ldflags, flags...)
 }
 
-// linkerInit initializes dynamic properties of the linker (such as runpath).
+// linkerInit initializes dynamic properties of the linker.
 func (linker *baseLinker) linkerInit(ctx BaseModuleContext) {
-	if ctx.toolchain().Is64Bit() {
-		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib64", "lib64")
-	} else {
-		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib", "lib")
-	}
 }
 
 func (linker *baseLinker) linkerProps() []interface{} {
@@ -529,17 +523,8 @@
 
 	flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
 
-	if ctx.Host() && !ctx.Windows() {
-		rpathPrefix := `\$$ORIGIN/`
-		if ctx.Darwin() {
-			rpathPrefix = "@loader_path/"
-		}
-
-		if !ctx.static() {
-			for _, rpath := range linker.dynamicProperties.RunPaths {
-				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
-			}
-		}
+	if ctx.Host() && !ctx.Windows() && !ctx.static() {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
 	}
 
 	if ctx.useSdk() {
@@ -610,6 +595,45 @@
 	return flags
 }
 
+// RpathFlags returns the rpath linker flags for current target to search the following directories relative
+// to the binary:
+//
+//   - "." to find libraries alongside tests
+//   - "lib[64]" to find libraries in a subdirectory of the binaries' directory
+//   - "../lib[64]" to find libraries when the binaries are in a bin directory
+//   - "../../lib[64]" to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+//     out/host/linux-x86/nativetest/<test dir>/<test>
+//   - "../../../lib[[64] to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+//     out/host/linux-x86/testcases/<test dir>/<CPU>/<test>
+func RpathFlags(ctx android.ModuleContext) []string {
+	key := struct {
+		os   android.OsType
+		arch android.ArchType
+	}{ctx.Target().Os, ctx.Target().Arch.ArchType}
+
+	return ctx.Config().OnceStringSlice(android.NewCustomOnceKey(key), func() []string {
+		rpathPrefix := `\$$ORIGIN/`
+		if key.os == android.Darwin {
+			rpathPrefix = "@loader_path/"
+		}
+
+		var libDir string
+		if key.arch.Multilib == "lib64" {
+			libDir = "lib64"
+		} else {
+			libDir = "lib"
+		}
+
+		return []string{
+			"-Wl,-rpath," + rpathPrefix,
+			"-Wl,-rpath," + rpathPrefix + libDir,
+			"-Wl,-rpath," + rpathPrefix + filepath.Join("..", libDir),
+			"-Wl,-rpath," + rpathPrefix + filepath.Join("../..", libDir),
+			"-Wl,-rpath," + rpathPrefix + filepath.Join("../../..", libDir),
+		}
+	})
+}
+
 func (linker *baseLinker) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 	panic(fmt.Errorf("baseLinker doesn't know how to link"))
diff --git a/cc/makevars.go b/cc/makevars.go
index c70d4a6..6c3f551 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -93,6 +93,7 @@
 
 	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
+	ctx.Strict("GLOBAL_CLANG_CFLAGS_64_NO_OVERRIDE", "${config.NoOverride64GlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
 	ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
 
diff --git a/cc/object.go b/cc/object.go
index 6cb1a30..11ce793 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -142,6 +142,7 @@
 	Absolute_includes   bazel.StringListAttribute
 	Stl                 *string
 	Linker_script       bazel.LabelAttribute
+	Crt                 *bool
 	sdkAttributes
 }
 
@@ -208,6 +209,7 @@
 		Absolute_includes:   compilerAttrs.absoluteIncludes,
 		Stl:                 compilerAttrs.stl,
 		Linker_script:       linkerScript,
+		Crt:                 m.linker.(*objectLinker).Properties.Crt,
 		sdkAttributes:       bp2BuildParseSdkAttributes(m),
 	}
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index af83278..9e62bf8 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -462,6 +462,8 @@
 	}
 
 	h.module.maybeUnhideFromMake()
+
+	h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
 }
 
 func (h *prebuiltLibraryBazelHandler) processStaticBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 8e2d161..66f459a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -76,8 +76,11 @@
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
 	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
-		"export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
+		"export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
 	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
+
+	hostOnlySanitizeFlags   = []string{"-fno-sanitize-recover=all"}
+	deviceOnlySanitizeFlags = []string{"-fsanitize-trap=all", "-ftrap-function=abort"}
 )
 
 type SanitizerType int
@@ -379,7 +382,12 @@
 
 var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)
 
+var exportedVars = android.NewExportedVariables(pctx)
+
 func init() {
+	exportedVars.ExportStringListStaticVariable("HostOnlySanitizeFlags", hostOnlySanitizeFlags)
+	exportedVars.ExportStringList("DeviceOnlySanitizeFlags", deviceOnlySanitizeFlags)
+
 	android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
 	android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
 }
@@ -869,9 +877,9 @@
 			// When fuzzing, we wish to crash with diagnostics on any bug.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
 		} else if ctx.Host() {
-			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover=all")
+			flags.Local.CFlags = append(flags.Local.CFlags, hostOnlySanitizeFlags...)
 		} else {
-			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+			flags.Local.CFlags = append(flags.Local.CFlags, deviceOnlySanitizeFlags...)
 		}
 
 		if enableMinimalRuntime(s) {
@@ -1787,3 +1795,7 @@
 func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
 	hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
 }
+
+func BazelCcSanitizerToolchainVars(config android.Config) string {
+	return android.BazelToolchainVars(config, exportedVars)
+}
diff --git a/cc/test.go b/cc/test.go
index 16ef0ef..5c4d548 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -311,23 +311,6 @@
 	return deps
 }
 
-func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
-	// 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
-	// find out/host/linux-x86/lib[64]/library.so
-	// 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
-	// also find out/host/linux-x86/lib[64]/library.so
-	runpaths := []string{"../../lib", "../../../lib"}
-	for _, runpath := range runpaths {
-		if ctx.toolchain().Is64Bit() {
-			runpath += "64"
-		}
-		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
-	}
-
-	// add "" to rpath so that test binaries can find libraries in their own test directory
-	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
-}
-
 func (test *testDecorator) linkerProps() []interface{} {
 	return []interface{}{&test.LinkerProperties}
 }
@@ -356,11 +339,6 @@
 	return props
 }
 
-func (test *testBinary) linkerInit(ctx BaseModuleContext) {
-	test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
-	test.binaryDecorator.linkerInit(ctx)
-}
-
 func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = test.testDecorator.linkerDeps(ctx, deps)
 	deps = test.binaryDecorator.linkerDeps(ctx, deps)
@@ -535,11 +513,6 @@
 	return append(props, test.libraryDecorator.linkerProps()...)
 }
 
-func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
-	test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
-	test.libraryDecorator.linkerInit(ctx)
-}
-
 func (test *testLibrary) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = test.testDecorator.linkerDeps(ctx, deps)
 	deps = test.libraryDecorator.linkerDeps(ctx, deps)
@@ -610,15 +583,6 @@
 	return true
 }
 
-func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
-	runpath := "../../lib"
-	if ctx.toolchain().Is64Bit() {
-		runpath += "64"
-	}
-	benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
-	benchmark.binaryDecorator.linkerInit(ctx)
-}
-
 func (benchmark *benchmarkDecorator) linkerProps() []interface{} {
 	props := benchmark.binaryDecorator.linkerProps()
 	props = append(props, &benchmark.Properties)
@@ -688,6 +652,8 @@
 	outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 	handler.module.linker.(*testBinary).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+	handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
 }
 
 // binaryAttributes contains Bazel attributes corresponding to a cc test
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 712c7fc..e3d1179 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -359,7 +359,8 @@
 		}
 		for _, file := range inputZip.Entries() {
 			pyPkg := getPackage(file.Name)
-			if filepath.Base(file.Name) == "__init__.py" {
+			baseName := filepath.Base(file.Name)
+			if baseName == "__init__.py" || baseName == "__init__.pyc" {
 				if _, found := initedPackages[pyPkg]; found {
 					panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
 				}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index d8d5e5d..0115f4a 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -321,7 +321,7 @@
 		jobs = runtime.NumCPU() / 4
 
 		ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
-		if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
+		if ramJobs := ramGb / 40; ramGb > 0 && jobs > ramJobs {
 			jobs = ramJobs
 		}
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index baa7380..29a6f95 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -145,8 +145,10 @@
 func runApiBp2build(ctx *android.Context, extraNinjaDeps []string) string {
 	ctx.EventHandler.Begin("api_bp2build")
 	defer ctx.EventHandler.End("api_bp2build")
-	// Do not allow missing dependencies.
-	ctx.SetAllowMissingDependencies(false)
+	// api_bp2build does not run the typical pipeline of soong mutators.
+	// Hoevever, it still runs the defaults mutator which can create dependencies.
+	// These dependencies might not always exist (e.g. in tests)
+	ctx.SetAllowMissingDependencies(ctx.Config().AllowMissingDependencies())
 	ctx.RegisterForApiBazelConversion()
 
 	// Register the Android.bp files in the tree
@@ -176,7 +178,7 @@
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 
 	// Create soong_injection repository
-	soongInjectionFiles := bp2build.CreateSoongInjectionFiles(ctx.Config(), bp2build.CreateCodegenMetrics())
+	soongInjectionFiles := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
 	absoluteSoongInjectionDir := shared.JoinPath(topDir, ctx.Config().SoongOutDir(), bazel.SoongInjectionDirName)
 	for _, file := range soongInjectionFiles {
 		// The API targets in api_bp2build workspace do not have any dependency on api_bp2build.
@@ -189,7 +191,7 @@
 
 	workspace := shared.JoinPath(ctx.Config().SoongOutDir(), "api_bp2build")
 	// Create the symlink forest
-	symlinkDeps := bp2build.PlantSymlinkForest(
+	symlinkDeps, _, _ := bp2build.PlantSymlinkForest(
 		ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE"),
 		topDir,
 		workspace,
@@ -459,8 +461,10 @@
 // symlink tree creation binary. Then the latter would not need to depend on
 // the very heavy-weight machinery of soong_build .
 func runSymlinkForestCreation(ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
+	var ninjaDeps []string
+	var mkdirCount, symlinkCount uint64
+
 	ctx.EventHandler.Do("symlink_forest", func() {
-		var ninjaDeps []string
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 		verbose := ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE")
 
@@ -469,15 +473,16 @@
 		// or file created/deleted under it would trigger an update of the symlink forest.
 		generatedRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "bp2build")
 		workspaceRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "workspace")
+		var symlinkForestDeps []string
 		ctx.EventHandler.Do("plant", func() {
-			symlinkForestDeps := bp2build.PlantSymlinkForest(
+			symlinkForestDeps, mkdirCount, symlinkCount = bp2build.PlantSymlinkForest(
 				verbose, topDir, workspaceRoot, generatedRoot, excludedFromSymlinkForest(ctx, verbose))
-			ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 		})
-
-		writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
-		touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
+		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 	})
+
+	writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
+	touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
 	codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
 	if codegenMetrics == nil {
 		m := bp2build.CreateCodegenMetrics()
@@ -486,6 +491,8 @@
 		//TODO (usta) we cannot determine if we loaded a stale file, i.e. from an unrelated prior
 		//invocation of codegen. We should simply use a separate .pb file
 	}
+	codegenMetrics.SetSymlinkCount(symlinkCount)
+	codegenMetrics.SetMkDirCount(mkdirCount)
 	writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
 	return cmdlineArgs.SymlinkForestMarker
 }
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8c99988..661bd5d 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -199,11 +199,16 @@
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
+	bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
+
+	//the profile file generated by Bazel"
+	bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
 	metricsFiles := []string{
 		buildErrorFile,           // build error strings
 		rbeMetricsFile,           // high level metrics related to remote build execution.
 		bp2buildMetricsFile,      // high level metrics related to bp2build.
 		soongMetricsFile,         // high level metrics related to this build system.
+		bazelMetricsFile,         // high level metrics related to bazel execution
 		config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
 	}
 
@@ -217,7 +222,7 @@
 
 	defer met.Dump(soongMetricsFile)
 	if !config.SkipMetricsUpload() {
-		defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
+		defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
 	}
 
 }
diff --git a/docs/map_files.md b/docs/map_files.md
index 1388059..35e8cbb 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -148,9 +148,24 @@
 
 ### systemapi
 
-This is a synonym of the `apex` tag. It should be used to clarify that the API
-is an API exposed by the system for an APEX, whereas `apex` should be used for
-APIs exposed by an APEX to the platform or another APEX.
+Indicates that the symbol is exposed by the platform for an apex. Whereas `apex`
+should be used for APIs exposed by an APEX to the platform or another APEX.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
+
+Since a single library can be installed ether in platform or an apex, but not
+both, a single map.txt file should not contain _both_ # apex and # systemapi symbols.
+
+The granularity between # apex and # systemapi exists to help the API review
+process (b/191371676). These two symbols have very similar lifetime "in
+practice". A #systemapi symbol can be dropped from the next release if we are
+confident that no one is using it. Similarily, #apex can be dropped if we are
+sure that the old platform which used the symbol has reached EOL and thus is no
+longer accepting new APEX updates. Unlike the APIs for apps where we have zero
+control over how APIs are used, we are in a much more controllable environment
+when talking about #systemapi and #apex symbols. So, we have some flexibility
+here when determining the lifetime of a symbol.
 
 ### var
 
diff --git a/go.mod b/go.mod
index 7239f6d..a5d9dd5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,21 +1,9 @@
 module android/soong
 
+go 1.19
+
 require (
-  google.golang.org/protobuf v0.0.0
-  github.com/google/blueprint v0.0.0
-  prebuilts/bazel/common/proto/analysis_v2 v0.0.0
-  prebuilts/bazel/common/proto/build v0.0.0 // indirect
+	github.com/google/blueprint v0.0.0
+	google.golang.org/protobuf v0.0.0
+	prebuilts/bazel/common/proto/analysis_v2 v0.0.0
 )
-
-replace (
-  google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
-  github.com/google/blueprint v0.0.0 => ../blueprint
-  github.com/google/go-cmp v0.5.5 => ../../external/go-cmp
-  prebuilts/bazel/common/proto/analysis_v2 => ../../prebuilts/bazel/common/proto/analysis_v2
-  prebuilts/bazel/common/proto/build => ../../prebuilts/bazel/common/proto/build
-)
-
-// Indirect deps from golang-protobuf
-exclude github.com/golang/protobuf v1.5.0
-
-go 2.0
diff --git a/go.work b/go.work
new file mode 100644
index 0000000..737a9df
--- /dev/null
+++ b/go.work
@@ -0,0 +1,19 @@
+go 1.19
+
+use (
+	.
+	../../external/go-cmp
+	../../external/golang-protobuf
+	../../prebuilts/bazel/common/proto/analysis_v2
+	../../prebuilts/bazel/common/proto/build
+	../blueprint
+)
+
+replace (
+	github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
+	github.com/google/blueprint v0.0.0 => ../blueprint
+	github.com/google/go-cmp v0.0.0 => ../../external/go-cmp
+	google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
+	prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../prebuilts/bazel/common/proto/analysis_v2
+	prebuilts/bazel/common/proto/build v0.0.0 => ../../prebuilts/bazel/common/proto/build
+)
diff --git a/java/aar.go b/java/aar.go
index f4a2ff2..a483e13 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -651,6 +651,8 @@
 	// Functionality common to Module and Import.
 	embeddableInModuleAndImport
 
+	providesTransitiveHeaderJars
+
 	properties AARImportProperties
 
 	classpathFile         android.WritablePath
@@ -897,8 +899,11 @@
 		a.assetsPackage = mergedAssets
 	}
 
+	a.collectTransitiveHeaderJars(ctx)
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
+		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
+		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
 		ImplementationJars:             android.PathsIfNonNil(a.classpathFile),
 	})
@@ -1031,7 +1036,7 @@
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "aar_import",
-			Bzl_load_location: "@rules_android//rules:rules.bzl",
+			Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 		},
 		android.CommonAttributes{Name: name},
 		&bazelAndroidLibraryImport{
@@ -1041,6 +1046,21 @@
 		},
 	)
 
+	neverlink := true
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "android_library",
+			Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+		},
+		android.CommonAttributes{Name: name + "-neverlink"},
+		&bazelAndroidLibrary{
+			javaLibraryAttributes: &javaLibraryAttributes{
+				Neverlink: bazel.BoolAttribute{Value: &neverlink},
+				Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+			},
+		},
+	)
+
 }
 
 func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@@ -1054,10 +1074,14 @@
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
 	}
 
+	if len(a.properties.Common_srcs) != 0 {
+		commonAttrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, a.properties.Common_srcs))
+	}
+
 	name := a.Name()
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_library",
-		Bzl_load_location: "@rules_android//rules:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(
diff --git a/java/android_manifest.go b/java/android_manifest.go
index c785310..f6457a0 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -149,13 +149,14 @@
 
 	if params.SdkContext != nil {
 		targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
-		args = append(args, "--targetSdkVersion ", targetSdkVersion)
 
 		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
 			targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
 			deps = append(deps, ApiFingerprintPath(ctx))
 		}
 
+		args = append(args, "--targetSdkVersion ", targetSdkVersion)
+
 		minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
diff --git a/java/app.go b/java/app.go
index 3c5760b..4d9c407 100755
--- a/java/app.go
+++ b/java/app.go
@@ -801,6 +801,8 @@
 						unstrippedFile: dep.UnstrippedOutputFile(),
 						partition:      dep.Partition(),
 					})
+				} else if ctx.Config().AllowMissingDependencies() {
+					ctx.AddMissingDependencies([]string{otherName})
 				} else {
 					ctx.ModuleErrorf("dependency %q missing output file", otherName)
 				}
@@ -1310,6 +1312,9 @@
 			ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
 			ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
+	} else {
+		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
+		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
 	}
 }
 
@@ -1476,7 +1481,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_app_certificate",
-		Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -1515,7 +1520,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_binary",
-		Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
 	}
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
 
diff --git a/java/app_import.go b/java/app_import.go
index 8c1e19c..e24e780 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -17,6 +17,7 @@
 // This file contains the module implementations for android_app_import and android_test_import.
 
 import (
+	"github.com/google/blueprint"
 	"reflect"
 
 	"github.com/google/blueprint/proptools"
@@ -31,6 +32,24 @@
 	initAndroidAppImportVariantGroupTypes()
 }
 
+var (
+	uncompressEmbeddedJniLibsRule = pctx.AndroidStaticRule("uncompress-embedded-jni-libs", blueprint.RuleParams{
+		Command: `if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+			`${config.Zip2ZipCmd} -i $in -o $out -0 'lib/**/*.so'` +
+			`; else cp -f $in $out; fi`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Uncompress embedded JNI libs",
+	})
+
+	uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
+		Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+			`${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
+			`; else cp -f $in $out; fi`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Uncompress dex files",
+	})
+)
+
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
 	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
@@ -193,15 +212,12 @@
 		})
 		return
 	}
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
-		BuiltTool("zip2zip").
-		FlagWithInput("-i ", inputPath).
-		FlagWithOutput("-o ", outputPath).
-		FlagWithArg("-0 ", "'lib/**/*.so'").
-		Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
-	rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   uncompressEmbeddedJniLibsRule,
+		Input:  inputPath,
+		Output: outputPath,
+	})
 }
 
 // Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -218,19 +234,6 @@
 	return shouldUncompressDex(ctx, &a.dexpreopter)
 }
 
-func (a *AndroidAppImport) uncompressDex(
-	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
-		BuiltTool("zip2zip").
-		FlagWithInput("-i ", inputPath).
-		FlagWithOutput("-o ", outputPath).
-		FlagWithArg("-0 ", "'classes*.dex'").
-		Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
-	rule.Build("uncompress-dex", "Uncompress dex files")
-}
-
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
 }
@@ -306,7 +309,11 @@
 	a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
 	if a.dexpreopter.uncompressedDex {
 		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
-		a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   uncompressDexRule,
+			Input:  jnisUncompressed,
+			Output: dexUncompressed,
+		})
 		jnisUncompressed = dexUncompressed
 	}
 
diff --git a/java/app_import_test.go b/java/app_import_test.go
index ad27e3a..a29606f 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"reflect"
-	"regexp"
 	"strings"
 	"testing"
 
@@ -294,7 +293,6 @@
 		},
 	}
 
-	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
 	for _, test := range testCases {
 		result := android.GroupFixturePreparers(
 			PrepareForTestWithJavaDefaultModules,
@@ -305,13 +303,9 @@
 		).RunTestWithBp(t, bp)
 
 		variant := result.ModuleForTests("foo", "android_common")
-		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
-		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
-		if len(matches) != 2 {
-			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
-		}
-		if strings.HasSuffix(matches[1], test.expected) {
-			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+		if strings.HasSuffix(input, test.expected) {
+			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
 		}
 
 		provenanceMetaDataRule := variant.Rule("genProvenanceMetaData")
@@ -456,7 +450,6 @@
 		},
 	}
 
-	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
 	for _, test := range testCases {
 		ctx, _ := testJava(t, test.bp)
 
@@ -469,13 +462,9 @@
 			android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
 			continue
 		}
-		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
-		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
-		if len(matches) != 2 {
-			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
-		}
-		if strings.HasSuffix(matches[1], test.expected) {
-			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+		if strings.HasSuffix(input, test.expected) {
+			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
 		}
 		rule := variant.Rule("genProvenanceMetaData")
 		android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
@@ -686,8 +675,8 @@
 		`)
 
 	variant := ctx.ModuleForTests("foo", "android_common")
-	jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
-	if !strings.HasPrefix(jniRule, "if (zipinfo") {
+	jniRule := variant.Output("jnis-uncompressed/foo.apk").BuildParams.Rule.String()
+	if jniRule == android.Cp.String() {
 		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
 	}
 
diff --git a/java/app_test.go b/java/app_test.go
index cd88864..3fb67c1 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -3163,6 +3163,7 @@
 				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_sdk_codename = &platform_sdk_codename
 				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+				variables.Unbundled_build = proptools.BoolPtr(true)
 				variables.Unbundled_build_apps = []string{"sampleModule"}
 			}),
 		)
@@ -3241,6 +3242,7 @@
 				variables.Platform_sdk_final = &testCase.platform_sdk_final
 				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_sdk_codename = &platform_sdk_codename
+				variables.Unbundled_build = proptools.BoolPtr(true)
 				variables.Unbundled_build_apps = []string{"sampleModule"}
 			}),
 		)
@@ -3311,6 +3313,7 @@
 				variables.Platform_sdk_final = &testCase.platform_sdk_final
 				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_sdk_codename = &platform_sdk_codename
+				variables.Unbundled_build = proptools.BoolPtr(true)
 				variables.Unbundled_build_apps = []string{"sampleModule"}
 			}),
 		)
diff --git a/java/base.go b/java/base.go
index 84fda37..cce06a4 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1583,6 +1583,8 @@
 
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
+		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
+		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
 		ImplementationJars:             android.PathsIfNonNil(j.implementationJarFile),
 		ResourceJars:                   android.PathsIfNonNil(j.resourceJar),
@@ -1719,6 +1721,52 @@
 	return instrumentedJar
 }
 
+type providesTransitiveHeaderJars struct {
+	// set of header jars for all transitive libs deps
+	transitiveLibsHeaderJars *android.DepSet
+	// set of header jars for all transitive static libs deps
+	transitiveStaticLibsHeaderJars *android.DepSet
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet {
+	return j.transitiveLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet {
+	return j.transitiveStaticLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) {
+	directLibs := android.Paths{}
+	directStaticLibs := android.Paths{}
+	transitiveLibs := []*android.DepSet{}
+	transitiveStaticLibs := []*android.DepSet{}
+	ctx.VisitDirectDeps(func(module android.Module) {
+		// don't add deps of the prebuilt version of the same library
+		if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
+			return
+		}
+
+		dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		if dep.TransitiveLibsHeaderJars != nil {
+			transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
+		}
+		if dep.TransitiveStaticLibsHeaderJars != nil {
+			transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+		}
+
+		tag := ctx.OtherModuleDependencyTag(module)
+		_, isUsesLibDep := tag.(usesLibraryDependencyTag)
+		if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
+			directLibs = append(directLibs, dep.HeaderJars...)
+		} else if tag == staticLibTag {
+			directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
+		}
+	})
+	j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
+	j.transitiveStaticLibsHeaderJars = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
+}
+
 func (j *Module) HeaderJars() android.Paths {
 	if j.headerJarFile == nil {
 		return nil
@@ -1947,6 +1995,7 @@
 
 	sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
 
+	j.collectTransitiveHeaderJars(ctx)
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
diff --git a/java/config/config.go b/java/config/config.go
index 49d88c4..7c22076 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -96,8 +96,6 @@
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
 		"-JXmx2048M",
-		// Disable this optimization as it can impact weak reference semantics. See b/233432839.
-		"-JDcom.android.tools.r8.disableEnqueuerDeferredTracing=true",
 	}, dexerJavaVmFlagsList...))
 
 	exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 4fb1d76..b9332dd 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -63,11 +63,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
     sdk_version: "none",
     system_modules: "none",
@@ -148,11 +143,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
     sdk_version: "none",
     system_modules: "none",
@@ -278,11 +268,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
 }
 
@@ -294,11 +279,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
     ],
 }
 
@@ -322,11 +302,6 @@
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
-        // This one is not on device but it's needed when javac compiles code
-        // containing @Generated annotations produced by some code generation
-        // tools.
-        // See http://b/123891440.
-        "core-generated-annotation-stubs",
 
         // Ensure that core libraries that depend on the public API can access
         // the UnsupportedAppUsage, CorePlatformApi and IntraCoreApi
diff --git a/java/dex.go b/java/dex.go
index 40ee99d..a8dd375 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -89,7 +89,10 @@
 	// list of extra proguard flag files
 	extraProguardFlagFiles android.Paths
 	proguardDictionary     android.OptionalPath
+	proguardConfiguration  android.OptionalPath
 	proguardUsageZip       android.OptionalPath
+
+	providesTransitiveHeaderJars
 }
 
 func (d *dexer) effectiveOptimizeEnabled() bool {
@@ -130,17 +133,18 @@
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
+			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
 			`mkdir -p $$(dirname ${outUsage}) && ` +
 			`mkdir -p $$(dirname $tmpJar) && ` +
 			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
 			`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
+			`--pg-conf-output ${outConfig} ` +
 			`-printusage ${outUsage} ` +
 			`--deps-file ${out}.d ` +
 			`$r8Flags && ` +
-			`touch "${outDict}" "${outUsage}" && ` +
+			`touch "${outDict}" "${outConfig}" "${outUsage}" && ` +
 			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
@@ -176,7 +180,7 @@
 			ExecStrategy: "${config.RER8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
+	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
 		"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
@@ -249,13 +253,32 @@
 	})
 
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
-	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
-	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
-
 	r8Deps = append(r8Deps, proguardRaiseDeps...)
+	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.bootClasspath...)
+	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.dexClasspath...)
 
+	transitiveStaticLibsLookupMap := map[android.Path]bool{}
+	if d.transitiveStaticLibsHeaderJars != nil {
+		for _, jar := range d.transitiveStaticLibsHeaderJars.ToList() {
+			transitiveStaticLibsLookupMap[jar] = true
+		}
+	}
+	transitiveHeaderJars := android.Paths{}
+	if d.transitiveLibsHeaderJars != nil {
+		for _, jar := range d.transitiveLibsHeaderJars.ToList() {
+			if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
+				// don't include a lib if it is already packaged in the current JAR as a static lib
+				continue
+			}
+			transitiveHeaderJars = append(transitiveHeaderJars, jar)
+		}
+	}
+	transitiveClasspath := classpath(transitiveHeaderJars)
+	r8Flags = append(r8Flags, transitiveClasspath.FormJavaClassPath("-libraryjars"))
+	r8Deps = append(r8Deps, transitiveClasspath...)
+
 	flagFiles := android.Paths{
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
@@ -342,6 +365,8 @@
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+		proguardConfiguration := android.PathForModuleOut(ctx, "proguard_configuration")
+		d.proguardConfiguration = android.OptionalPathForPath(proguardConfiguration)
 		proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
 		proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
 			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
@@ -354,6 +379,7 @@
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
 			"zipFlags":       zipFlags,
 			"outDict":        proguardDictionary.String(),
+			"outConfig":      proguardConfiguration.String(),
 			"outUsageDir":    proguardUsageDir.String(),
 			"outUsage":       proguardUsage.String(),
 			"outUsageZip":    proguardUsageZip.String(),
diff --git a/java/dex_test.go b/java/dex_test.go
index fc6cd0f..dc85f9e 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -18,6 +18,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func TestR8(t *testing.T) {
@@ -74,7 +76,7 @@
 
 	android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], libHeader.String())
-	android.AssertStringDoesNotContain(t, "expected no  static_lib header jar in app javac classpath",
+	android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], staticLibHeader.String())
 	android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
 		appR8.Args["r8Flags"], "-ignorewarnings")
@@ -86,6 +88,174 @@
 		corePlatformAppR8.Args["r8Flags"], "--android-platform-build")
 }
 
+func TestR8TransitiveDeps(t *testing.T) {
+	bp := `
+		override_android_app {
+			name: "override_app",
+			base: "app",
+		}
+
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			libs: [
+				"lib",
+				"uses_libs_dep_import",
+			],
+			static_libs: [
+				"static_lib",
+				"repeated_dep",
+			],
+			platform_apis: true,
+		}
+
+		java_library {
+			name: "static_lib",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "lib",
+			libs: [
+				"transitive_lib",
+				"repeated_dep",
+				"prebuilt_lib",
+			],
+			static_libs: ["transitive_static_lib"],
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "repeated_dep",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "transitive_static_lib",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "transitive_lib",
+			srcs: ["foo.java"],
+			libs: ["transitive_lib_2"],
+		}
+
+		java_library {
+			name: "transitive_lib_2",
+			srcs: ["foo.java"],
+		}
+
+		java_import {
+			name: "lib",
+			jars: ["lib.jar"],
+		}
+
+		java_library {
+			name: "uses_lib",
+			srcs: ["foo.java"],
+		}
+
+		java_library {
+			name: "optional_uses_lib",
+			srcs: ["foo.java"],
+		}
+
+		android_library {
+			name: "uses_libs_dep",
+			uses_libs: ["uses_lib"],
+			optional_uses_libs: ["optional_uses_lib"],
+		}
+
+		android_library_import {
+			name: "uses_libs_dep_import",
+			aars: ["aar.aar"],
+			static_libs: ["uses_libs_dep"],
+		}
+	`
+
+	testcases := []struct {
+		name      string
+		unbundled bool
+	}{
+		{
+			name:      "non-unbundled build",
+			unbundled: false,
+		},
+		{
+			name:      "unbundled build",
+			unbundled: true,
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixturePreparer := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd
+			if tc.unbundled {
+				fixturePreparer = android.GroupFixturePreparers(
+					fixturePreparer,
+					android.FixtureModifyProductVariables(
+						func(variables android.FixtureProductVariables) {
+							variables.Unbundled_build = proptools.BoolPtr(true)
+						},
+					),
+				)
+			}
+			result := fixturePreparer.RunTestWithBp(t, bp)
+
+			getHeaderJar := func(name string) android.Path {
+				mod := result.ModuleForTests(name, "android_common")
+				return mod.Output("turbine-combined/" + name + ".jar").Output
+			}
+
+			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+			overrideAppR8 := result.ModuleForTests("app", "android_common_override_app").Rule("r8")
+			appHeader := getHeaderJar("app")
+			overrideAppHeader := result.ModuleForTests("app", "android_common_override_app").Output("turbine-combined/app.jar").Output
+			libHeader := getHeaderJar("lib")
+			transitiveLibHeader := getHeaderJar("transitive_lib")
+			transitiveLib2Header := getHeaderJar("transitive_lib_2")
+			staticLibHeader := getHeaderJar("static_lib")
+			transitiveStaticLibHeader := getHeaderJar("transitive_static_lib")
+			repeatedDepHeader := getHeaderJar("repeated_dep")
+			usesLibHeader := getHeaderJar("uses_lib")
+			optionalUsesLibHeader := getHeaderJar("optional_uses_lib")
+			prebuiltLibHeader := result.ModuleForTests("prebuilt_lib", "android_common").Output("combined/lib.jar").Output
+
+			for _, rule := range []android.TestingBuildParams{appR8, overrideAppR8} {
+				android.AssertStringDoesNotContain(t, "expected no app header jar in app r8 classpath",
+					rule.Args["r8Flags"], appHeader.String())
+				android.AssertStringDoesNotContain(t, "expected no override_app header jar in app r8 classpath",
+					rule.Args["r8Flags"], overrideAppHeader.String())
+				android.AssertStringDoesContain(t, "expected transitive lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], transitiveLibHeader.String())
+				android.AssertStringDoesContain(t, "expected transitive lib ^2 header jar in app r8 classpath",
+					rule.Args["r8Flags"], transitiveLib2Header.String())
+				android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], libHeader.String())
+				android.AssertStringDoesContain(t, "expected uses_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], usesLibHeader.String())
+				android.AssertStringDoesContain(t, "expected optional_uses_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], optionalUsesLibHeader.String())
+				android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], staticLibHeader.String())
+				android.AssertStringDoesNotContain(t, "expected no transitive static_lib header jar in app r8 classpath",
+					rule.Args["r8Flags"], transitiveStaticLibHeader.String())
+				// we shouldn't list this dep because it is already included as static_libs in the app
+				android.AssertStringDoesNotContain(t, "expected no repeated_dep header jar in app r8 classpath",
+					rule.Args["r8Flags"], repeatedDepHeader.String())
+				// skip a prebuilt transitive dep if the source is also a transitive dep
+				android.AssertStringDoesNotContain(t, "expected no prebuilt header jar in app r8 classpath",
+					rule.Args["r8Flags"], prebuiltLibHeader.String())
+				android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
+					rule.Args["r8Flags"], "-ignorewarnings")
+				android.AssertStringDoesContain(t, "expected --android-platform-build in app r8 flags",
+					rule.Args["r8Flags"], "--android-platform-build")
+			}
+		})
+	}
+}
+
 func TestR8Flags(t *testing.T) {
 	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
 		android_app {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index aa55f37..01a2c14 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -602,8 +602,15 @@
 		Flag("-J-Xmx1600m").
 		Flag("-J-XX:-OmitStackTraceInFastThrow").
 		Flag("-XDignore.symbol.file").
-		FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+		Flag("--ignore-source-errors").
+		// b/240421555: use a stub doclet until Doclava works with JDK 17
+		//FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+		FlagWithArg("-doclet ", "com.google.stubdoclet.StubDoclet").
 		FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+		FlagWithArg("-Xmaxerrs ", "1").
+		FlagWithArg("-Xmaxwarns ", "1").
+		Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
 		FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
 		FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
 
@@ -687,7 +694,7 @@
 	outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
 
 	cmd := rule.Command().
-		BuiltTool("soong_javac_wrapper").Tool(android.PathForSource(ctx, "prebuilts/jdk/jdk11/linux-x86/bin/javadoc")).
+		BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
 		Flag(config.JavacVmFlags).
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
@@ -773,6 +780,8 @@
 
 	jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar")
 	doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar")
+	// b/240421555: use a stub doclet until Doclava works with JDK 17
+	stubdoclet := ctx.Config().HostJavaToolPath(ctx, "stubdoclet.jar")
 
 	outDir := android.PathForModuleOut(ctx, "out")
 	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
@@ -800,7 +809,8 @@
 	if Bool(d.properties.Dokka_enabled) {
 		desc = "dokka"
 	} else {
-		d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
+		// b/240421555: use a stub doclet until Doclava works with JDK 17
+		d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava, stubdoclet})
 
 		for _, o := range d.Javadoc.properties.Out {
 			cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
@@ -818,9 +828,9 @@
 		FlagWithArg("-C ", outDir.String()).
 		FlagWithArg("-D ", outDir.String())
 
-	rule.Restat()
+	// rule.Restat()
 
-	zipSyncCleanupCmd(rule, srcJarDir)
+	// zipSyncCleanupCmd(rule, srcJarDir)
 
 	rule.Build("javadoc", desc)
 }
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 4bbe70a..d9613e5 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -148,6 +148,10 @@
 	// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
 	// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
 	Extensions_info_file *string `android:"path"`
+
+	// API surface of this module. If set, the module contributes to an API surface.
+	// For the full list of available API surfaces, refer to soong/android/sdk_version.go
+	Api_surface *string
 }
 
 // Used by xsd_config
@@ -178,6 +182,10 @@
 		&module.Javadoc.properties)
 
 	InitDroiddocModule(module, android.HostAndDeviceSupported)
+
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+		module.createApiContribution(ctx)
+	})
 	return module
 }
 
@@ -862,6 +870,23 @@
 	}, attrs)
 }
 
+func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
+	api_file := d.properties.Check_api.Current.Api_file
+	api_surface := d.properties.Api_surface
+
+	props := struct {
+		Name        *string
+		Api_surface *string
+		Api_file    *string
+	}{}
+
+	props.Name = proptools.StringPtr(d.Name() + ".api.contribution")
+	props.Api_surface = api_surface
+	props.Api_file = api_file
+
+	ctx.CreateModule(ApiContributionFactory, &props)
+}
+
 // TODO (b/262014796): Export the API contributions of CorePlatformApi
 // A map to populate the api surface of a droidstub from a substring appearing in its name
 // This map assumes that droidstubs (either checked-in or created by java_sdk_library)
@@ -876,6 +901,7 @@
 		"system":        android.SdkSystem,
 		"module_lib":    android.SdkModule,
 		"module-lib":    android.SdkModule,
+		"platform.api":  android.SdkCorePlatform,
 		"test":          android.SdkTest,
 		"toolchain":     android.SdkToolchain,
 	}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index ef2e6dc..6c22937 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -346,3 +346,27 @@
 		android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
 	}
 }
+
+func TestDroidStubsApiContributionGeneration(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droidstubs {
+			name: "foo",
+			srcs: ["A/a.java"],
+			api_surface: "public",
+			check_api: {
+				current: {
+					api_file: "A/current.txt",
+					removed_api_file: "A/removed.txt",
+				}
+			}
+		}
+		`,
+		map[string][]byte{
+			"A/a.java":      nil,
+			"A/current.txt": nil,
+			"A/removed.txt": nil,
+		},
+	)
+
+	ctx.ModuleForTests("foo.api.contribution", "")
+}
diff --git a/java/java.go b/java/java.go
index 3b0ad8d..659f98a 100644
--- a/java/java.go
+++ b/java/java.go
@@ -230,6 +230,12 @@
 	// against this module.  If empty, ImplementationJars should be used instead.
 	HeaderJars android.Paths
 
+	// set of header jars for all transitive libs deps
+	TransitiveLibsHeaderJars *android.DepSet
+
+	// set of header jars for all transitive static libs deps
+	TransitiveStaticLibsHeaderJars *android.DepSet
+
 	// ImplementationAndResourceJars is a list of jars that contain the implementations of classes
 	// in the module as well as any resources included in the module.
 	ImplementationAndResourcesJars android.Paths
@@ -380,6 +386,7 @@
 	instrumentationForTag   = dependencyTag{name: "instrumentation_for"}
 	extraLintCheckTag       = dependencyTag{name: "extra-lint-check", toolchain: true}
 	jniLibTag               = dependencyTag{name: "jnilib", runtimeLinked: true}
+	r8LibraryJarTag         = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	jniInstallTag           = installDependencyTag{name: "jni install"}
 	binaryInstallTag        = installDependencyTag{name: "binary install"}
@@ -1587,7 +1594,11 @@
 var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
 
 func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file))
+	var apiFile android.Path = nil
+	if apiFileString := ap.properties.Api_file; apiFileString != nil {
+		apiFile = android.PathForModuleSrc(ctx, String(apiFileString))
+	}
+
 	ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
 		ApiFile: apiFile,
 	})
@@ -1618,13 +1629,17 @@
 
 	// List of flags to be passed to the javac compiler to generate jar file
 	Javacflags []string
+
+	// List of shared java libs that this module has dependencies to and
+	// should be passed as classpath in javac invocation
+	Libs []string
 }
 
 func ApiLibraryFactory() android.Module {
 	module := &ApiLibrary{}
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	android.InitDefaultableModule(module)
 	module.AddProperties(&module.properties)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -1689,6 +1704,7 @@
 	for _, apiContributionName := range apiContributions {
 		ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
 	}
+	ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
 }
 
 func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1706,10 +1722,22 @@
 
 	homeDir := android.PathForModuleOut(ctx, "metalava", "home")
 
-	var srcFiles []android.Path
-	ctx.VisitDirectDepsWithTag(javaApiContributionTag, func(dep android.Module) {
-		provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
-		srcFiles = append(srcFiles, android.PathForSource(ctx, provider.ApiFile.String()))
+	var srcFiles android.Paths
+	var classPaths android.Paths
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		switch tag {
+		case javaApiContributionTag:
+			provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
+			providerApiFile := provider.ApiFile
+			if providerApiFile == nil {
+				ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
+			}
+			srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
+		case libTag:
+			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			classPaths = append(classPaths, provider.HeaderJars...)
+		}
 	})
 
 	// Add the api_files inputs
@@ -1739,11 +1767,16 @@
 	var flags javaBuilderFlags
 	flags.javaVersion = getStubsJavaVersion()
 	flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
+	flags.classpath = classpath(classPaths)
 
 	TransformJavaToClasses(ctx, al.stubsJar, 0, android.Paths{},
 		android.Paths{al.stubsSrcJar}, flags, android.Paths{})
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
+
+	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+		HeaderJars: android.PathsIfNonNil(al.stubsJar),
+	})
 }
 
 //
@@ -1926,9 +1959,9 @@
 
 	var flags javaBuilderFlags
 
+	j.collectTransitiveHeaderJars(ctx)
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
-
 		if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
 			dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
 			switch tag {
@@ -2018,6 +2051,8 @@
 
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.combinedClasspathFile),
+		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
+		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
 		ImplementationJars:             android.PathsIfNonNil(j.combinedClasspathFile),
 		AidlIncludeDirs:                j.exportAidlIncludeDirs,
@@ -2402,6 +2437,7 @@
 		&RuntimeResourceOverlayProperties{},
 		&LintProperties{},
 		&appTestHelperAppProperties{},
+		&JavaApiLibraryProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -2526,9 +2562,10 @@
 
 type javaCommonAttributes struct {
 	*javaResourcesAttributes
-	Srcs      bazel.LabelListAttribute
-	Plugins   bazel.LabelListAttribute
-	Javacopts bazel.StringListAttribute
+	Srcs        bazel.LabelListAttribute
+	Plugins     bazel.LabelListAttribute
+	Javacopts   bazel.StringListAttribute
+	Common_srcs bazel.LabelListAttribute
 }
 
 type javaDependencyLabels struct {
@@ -2566,7 +2603,7 @@
 // to be returned to the calling function.
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
 	var srcs bazel.LabelListAttribute
-	var deps bazel.LabelList
+	var deps bazel.LabelListAttribute
 	var staticDeps bazel.LabelList
 
 	archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
@@ -2672,18 +2709,17 @@
 		Javacopts: bazel.MakeStringListAttribute(javacopts),
 	}
 
-	if m.properties.Libs != nil {
-
-		// TODO 244210934 ALIX Check if this else statement breaks presubmits get rid of it if it doesn't
-		if strings.HasPrefix(ctx.ModuleType(), "java_binary") || strings.HasPrefix(ctx.ModuleType(), "java_library") || ctx.ModuleType() == "android_library" {
-			for _, d := range m.properties.Libs {
-				neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
-				neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
-				deps.Add(&neverlinkLabel)
+	for axis, configToProps := range archVariantProps {
+		for config, _props := range configToProps {
+			if archProps, ok := _props.(*CommonProperties); ok {
+				var libLabels []bazel.Label
+				for _, d := range archProps.Libs {
+					neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
+					neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
+					libLabels = append(libLabels, neverlinkLabel)
+				}
+				deps.SetSelectValue(axis, config, bazel.MakeLabelList(libLabels))
 			}
-
-		} else {
-			deps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Libs))))
 		}
 	}
 
@@ -2701,7 +2737,7 @@
 	staticDeps.Add(protoDepLabel)
 
 	depLabels := &javaDependencyLabels{}
-	depLabels.Deps = bazel.MakeLabelListAttribute(deps)
+	depLabels.Deps = deps
 	depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
 
 	bp2BuildInfo := &bp2BuildJavaInfo{
@@ -2714,10 +2750,9 @@
 
 type javaLibraryAttributes struct {
 	*javaCommonAttributes
-	Deps        bazel.LabelListAttribute
-	Exports     bazel.LabelListAttribute
-	Neverlink   bazel.BoolAttribute
-	Common_srcs bazel.LabelListAttribute
+	Deps      bazel.LabelListAttribute
+	Exports   bazel.LabelListAttribute
+	Neverlink bazel.BoolAttribute
 }
 
 func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
@@ -2751,7 +2786,7 @@
 			Bzl_load_location: "//build/bazel/rules/java:library.bzl",
 		}
 	} else {
-		attrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+		attrs.javaCommonAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
 
 		props = bazel.BazelTargetModuleProperties{
 			Rule_class:        "kt_jvm_library",
@@ -2807,14 +2842,8 @@
 		mainClass = mainClassInManifest
 	}
 
-	attrs := &javaBinaryHostAttributes{
-		javaCommonAttributes: commonAttrs,
-		Deps:                 deps,
-		Runtime_deps:         runtimeDeps,
-		Main_class:           mainClass,
-	}
-
 	// Attribute jvm_flags
+	var jvmFlags bazel.StringListAttribute
 	if m.binaryProperties.Jni_libs != nil {
 		jniLibPackages := map[string]bool{}
 		for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
@@ -2837,12 +2866,56 @@
 			// See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH
 			jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage)
 		}
-		attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
+		jvmFlags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
 	}
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class: "java_binary",
 	}
+	attrs := &javaBinaryHostAttributes{
+		Runtime_deps: runtimeDeps,
+		Main_class:   mainClass,
+		Jvm_flags:    jvmFlags,
+	}
+
+	if !bp2BuildInfo.hasKotlinSrcs && len(m.properties.Common_srcs) == 0 {
+		attrs.javaCommonAttributes = commonAttrs
+		attrs.Deps = deps
+	} else {
+		ktName := m.Name() + "_kt"
+		ktProps := bazel.BazelTargetModuleProperties{
+			Rule_class:        "kt_jvm_library",
+			Bzl_load_location: "@rules_kotlin//kotlin:jvm_library.bzl",
+		}
+		ktAttrs := &javaLibraryAttributes{
+			Deps: deps,
+			javaCommonAttributes: &javaCommonAttributes{
+				Srcs:      commonAttrs.Srcs,
+				Plugins:   commonAttrs.Plugins,
+				Javacopts: commonAttrs.Javacopts,
+			},
+		}
+
+		if len(m.properties.Common_srcs) != 0 {
+			ktAttrs.javaCommonAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+		}
+
+		// kt_jvm_library does not support resource_strip_prefix, if this attribute
+		// is set, than javaResourcesAttributes needs to be set in the
+		// javaCommonAttributes of the java_binary target
+		if commonAttrs.javaResourcesAttributes != nil {
+			if commonAttrs.javaResourcesAttributes.Resource_strip_prefix != nil {
+				attrs.javaCommonAttributes = &javaCommonAttributes{
+					javaResourcesAttributes: commonAttrs.javaResourcesAttributes,
+				}
+			} else {
+				ktAttrs.javaCommonAttributes.javaResourcesAttributes = commonAttrs.javaResourcesAttributes
+			}
+		}
+
+		ctx.CreateBazelTargetModule(ktProps, android.CommonAttributes{Name: ktName}, ktAttrs)
+		attrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + ktName}})
+	}
 
 	// Create the BazelTargetModule.
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/java/java_test.go b/java/java_test.go
index 085f627..21993ec 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -30,7 +30,6 @@
 	"android/soong/cc"
 	"android/soong/dexpreopt"
 	"android/soong/genrule"
-	"android/soong/python"
 )
 
 // Legacy preparer used for running tests within the java package.
@@ -49,7 +48,6 @@
 	// Include all the default java modules.
 	PrepareForTestWithJavaDefaultModules,
 	PrepareForTestWithOverlayBuildComponents,
-	python.PrepareForTestWithPythonBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
 	}),
@@ -1440,24 +1438,26 @@
 }
 
 func TestDataNativeBinaries(t *testing.T) {
-	ctx, _ := testJava(t, `
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
 		java_test_host {
 			name: "foo",
 			srcs: ["a.java"],
 			data_native_bins: ["bin"]
 		}
 
-		python_binary_host {
+		cc_binary_host {
 			name: "bin",
-			srcs: ["bin.py"],
+			srcs: ["bin.cpp"],
 		}
-	`)
+	`).TestContext
 
 	buildOS := ctx.Config().BuildOS.String()
 
 	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
-	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
 	actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
 }
@@ -1840,6 +1840,20 @@
 		}`)
 }
 
+func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+	testJavaError(t,
+		"Error: foo has an empty api file.",
+		`java_api_contribution {
+			name: "foo",
+		}
+		java_api_library {
+			name: "bar",
+			api_surface: "public",
+			api_contributions: ["foo"],
+		}
+	`)
+}
+
 func TestJavaApiLibraryAndProviderLink(t *testing.T) {
 	provider_bp_a := `
 	java_api_contribution {
@@ -1894,6 +1908,98 @@
 	}
 }
 
+func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `
+	java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	provider_bp_c := `
+	java_api_contribution {
+		name: "foo3",
+		api_file: "foo3.txt",
+	}
+	`
+	provider_bp_d := `
+	java_api_contribution {
+		name: "foo4",
+		api_file: "foo4.txt",
+	}
+	`
+	ctx, _ := testJavaWithFS(t, `
+		java_defaults {
+			name: "baz1",
+			api_surface: "public",
+			api_contributions: ["foo1", "foo2"],
+		}
+
+		java_defaults {
+			name: "baz2",
+			api_surface: "system",
+			api_contributions: ["foo3"],
+		}
+
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_contributions: ["foo1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "public",
+			defaults:["baz1"],
+		}
+
+		java_api_library {
+			name: "bar3",
+			api_surface: "system",
+			defaults:["baz1", "baz2"],
+			api_contributions: ["foo4"],
+			api_files: ["api1/current.txt", "api2/current.txt"]
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+			"c/Android.bp": []byte(provider_bp_c),
+			"d/Android.bp": []byte(provider_bp_d),
+		})
+
+	testcases := []struct {
+		moduleName         string
+		sourceTextFileDirs []string
+	}{
+		{
+			moduleName:         "bar1",
+			sourceTextFileDirs: []string{"a/foo1.txt"},
+		},
+		{
+			moduleName:         "bar2",
+			sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt"},
+		},
+		{
+			moduleName:         "bar3",
+			sourceTextFileDirs: []string{"c/foo3.txt", "a/foo1.txt", "b/foo2.txt", "d/foo4.txt", "api1/current.txt", "api2/current.txt"},
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		manifest := m.Output("metalava.sbox.textproto")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+		manifestCommand := sboxProto.Commands[0].GetCommand()
+		sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
+		android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
+	}
+}
+
 func TestJavaApiLibraryJarGeneration(t *testing.T) {
 	provider_bp_a := `
 	java_api_contribution {
@@ -1901,7 +2007,8 @@
 		api_file: "foo1.txt",
 	}
 	`
-	provider_bp_b := `java_api_contribution {
+	provider_bp_b := `
+	java_api_contribution {
 		name: "foo2",
 		api_file: "foo2.txt",
 	}
@@ -1946,6 +2053,81 @@
 	}
 }
 
+func TestJavaApiLibraryLibsLink(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `
+	java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	lib_bp_a := `
+	java_library {
+		name: "lib1",
+		srcs: ["Lib.java"],
+	}
+	`
+	lib_bp_b := `
+	java_library {
+		name: "lib2",
+		srcs: ["Lib.java"],
+	}
+	`
+
+	ctx, _ := testJavaWithFS(t, `
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_contributions: ["foo1"],
+			libs: ["lib1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "system",
+			api_contributions: ["foo1", "foo2"],
+			libs: ["lib1", "lib2", "bar1"],
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+			"c/Android.bp": []byte(lib_bp_a),
+			"c/Lib.java":   {},
+			"d/Android.bp": []byte(lib_bp_b),
+			"d/Lib.java":   {},
+		})
+
+	testcases := []struct {
+		moduleName        string
+		classPathJarNames []string
+	}{
+		{
+			moduleName:        "bar1",
+			classPathJarNames: []string{"lib1.jar"},
+		},
+		{
+			moduleName:        "bar2",
+			classPathJarNames: []string{"lib1.jar", "lib2.jar", "bar1/android.jar"},
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		javacRules := m.Rule("javac")
+		classPathArgs := javacRules.Args["classpath"]
+		for _, jarName := range c.classPathJarNames {
+			if !strings.Contains(classPathArgs, jarName) {
+				t.Errorf("Module output does not contain expected jar %s", jarName)
+			}
+		}
+	}
+}
+
 func TestTradefedOptions(t *testing.T) {
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 491ce29..933fc51 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -44,6 +44,10 @@
 
 	kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
 		Output("turbine-combined/kotlin-stdlib.jar").Output
+	kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
+		Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
+	kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
+		Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
 	kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
 		Output("turbine-combined/kotlin-annotations.jar").Output
 
@@ -79,6 +83,16 @@
 			fooJar.Inputs.Strings(), kotlinStdlib.String())
 	}
 
+	if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
+		t.Errorf("foo jar inputs %v does not contain %v",
+			fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
+	}
+
+	if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
+		t.Errorf("foo jar inputs %v does not contain %v",
+			fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
+	}
+
 	if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
 		t.Errorf("foo jar inputs %v does not contain %v",
 			fooJar.Inputs.Strings(), kotlinAnnotations.String())
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 3b64bf7..b872365 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1599,6 +1599,7 @@
 		Srcs                             []string
 		Installable                      *bool
 		Sdk_version                      *string
+		Api_surface                      *string
 		System_modules                   *string
 		Libs                             []string
 		Output_javadoc_comments          *bool
@@ -1638,6 +1639,7 @@
 	props.Srcs = append(props.Srcs, module.properties.Srcs...)
 	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
 	props.Sdk_version = module.deviceProperties.Sdk_version
+	props.Api_surface = &apiScope.name
 	props.System_modules = module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
index f8c930a..5f55f6a 100644
--- a/mk2rbc/android_products_test.go
+++ b/mk2rbc/android_products_test.go
@@ -29,7 +29,6 @@
 	}
 	expectedProducts := map[string]string{
 		"aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
-		"aosp_tv_arm":    abspath("aosp_tv_arm.mk"),
 		"aosp_tv_arm64":  abspath("aosp_tv_arm64.mk"),
 	}
 	if !reflect.DeepEqual(actualProducts, expectedProducts) {
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
index a2220ed..400ec35 100644
--- a/mk2rbc/test/android_products.mk.test
+++ b/mk2rbc/test/android_products.mk.test
@@ -1,4 +1,3 @@
 PRODUCT_MAKEFILES := \
-    $(LOCAL_DIR)/aosp_tv_arm.mk \
     $(LOCAL_DIR)/aosp_tv_arm64.mk \
     aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file
diff --git a/python/Android.bp b/python/Android.bp
index e49fa6a..7578673 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -9,13 +9,13 @@
         "blueprint",
         "soong-android",
         "soong-tradefed",
+        "soong-cc",
     ],
     srcs: [
-        "androidmk.go",
         "binary.go",
+        "bp2build.go",
         "builder.go",
         "defaults.go",
-        "installer.go",
         "library.go",
         "proto.go",
         "python.go",
diff --git a/python/androidmk.go b/python/androidmk.go
deleted file mode 100644
index 7dc4713..0000000
--- a/python/androidmk.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 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 python
-
-import (
-	"path/filepath"
-	"strings"
-
-	"android/soong/android"
-)
-
-type subAndroidMkProvider interface {
-	AndroidMk(*Module, *android.AndroidMkEntries)
-}
-
-func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
-	if p.subAndroidMkOnce == nil {
-		p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
-	}
-	if androidmk, ok := obj.(subAndroidMkProvider); ok {
-		if !p.subAndroidMkOnce[androidmk] {
-			p.subAndroidMkOnce[androidmk] = true
-			androidmk.AndroidMk(p, entries)
-		}
-	}
-}
-
-func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
-	entries := android.AndroidMkEntries{OutputFile: p.installSource}
-
-	p.subAndroidMk(&entries, p.installer)
-
-	return []android.AndroidMkEntries{entries}
-}
-
-func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
-	entries.Class = "EXECUTABLES"
-
-	entries.ExtraEntries = append(entries.ExtraEntries,
-		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
-		})
-	base.subAndroidMk(entries, p.pythonInstaller)
-}
-
-func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
-	entries.Class = "NATIVE_TESTS"
-
-	entries.ExtraEntries = append(entries.ExtraEntries,
-		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
-			if p.testConfig != nil {
-				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
-			}
-
-			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-
-			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-
-			p.testProperties.Test_options.SetAndroidMkEntries(entries)
-		})
-	base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
-}
-
-func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
-	entries.Required = append(entries.Required, "libc++")
-	entries.ExtraEntries = append(entries.ExtraEntries,
-		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			path, file := filepath.Split(installer.path.String())
-			stem := strings.TrimSuffix(file, filepath.Ext(file))
-
-			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
-			entries.SetString("LOCAL_MODULE_PATH", path)
-			entries.SetString("LOCAL_MODULE_STEM", stem)
-			entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
-			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
-		})
-}
diff --git a/python/binary.go b/python/binary.go
index 670e0d3..75135f3 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,11 +18,10 @@
 
 import (
 	"fmt"
+	"path/filepath"
+	"strings"
 
 	"android/soong/android"
-	"android/soong/bazel"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -33,63 +32,6 @@
 	ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
 }
 
-type bazelPythonBinaryAttributes struct {
-	Main           *bazel.Label
-	Srcs           bazel.LabelListAttribute
-	Deps           bazel.LabelListAttribute
-	Python_version *string
-	Imports        bazel.StringListAttribute
-}
-
-func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
-	// TODO(b/182306917): this doesn't fully handle all nested props versioned
-	// by the python version, which would have been handled by the version split
-	// mutator. This is sufficient for very simple python_binary_host modules
-	// under Bionic.
-	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
-	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
-	var python_version *string
-	if py3Enabled && py2Enabled {
-		panic(fmt.Errorf(
-			"error for '%s' module: bp2build's python_binary_host converter does not support "+
-				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
-	} else if py2Enabled {
-		python_version = &pyVersion2
-	} else {
-		// do nothing, since python_version defaults to PY3.
-	}
-
-	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-	attrs := &bazelPythonBinaryAttributes{
-		Main:           nil,
-		Srcs:           baseAttrs.Srcs,
-		Deps:           baseAttrs.Deps,
-		Python_version: python_version,
-		Imports:        baseAttrs.Imports,
-	}
-
-	for _, propIntf := range m.GetProperties() {
-		if props, ok := propIntf.(*BinaryProperties); ok {
-			// main is optional.
-			if props.Main != nil {
-				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
-				attrs.Main = &main
-				break
-			}
-		}
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		// Use the native py_binary rule.
-		Rule_class: "py_binary",
-	}
-
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: m.Name(),
-		Data: baseAttrs.Data,
-	}, attrs)
-}
-
 type BinaryProperties struct {
 	// the name of the source file that is the main entry point of the program.
 	// this file must also be listed in srcs.
@@ -118,49 +60,58 @@
 	Auto_gen_config *bool
 }
 
-type binaryDecorator struct {
+type PythonBinaryModule struct {
+	PythonLibraryModule
 	binaryProperties BinaryProperties
 
-	*pythonInstaller
+	// (.intermediate) module output path as installation source.
+	installSource android.Path
+
+	// Final installation path.
+	installedDest android.Path
+
+	androidMkSharedLibs []string
 }
 
+var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
+var _ android.Module = (*PythonBinaryModule)(nil)
+
 type IntermPathProvider interface {
 	IntermPathForModuleOut() android.OptionalPath
 }
 
-func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
-	module := newModule(hod, android.MultilibFirst)
-	decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
-
-	module.bootstrapper = decorator
-	module.installer = decorator
-
-	return module, decorator
+func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
+	return &PythonBinaryModule{
+		PythonLibraryModule: *newModule(hod, android.MultilibFirst),
+	}
 }
 
 func PythonBinaryHostFactory() android.Module {
-	module, _ := NewBinary(android.HostSupported)
-
-	android.InitBazelModule(module)
-
-	return module.init()
+	return NewBinary(android.HostSupported).init()
 }
 
-func (binary *binaryDecorator) autorun() bool {
-	return BoolDefault(binary.binaryProperties.Autorun, true)
+func (p *PythonBinaryModule) init() android.Module {
+	p.AddProperties(&p.properties, &p.protoProperties)
+	p.AddProperties(&p.binaryProperties)
+	android.InitAndroidArchModule(p, p.hod, p.multilib)
+	android.InitDefaultableModule(p)
+	android.InitBazelModule(p)
+	return p
 }
 
-func (binary *binaryDecorator) bootstrapperProps() []interface{} {
-	return []interface{}{&binary.binaryProperties}
+func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+	p.buildBinary(ctx)
+	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
+		p.installSource.Base(), p.installSource)
 }
 
-func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
-	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
-	depsSrcsZips android.Paths) android.OptionalPath {
-
+func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
+	embeddedLauncher := p.isEmbeddedLauncherEnabled()
+	depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
 	main := ""
-	if binary.autorun() {
-		main = binary.getPyMainFile(ctx, srcsPathMappings)
+	if p.autorun() {
+		main = p.getPyMainFile(ctx, p.srcsPathMappings)
 	}
 
 	var launcherPath android.OptionalPath
@@ -175,15 +126,88 @@
 			}
 		})
 	}
-	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
-		binary.getHostInterpreterName(ctx, actualVersion),
-		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+	srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1)
+	if embeddedLauncher {
+		srcsZips = append(srcsZips, p.precompiledSrcsZip)
+	} else {
+		srcsZips = append(srcsZips, p.srcsZip)
+	}
+	srcsZips = append(srcsZips, depsSrcsZips...)
+	p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+		p.getHostInterpreterName(ctx, p.properties.Actual_version),
+		main, p.getStem(ctx), srcsZips)
 
-	return android.OptionalPathForPath(binFile)
+	var sharedLibs []string
+	// if embedded launcher is enabled, we need to collect the shared library dependencies of the
+	// launcher
+	for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+		sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+	}
+	p.androidMkSharedLibs = sharedLibs
+}
+
+func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
+	entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}
+
+	entries.Class = "EXECUTABLES"
+
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+		})
+
+	entries.Required = append(entries.Required, "libc++")
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			path, file := filepath.Split(p.installedDest.String())
+			stem := strings.TrimSuffix(file, filepath.Ext(file))
+
+			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+			entries.SetString("LOCAL_MODULE_PATH", path)
+			entries.SetString("LOCAL_MODULE_STEM", stem)
+			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
+			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+		})
+
+	return []android.AndroidMkEntries{entries}
+}
+
+func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	p.PythonLibraryModule.DepsMutator(ctx)
+
+	if p.isEmbeddedLauncherEnabled() {
+		p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target())
+	}
+}
+
+// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
+// fulfilling the android.HostToolProvider interface.
+func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
+	// TODO: This should only be set when building host binaries -- tests built for device would be
+	// setting this incorrectly.
+	return android.OptionalPathForPath(p.installedDest)
+}
+
+// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
+func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.installSource}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
+	return Bool(p.properties.Embedded_launcher)
+}
+
+func (b *PythonBinaryModule) autorun() bool {
+	return BoolDefault(b.binaryProperties.Autorun, true)
 }
 
 // get host interpreter name.
-func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
 	actualVersion string) string {
 	var interp string
 	switch actualVersion {
@@ -200,13 +224,13 @@
 }
 
 // find main program path within runfiles tree.
-func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
 	srcsPathMappings []pathMapping) string {
 	var main string
-	if String(binary.binaryProperties.Main) == "" {
+	if String(p.binaryProperties.Main) == "" {
 		main = ctx.ModuleName() + pyExt
 	} else {
-		main = String(binary.binaryProperties.Main)
+		main = String(p.binaryProperties.Main)
 	}
 
 	for _, path := range srcsPathMappings {
@@ -219,11 +243,21 @@
 	return ""
 }
 
-func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
+func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
 	stem := ctx.ModuleName()
-	if String(binary.binaryProperties.Stem) != "" {
-		stem = String(binary.binaryProperties.Stem)
+	if String(p.binaryProperties.Stem) != "" {
+		stem = String(p.binaryProperties.Stem)
 	}
 
-	return stem + String(binary.binaryProperties.Suffix)
+	return stem + String(p.binaryProperties.Suffix)
+}
+
+func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
+	if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
+		dir = dir64
+	}
+	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+	}
+	return android.PathForModuleInstall(ctx, dir, relative)
 }
diff --git a/python/bp2build.go b/python/bp2build.go
new file mode 100644
index 0000000..bdac2dc
--- /dev/null
+++ b/python/bp2build.go
@@ -0,0 +1,226 @@
+// Copyright 2023 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 python
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/bazel"
+)
+
+type bazelPythonLibraryAttributes struct {
+	Srcs         bazel.LabelListAttribute
+	Deps         bazel.LabelListAttribute
+	Imports      bazel.StringListAttribute
+	Srcs_version *string
+}
+
+type bazelPythonProtoLibraryAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
+type baseAttributes struct {
+	// TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
+	//Pkg_path    bazel.StringAttribute
+	// TODO: Related to Pkg_bath and similarLy gated
+	//Is_internal bazel.BoolAttribute
+	// Combines Srcs and Exclude_srcs
+	Srcs bazel.LabelListAttribute
+	Deps bazel.LabelListAttribute
+	// Combines Data and Java_data (invariant)
+	Data    bazel.LabelListAttribute
+	Imports bazel.StringListAttribute
+}
+
+func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
+	var attrs baseAttributes
+	archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
+	for axis, configToProps := range archVariantBaseProps {
+		for config, props := range configToProps {
+			if baseProps, ok := props.(*BaseProperties); ok {
+				attrs.Srcs.SetSelectValue(axis, config,
+					android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
+				attrs.Deps.SetSelectValue(axis, config,
+					android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
+				data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
+				data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
+				attrs.Data.SetSelectValue(axis, config, data)
+			}
+		}
+	}
+
+	partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
+		"proto": android.ProtoSrcLabelPartition,
+		"py":    bazel.LabelPartition{Keep_remainder: true},
+	})
+	attrs.Srcs = partitionedSrcs["py"]
+
+	if !partitionedSrcs["proto"].IsEmpty() {
+		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
+		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+
+		pyProtoLibraryName := m.Name() + "_py_proto"
+		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+			Rule_class:        "py_proto_library",
+			Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
+		}, android.CommonAttributes{
+			Name: pyProtoLibraryName,
+		}, &bazelPythonProtoLibraryAttributes{
+			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+		})
+
+		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
+	}
+
+	// Bazel normally requires `import path.from.top.of.tree` statements in
+	// python code, but with soong you can directly import modules from libraries.
+	// Add "imports" attributes to the bazel library so it matches soong's behavior.
+	imports := "."
+	if m.properties.Pkg_path != nil {
+		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
+		// pkg_path properly right now. If the folder structure that contains this
+		// Android.bp file matches pkg_path, we can set imports to an appropriate
+		// number of ../..s to emulate moving the files under a pkg_path folder.
+		pkg_path := filepath.Clean(*m.properties.Pkg_path)
+		if strings.HasPrefix(pkg_path, "/") {
+			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
+		}
+
+		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
+			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
+		}
+		numFolders := strings.Count(pkg_path, "/") + 1
+		dots := make([]string, numFolders)
+		for i := 0; i < numFolders; i++ {
+			dots[i] = ".."
+		}
+		imports = strings.Join(dots, "/")
+	}
+	attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
+
+	return attrs
+}
+
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *PythonLibraryModule) {
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_library modules under
+	// Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version *string
+	if py2Enabled && !py3Enabled {
+		python_version = &pyVersion2
+	} else if !py2Enabled && py3Enabled {
+		python_version = &pyVersion3
+	} else if !py2Enabled && !py3Enabled {
+		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
+	} else {
+		// do nothing, since python_version defaults to PY2ANDPY3
+	}
+
+	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+
+	attrs := &bazelPythonLibraryAttributes{
+		Srcs:         baseAttrs.Srcs,
+		Deps:         baseAttrs.Deps,
+		Srcs_version: python_version,
+		Imports:      baseAttrs.Imports,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_library rule.
+		Rule_class: "py_library",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: m.Name(),
+		Data: baseAttrs.Data,
+	}, attrs)
+}
+
+type bazelPythonBinaryAttributes struct {
+	Main           *bazel.Label
+	Srcs           bazel.LabelListAttribute
+	Deps           bazel.LabelListAttribute
+	Python_version *string
+	Imports        bazel.StringListAttribute
+}
+
+func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *PythonBinaryModule) {
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_binary_host modules
+	// under Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version *string
+	if py3Enabled && py2Enabled {
+		panic(fmt.Errorf(
+			"error for '%s' module: bp2build's python_binary_host converter does not support "+
+				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
+	} else if py2Enabled {
+		python_version = &pyVersion2
+	} else {
+		// do nothing, since python_version defaults to PY3.
+	}
+
+	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+	attrs := &bazelPythonBinaryAttributes{
+		Main:           nil,
+		Srcs:           baseAttrs.Srcs,
+		Deps:           baseAttrs.Deps,
+		Python_version: python_version,
+		Imports:        baseAttrs.Imports,
+	}
+
+	for _, propIntf := range m.GetProperties() {
+		if props, ok := propIntf.(*BinaryProperties); ok {
+			// main is optional.
+			if props.Main != nil {
+				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
+				attrs.Main = &main
+				break
+			}
+		}
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_binary rule.
+		Rule_class: "py_binary",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: m.Name(),
+		Data: baseAttrs.Data,
+	}, attrs)
+}
+
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	pythonLibBp2Build(ctx, p)
+}
+
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	pythonBinaryBp2Build(ctx, p)
+}
+
+func (p *PythonTestModule) ConvertWithBp2build(_ android.TopDownMutatorContext) {
+	// Tests are currently unsupported
+}
diff --git a/python/builder.go b/python/builder.go
index b4ab206..1066493 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -70,6 +70,17 @@
 			CommandDeps: []string{"$mergeParCmd"},
 		},
 		"srcsZips", "launcher")
+
+	precompile = pctx.AndroidStaticRule("precompilePython", blueprint.RuleParams{
+		Command: `LD_LIBRARY_PATH="$ldLibraryPath" ` +
+			`PYTHONPATH=$stdlibZip/internal/stdlib ` +
+			`$launcher build/soong/python/scripts/precompile_python.py $in $out`,
+		CommandDeps: []string{
+			"$stdlibZip",
+			"$launcher",
+			"build/soong/python/scripts/precompile_python.py",
+		},
+	}, "stdlibZip", "launcher", "ldLibraryPath")
 )
 
 func init() {
diff --git a/python/installer.go b/python/installer.go
deleted file mode 100644
index 396f036..0000000
--- a/python/installer.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 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 python
-
-import (
-	"path/filepath"
-
-	"android/soong/android"
-)
-
-// This file handles installing python executables into their final location
-
-type installLocation int
-
-const (
-	InstallInData installLocation = iota
-)
-
-type pythonInstaller struct {
-	dir      string
-	dir64    string
-	relative string
-
-	path android.InstallPath
-
-	androidMkSharedLibs []string
-}
-
-func NewPythonInstaller(dir, dir64 string) *pythonInstaller {
-	return &pythonInstaller{
-		dir:   dir,
-		dir64: dir64,
-	}
-}
-
-var _ installer = (*pythonInstaller)(nil)
-
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
-	dir := installer.dir
-	if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
-		dir = installer.dir64
-	}
-	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
-		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
-	}
-	return android.PathForModuleInstall(ctx, dir, installer.relative)
-}
-
-func (installer *pythonInstaller) install(ctx android.ModuleContext, file android.Path) {
-	installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
-}
-
-func (installer *pythonInstaller) setAndroidMkSharedLibs(sharedLibs []string) {
-	installer.androidMkSharedLibs = sharedLibs
-}
diff --git a/python/library.go b/python/library.go
index df92df4..7cdb80b 100644
--- a/python/library.go
+++ b/python/library.go
@@ -18,9 +18,6 @@
 
 import (
 	"android/soong/android"
-	"android/soong/bazel"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -33,66 +30,9 @@
 }
 
 func PythonLibraryHostFactory() android.Module {
-	module := newModule(android.HostSupported, android.MultilibFirst)
-
-	android.InitBazelModule(module)
-
-	return module.init()
-}
-
-type bazelPythonLibraryAttributes struct {
-	Srcs         bazel.LabelListAttribute
-	Deps         bazel.LabelListAttribute
-	Imports      bazel.StringListAttribute
-	Srcs_version *string
-}
-
-type bazelPythonProtoLibraryAttributes struct {
-	Deps bazel.LabelListAttribute
-}
-
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
-	// TODO(b/182306917): this doesn't fully handle all nested props versioned
-	// by the python version, which would have been handled by the version split
-	// mutator. This is sufficient for very simple python_library modules under
-	// Bionic.
-	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
-	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
-	var python_version *string
-	if py2Enabled && !py3Enabled {
-		python_version = &pyVersion2
-	} else if !py2Enabled && py3Enabled {
-		python_version = &pyVersion3
-	} else if !py2Enabled && !py3Enabled {
-		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
-	} else {
-		// do nothing, since python_version defaults to PY2ANDPY3
-	}
-
-	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-
-	attrs := &bazelPythonLibraryAttributes{
-		Srcs:         baseAttrs.Srcs,
-		Deps:         baseAttrs.Deps,
-		Srcs_version: python_version,
-		Imports:      baseAttrs.Imports,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		// Use the native py_library rule.
-		Rule_class: "py_library",
-	}
-
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: m.Name(),
-		Data: baseAttrs.Data,
-	}, attrs)
+	return newModule(android.HostSupported, android.MultilibFirst).init()
 }
 
 func PythonLibraryFactory() android.Module {
-	module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
-
-	android.InitBazelModule(module)
-
-	return module.init()
+	return newModule(android.HostAndDeviceSupported, android.MultilibBoth).init()
 }
diff --git a/python/python.go b/python/python.go
index 24e1bb2..18e5b68 100644
--- a/python/python.go
+++ b/python/python.go
@@ -22,8 +22,6 @@
 	"regexp"
 	"strings"
 
-	"android/soong/bazel"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -122,26 +120,13 @@
 	Embedded_launcher *bool `blueprint:"mutated"`
 }
 
-type baseAttributes struct {
-	// TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
-	//Pkg_path    bazel.StringAttribute
-	// TODO: Related to Pkg_bath and similarLy gated
-	//Is_internal bazel.BoolAttribute
-	// Combines Srcs and Exclude_srcs
-	Srcs bazel.LabelListAttribute
-	Deps bazel.LabelListAttribute
-	// Combines Data and Java_data (invariant)
-	Data    bazel.LabelListAttribute
-	Imports bazel.StringListAttribute
-}
-
 // Used to store files of current module after expanding dependencies
 type pathMapping struct {
 	dest string
 	src  android.Path
 }
 
-type Module struct {
+type PythonLibraryModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.BazelModuleBase
@@ -153,16 +138,6 @@
 	hod      android.HostOrDeviceSupported
 	multilib android.Multilib
 
-	// interface used to bootstrap .par executable when embedded_launcher is true
-	// this should be set by Python modules which are runnable, e.g. binaries and tests
-	// bootstrapper might be nil (e.g. Python library module).
-	bootstrapper bootstrapper
-
-	// interface that implements functions required for installation
-	// this should be set by Python modules which are runnable, e.g. binaries and tests
-	// installer might be nil (e.g. Python library module).
-	installer installer
-
 	// the Python files of current module after expanding source dependencies.
 	// pathMapping: <dest: runfile_path, src: source_path>
 	srcsPathMappings []pathMapping
@@ -171,152 +146,62 @@
 	// pathMapping: <dest: runfile_path, src: source_path>
 	dataPathMappings []pathMapping
 
-	// the zip filepath for zipping current module source/data files.
+	// The zip file containing the current module's source/data files.
 	srcsZip android.Path
 
-	// dependency modules' zip filepath for zipping current module source/data files.
-	depsSrcsZips android.Paths
-
-	// (.intermediate) module output path as installation source.
-	installSource android.OptionalPath
-
-	// Map to ensure sub-part of the AndroidMk for this module is only added once
-	subAndroidMkOnce map[subAndroidMkProvider]bool
+	// The zip file containing the current module's source/data files, with the
+	// source files precompiled.
+	precompiledSrcsZip android.Path
 }
 
 // newModule generates new Python base module
-func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
-	return &Module{
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *PythonLibraryModule {
+	return &PythonLibraryModule{
 		hod:      hod,
 		multilib: multilib,
 	}
 }
 
-func (m *Module) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
-	var attrs baseAttributes
-	archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
-	for axis, configToProps := range archVariantBaseProps {
-		for config, props := range configToProps {
-			if baseProps, ok := props.(*BaseProperties); ok {
-				attrs.Srcs.SetSelectValue(axis, config,
-					android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
-				attrs.Deps.SetSelectValue(axis, config,
-					android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
-				data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
-				data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
-				attrs.Data.SetSelectValue(axis, config, data)
-			}
-		}
-	}
-
-	partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
-		"proto": android.ProtoSrcLabelPartition,
-		"py":    bazel.LabelPartition{Keep_remainder: true},
-	})
-	attrs.Srcs = partitionedSrcs["py"]
-
-	if !partitionedSrcs["proto"].IsEmpty() {
-		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
-		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
-
-		pyProtoLibraryName := m.Name() + "_py_proto"
-		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
-			Rule_class:        "py_proto_library",
-			Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
-		}, android.CommonAttributes{
-			Name: pyProtoLibraryName,
-		}, &bazelPythonProtoLibraryAttributes{
-			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
-		})
-
-		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
-	}
-
-	// Bazel normally requires `import path.from.top.of.tree` statements in
-	// python code, but with soong you can directly import modules from libraries.
-	// Add "imports" attributes to the bazel library so it matches soong's behavior.
-	imports := "."
-	if m.properties.Pkg_path != nil {
-		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
-		// pkg_path properly right now. If the folder structure that contains this
-		// Android.bp file matches pkg_path, we can set imports to an appropriate
-		// number of ../..s to emulate moving the files under a pkg_path folder.
-		pkg_path := filepath.Clean(*m.properties.Pkg_path)
-		if strings.HasPrefix(pkg_path, "/") {
-			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
-		}
-
-		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
-			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
-		}
-		numFolders := strings.Count(pkg_path, "/") + 1
-		dots := make([]string, numFolders)
-		for i := 0; i < numFolders; i++ {
-			dots[i] = ".."
-		}
-		imports = strings.Join(dots, "/")
-	}
-	attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
-
-	return attrs
-}
-
-// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
-type bootstrapper interface {
-	bootstrapperProps() []interface{}
-	bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
-		srcsPathMappings []pathMapping, srcsZip android.Path,
-		depsSrcsZips android.Paths) android.OptionalPath
-
-	autorun() bool
-}
-
-// installer interface should be implemented for installable modules, e.g. binary and test
-type installer interface {
-	install(ctx android.ModuleContext, path android.Path)
-	setAndroidMkSharedLibs(sharedLibs []string)
-}
-
 // interface implemented by Python modules to provide source and data mappings and zip to python
 // modules that depend on it
 type pythonDependency interface {
 	getSrcsPathMappings() []pathMapping
 	getDataPathMappings() []pathMapping
 	getSrcsZip() android.Path
+	getPrecompiledSrcsZip() android.Path
 }
 
 // getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
-func (p *Module) getSrcsPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping {
 	return p.srcsPathMappings
 }
 
 // getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
-func (p *Module) getDataPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getDataPathMappings() []pathMapping {
 	return p.dataPathMappings
 }
 
 // getSrcsZip returns the filepath where the current module's source/data files are zipped.
-func (p *Module) getSrcsZip() android.Path {
+func (p *PythonLibraryModule) getSrcsZip() android.Path {
 	return p.srcsZip
 }
 
-var _ pythonDependency = (*Module)(nil)
+// getSrcsZip returns the filepath where the current module's source/data files are zipped.
+func (p *PythonLibraryModule) getPrecompiledSrcsZip() android.Path {
+	return p.precompiledSrcsZip
+}
 
-var _ android.AndroidMkEntriesProvider = (*Module)(nil)
+func (p *PythonLibraryModule) getBaseProperties() *BaseProperties {
+	return &p.properties
+}
 
-func (p *Module) init(additionalProps ...interface{}) android.Module {
+var _ pythonDependency = (*PythonLibraryModule)(nil)
+
+func (p *PythonLibraryModule) init() android.Module {
 	p.AddProperties(&p.properties, &p.protoProperties)
-
-	// Add additional properties for bootstrapping/installation
-	// This is currently tied to the bootstrapper interface;
-	// however, these are a combination of properties for the installation and bootstrapping of a module
-	if p.bootstrapper != nil {
-		p.AddProperties(p.bootstrapper.bootstrapperProps()...)
-	}
-
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
 	android.InitDefaultableModule(p)
-
+	android.InitBazelModule(p)
 	return p
 }
 
@@ -338,36 +223,48 @@
 }
 
 var (
-	pythonLibTag         = dependencyTag{name: "pythonLib"}
-	javaDataTag          = dependencyTag{name: "javaData"}
+	pythonLibTag = dependencyTag{name: "pythonLib"}
+	javaDataTag  = dependencyTag{name: "javaData"}
+	// The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
 	launcherTag          = dependencyTag{name: "launcher"}
 	launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
-	pathComponentRegexp  = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
-	pyExt                = ".py"
-	protoExt             = ".proto"
-	pyVersion2           = "PY2"
-	pyVersion3           = "PY3"
-	internalPath         = "internal"
+	// The python interpreter built for host so that we can precompile python sources.
+	// This only works because the precompiled sources don't vary by architecture.
+	// The soong module name is "py3-launcher".
+	hostLauncherTag          = dependencyTag{name: "hostLauncher"}
+	hostlauncherSharedLibTag = dependencyTag{name: "hostlauncherSharedLib"}
+	hostStdLibTag            = dependencyTag{name: "hostStdLib"}
+	pathComponentRegexp      = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+	pyExt                    = ".py"
+	protoExt                 = ".proto"
+	pyVersion2               = "PY2"
+	pyVersion3               = "PY3"
+	internalPath             = "internal"
 )
 
+type basePropertiesProvider interface {
+	getBaseProperties() *BaseProperties
+}
+
 // versionSplitMutator creates version variants for modules and appends the version-specific
 // properties for a given variant to the properties in the variant module
 func versionSplitMutator() func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
-		if base, ok := mctx.Module().(*Module); ok {
-			versionNames := []string{}
+		if base, ok := mctx.Module().(basePropertiesProvider); ok {
+			props := base.getBaseProperties()
+			var versionNames []string
 			// collect version specific properties, so that we can merge version-specific properties
 			// into the module's overall properties
-			versionProps := []VersionProperties{}
+			var versionProps []VersionProperties
 			// PY3 is first so that we alias the PY3 variant rather than PY2 if both
 			// are available
-			if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) {
+			if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
 				versionNames = append(versionNames, pyVersion3)
-				versionProps = append(versionProps, base.properties.Version.Py3)
+				versionProps = append(versionProps, props.Version.Py3)
 			}
-			if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) {
+			if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
 				versionNames = append(versionNames, pyVersion2)
-				versionProps = append(versionProps, base.properties.Version.Py2)
+				versionProps = append(versionProps, props.Version.Py2)
 			}
 			modules := mctx.CreateLocalVariations(versionNames...)
 			// Alias module to the first variant
@@ -376,9 +273,10 @@
 			}
 			for i, v := range versionNames {
 				// set the actual version for Python module.
-				modules[i].(*Module).properties.Actual_version = v
+				newProps := modules[i].(basePropertiesProvider).getBaseProperties()
+				newProps.Actual_version = v
 				// append versioned properties for the Python module to the overall properties
-				err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
+				err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil)
 				if err != nil {
 					panic(err)
 				}
@@ -387,38 +285,6 @@
 	}
 }
 
-// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
-// fulfilling HostToolProvider interface.
-func (p *Module) HostToolPath() android.OptionalPath {
-	if p.installer != nil {
-		if bin, ok := p.installer.(*binaryDecorator); ok {
-			// TODO: This should only be set when building host binaries -- tests built for device would be
-			// setting this incorrectly.
-			return android.OptionalPathForPath(bin.path)
-		}
-	}
-
-	return android.OptionalPath{}
-
-}
-
-// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
-func (p *Module) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		if outputFile := p.installSource; outputFile.Valid() {
-			return android.Paths{outputFile.Path()}, nil
-		}
-		return android.Paths{}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-func (p *Module) isEmbeddedLauncherEnabled() bool {
-	return p.installer != nil && Bool(p.properties.Embedded_launcher)
-}
-
 func anyHasExt(paths []string, ext string) bool {
 	for _, p := range paths {
 		if filepath.Ext(p) == ext {
@@ -429,7 +295,7 @@
 	return false
 }
 
-func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
+func (p *PythonLibraryModule) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
 	return anyHasExt(p.properties.Srcs, ext)
 }
 
@@ -437,7 +303,7 @@
 //   - handles proto dependencies,
 //   - if required, specifies launcher and adds launcher dependencies,
 //   - applies python version mutations to Python dependencies
-func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	android.ProtoDeps(ctx, &p.protoProperties)
 
 	versionVariation := []blueprint.Variation{
@@ -452,111 +318,85 @@
 	// Add python library dependencies for this python version variation
 	ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
 
-	// If this module will be installed and has an embedded launcher, we need to add dependencies for:
-	//   * standard library
-	//   * launcher
-	//   * shared dependencies of the launcher
-	if p.installer != nil && p.isEmbeddedLauncherEnabled() {
-		var stdLib string
-		var launcherModule string
-		// Add launcher shared lib dependencies. Ideally, these should be
-		// derived from the `shared_libs` property of the launcher. However, we
-		// cannot read the property at this stage and it will be too late to add
-		// dependencies later.
-		launcherSharedLibDeps := []string{
-			"libsqlite",
-		}
-		// Add launcher-specific dependencies for bionic
-		if ctx.Target().Os.Bionic() {
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
-		}
-		if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
-		}
-
-		switch p.properties.Actual_version {
-		case pyVersion2:
-			stdLib = "py2-stdlib"
-
-			launcherModule = "py2-launcher"
-			if p.bootstrapper.autorun() {
-				launcherModule = "py2-launcher-autorun"
-			}
-
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
-
-		case pyVersion3:
-			stdLib = "py3-stdlib"
-
-			launcherModule = "py3-launcher"
-			if p.bootstrapper.autorun() {
-				launcherModule = "py3-launcher-autorun"
-			}
-			if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
-				launcherModule += "-static"
-			}
-
-			if ctx.Device() {
-				launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
-			}
-		default:
-			panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
-				p.properties.Actual_version, ctx.ModuleName()))
-		}
-		ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
-		ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
-		ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
-	}
-
 	// Emulate the data property for java_data but with the arch variation overridden to "common"
 	// so that it can point to java modules.
 	javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
 	ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
+
+	p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget)
 }
 
-func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.generatePythonBuildActions(ctx)
-
-	// Only Python binary and test modules have non-empty bootstrapper.
-	if p.bootstrapper != nil {
-		// if the module is being installed, we need to collect all transitive dependencies to embed in
-		// the final par
-		p.collectPathsFromTransitiveDeps(ctx)
-		// bootstrap the module, including resolving main file, getting launcher path, and
-		// registering actions to build the par file
-		// bootstrap returns the binary output path
-		p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
-			p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
+// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib,
+// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use
+// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument
+// as the target to use for these dependencies. For embedded launcher python binaries, the launcher
+// that will be embedded will be under the same target as the python module itself. But when
+// precompiling python code, we need to get the python launcher built for host, even if we're
+// compiling the python module for device, so we pass a different target to this function.
+func (p *PythonLibraryModule) AddDepsOnPythonLauncherAndStdlib(ctx android.BottomUpMutatorContext,
+	stdLibTag, launcherTag, launcherSharedLibTag blueprint.DependencyTag,
+	autorun bool, targetForDeps android.Target) {
+	var stdLib string
+	var launcherModule string
+	// Add launcher shared lib dependencies. Ideally, these should be
+	// derived from the `shared_libs` property of the launcher. TODO: read these from
+	// the python launcher itself using ctx.OtherModuleProvider() or similar on the result
+	// of ctx.AddFarVariationDependencies()
+	launcherSharedLibDeps := []string{
+		"libsqlite",
+	}
+	// Add launcher-specific dependencies for bionic
+	if targetForDeps.Os.Bionic() {
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
+	}
+	if targetForDeps.Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
 	}
 
-	// Only Python binary and test modules have non-empty installer.
-	if p.installer != nil {
-		var sharedLibs []string
-		// if embedded launcher is enabled, we need to collect the shared library depenendencies of the
-		// launcher
-		for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
-			sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+	switch p.properties.Actual_version {
+	case pyVersion2:
+		stdLib = "py2-stdlib"
+
+		launcherModule = "py2-launcher"
+		if autorun {
+			launcherModule = "py2-launcher-autorun"
 		}
 
-		p.installer.setAndroidMkSharedLibs(sharedLibs)
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
+	case pyVersion3:
+		stdLib = "py3-stdlib"
 
-		// Install the par file from installSource
-		if p.installSource.Valid() {
-			p.installer.install(ctx, p.installSource.Path())
+		launcherModule = "py3-launcher"
+		if autorun {
+			launcherModule = "py3-launcher-autorun"
 		}
+		if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
+			launcherModule += "-static"
+		}
+		if ctx.Device() {
+			launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
+		}
+	default:
+		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+			p.properties.Actual_version, ctx.ModuleName()))
 	}
+	targetVariations := targetForDeps.Variations()
+	if ctx.ModuleName() != stdLib {
+		stdLibVariations := make([]blueprint.Variation, 0, len(targetVariations)+1)
+		stdLibVariations = append(stdLibVariations, blueprint.Variation{Mutator: "python_version", Variation: p.properties.Actual_version})
+		stdLibVariations = append(stdLibVariations, targetVariations...)
+		// Using AddFarVariationDependencies for all of these because they can be for a different
+		// platform, like if the python module itself was being compiled for device, we may want
+		// the python interpreter built for host so that we can precompile python sources.
+		ctx.AddFarVariationDependencies(stdLibVariations, stdLibTag, stdLib)
+	}
+	ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule)
+	ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...)
 }
 
-// generatePythonBuildActions performs build actions common to all Python modules
-func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) {
+// GenerateAndroidBuildActions performs build actions common to all Python modules
+func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
-	requiresSrcs := true
-	if p.bootstrapper != nil && !p.bootstrapper.autorun() {
-		requiresSrcs = false
-	}
-	if len(expandedSrcs) == 0 && requiresSrcs {
-		ctx.ModuleErrorf("doesn't have any source files!")
-	}
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
@@ -589,6 +429,7 @@
 
 	// generate the zipfile of all source and data files
 	p.srcsZip = p.createSrcsZip(ctx, pkgPath)
+	p.precompiledSrcsZip = p.precompileSrcs(ctx)
 }
 
 func isValidPythonPath(path string) error {
@@ -607,7 +448,7 @@
 
 // For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
 // for python/data files expanded from properties.
-func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
+func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
 	expandedSrcs, expandedData android.Paths) {
 	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
 	// check current module duplicates.
@@ -642,27 +483,28 @@
 }
 
 // createSrcsZip registers build actions to zip current module's sources and data.
-func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
+func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
 	relativeRootMap := make(map[string]android.Paths)
-	pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
-
 	var protoSrcs android.Paths
-	// "srcs" or "data" properties may contain filegroup so it might happen that
-	// the root directory for each source path is different.
-	for _, path := range pathMappings {
+	addPathMapping := func(path pathMapping) {
 		// handle proto sources separately
 		if path.src.Ext() == protoExt {
 			protoSrcs = append(protoSrcs, path.src)
 		} else {
-			var relativeRoot string
-			relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
-			if v, found := relativeRootMap[relativeRoot]; found {
-				relativeRootMap[relativeRoot] = append(v, path.src)
-			} else {
-				relativeRootMap[relativeRoot] = android.Paths{path.src}
-			}
+			relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+			relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
 		}
 	}
+
+	// "srcs" or "data" properties may contain filegroups so it might happen that
+	// the root directory for each source path is different.
+	for _, path := range p.srcsPathMappings {
+		addPathMapping(path)
+	}
+	for _, path := range p.dataPathMappings {
+		addPathMapping(path)
+	}
+
 	var zips android.Paths
 	if len(protoSrcs) > 0 {
 		protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
@@ -736,30 +578,79 @@
 	}
 }
 
-// isPythonLibModule returns whether the given module is a Python library Module or not
+func (p *PythonLibraryModule) precompileSrcs(ctx android.ModuleContext) android.Path {
+	// To precompile the python sources, we need a python interpreter and stdlib built
+	// for host. We then use those to compile the python sources, which may be used on either
+	// host of device. Python bytecode is architecture agnostic, so we're essentially
+	// "cross compiling" for device here purely by virtue of host and device python bytecode
+	// being the same.
+	var stdLib android.Path
+	var launcher android.Path
+	if ctx.ModuleName() == "py3-stdlib" || ctx.ModuleName() == "py2-stdlib" {
+		stdLib = p.srcsZip
+	} else {
+		ctx.VisitDirectDepsWithTag(hostStdLibTag, func(module android.Module) {
+			if dep, ok := module.(pythonDependency); ok {
+				stdLib = dep.getPrecompiledSrcsZip()
+			}
+		})
+	}
+	ctx.VisitDirectDepsWithTag(hostLauncherTag, func(module android.Module) {
+		if dep, ok := module.(IntermPathProvider); ok {
+			optionalLauncher := dep.IntermPathForModuleOut()
+			if optionalLauncher.Valid() {
+				launcher = optionalLauncher.Path()
+			}
+		}
+	})
+	var launcherSharedLibs android.Paths
+	var ldLibraryPath []string
+	ctx.VisitDirectDepsWithTag(hostlauncherSharedLibTag, func(module android.Module) {
+		if dep, ok := module.(IntermPathProvider); ok {
+			optionalPath := dep.IntermPathForModuleOut()
+			if optionalPath.Valid() {
+				launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path())
+				ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String()))
+			}
+		}
+	})
+
+	out := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszipprecompiled")
+	if stdLib == nil || launcher == nil {
+		// This shouldn't happen in a real build because we'll error out when adding dependencies
+		// on the stdlib and launcher if they don't exist. But some tests set
+		// AllowMissingDependencies.
+		return out
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        precompile,
+		Input:       p.srcsZip,
+		Output:      out,
+		Implicits:   launcherSharedLibs,
+		Description: "Precompile the python sources of " + ctx.ModuleName(),
+		Args: map[string]string{
+			"stdlibZip":     stdLib.String(),
+			"launcher":      launcher.String(),
+			"ldLibraryPath": strings.Join(ldLibraryPath, ":"),
+		},
+	})
+	return out
+}
+
+// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
 func isPythonLibModule(module blueprint.Module) bool {
-	if m, ok := module.(*Module); ok {
-		return m.isLibrary()
+	if _, ok := module.(*PythonLibraryModule); ok {
+		if _, ok := module.(*PythonBinaryModule); !ok {
+			return true
+		}
 	}
 	return false
 }
 
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
-func (p *Module) isLibrary() bool {
-	// Python library has no bootstrapper or installer
-	return p.bootstrapper == nil && p.installer == nil
-}
-
-func (p *Module) isBinary() bool {
-	_, ok := p.bootstrapper.(*binaryDecorator)
-	return ok
-}
-
 // collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
 // for module and its transitive dependencies and collects list of data/source file
 // zips for transitive dependencies.
-func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) {
+func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleContext, precompiled bool) android.Paths {
 	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
 	// check duplicates.
 	destToPySrcs := make(map[string]string)
@@ -773,6 +664,8 @@
 
 	seen := make(map[android.Module]bool)
 
+	var result android.Paths
+
 	// visit all its dependencies in depth first.
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		// we only collect dependencies tagged as python library deps
@@ -801,10 +694,15 @@
 				checkForDuplicateOutputPath(ctx, destToPyData,
 					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
 			}
-			p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
+			if precompiled {
+				result = append(result, dep.getPrecompiledSrcsZip())
+			} else {
+				result = append(result, dep.getSrcsZip())
+			}
 		}
 		return true
 	})
+	return result
 }
 
 // chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
@@ -825,18 +723,10 @@
 }
 
 // InstallInData returns true as Python is not supported in the system partition
-func (p *Module) InstallInData() bool {
+func (p *PythonLibraryModule) InstallInData() bool {
 	return true
 }
 
-func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	if p.isLibrary() {
-		pythonLibBp2Build(ctx, p)
-	} else if p.isBinary() {
-		pythonBinaryBp2Build(ctx, p)
-	}
-}
-
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/python/python_test.go b/python/python_test.go
index 42a1ffb..75a6a89 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -18,10 +18,10 @@
 	"fmt"
 	"os"
 	"path/filepath"
-	"regexp"
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
 )
 
 type pyModule struct {
@@ -33,8 +33,10 @@
 }
 
 var (
-	buildNamePrefix          = "soong_python_test"
-	moduleVariantErrTemplate = "%s: module %q variant %q: "
+	buildNamePrefix = "soong_python_test"
+	// We allow maching almost anything before the actual variant so that the os/arch variant
+	// is matched.
+	moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
 	pkgPathErrTemplate       = moduleVariantErrTemplate +
 		"pkg_path: %q must be a relative path contained in par file."
 	badIdentifierErrTemplate = moduleVariantErrTemplate +
@@ -312,10 +314,6 @@
 						"e/file4.py",
 					},
 					srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
-					depsSrcsZips: []string{
-						"out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
-						"out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
-					},
 				},
 			},
 		},
@@ -327,17 +325,26 @@
 		if d.desc != "module with duplicate runfile path" {
 			continue
 		}
-		errorPatterns := make([]string, len(d.errors))
-		for i, s := range d.errors {
-			errorPatterns[i] = regexp.QuoteMeta(s)
-		}
+		d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
+python_library {
+  name: "py3-stdlib",
+  host_supported: true,
+}
+cc_binary {
+  name: "py3-launcher",
+  host_supported: true,
+}
+`)
 
 		t.Run(d.desc, func(t *testing.T) {
 			result := android.GroupFixturePreparers(
 				android.PrepareForTestWithDefaults,
+				android.PrepareForTestWithArchMutator,
+				android.PrepareForTestWithAllowMissingDependencies,
+				cc.PrepareForTestWithCcDefaultModules,
 				PrepareForTestWithPythonBuildComponents,
 				d.mockFiles.AddToFixture(),
-			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
 				RunTest(t)
 
 			if len(result.Errs) > 0 {
@@ -346,17 +353,17 @@
 
 			for _, e := range d.expectedBinaries {
 				t.Run(e.name, func(t *testing.T) {
-					expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips)
+					expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
 				})
 			}
 		})
 	}
 }
 
-func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) {
+func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
 	module := ctx.ModuleForTests(name, variant)
 
-	base, baseOk := module.Module().(*Module)
+	base, baseOk := module.Module().(*PythonLibraryModule)
 	if !baseOk {
 		t.Fatalf("%s is not Python module!", name)
 	}
@@ -369,8 +376,6 @@
 	android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
 
 	android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
-
-	android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
 }
 
 func TestMain(m *testing.M) {
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
new file mode 100644
index 0000000..e12e7d2
--- /dev/null
+++ b/python/scripts/precompile_python.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import py_compile
+import os
+import shutil
+import tempfile
+import zipfile
+
+# This file needs to support both python 2 and 3.
+
+
+def process_one_file(name, inf, outzip):
+    if not name.endswith('.py'):
+        outzip.writestr(name, inf.read())
+        return
+
+    # Unfortunately py_compile requires the input/output files to be written
+    # out to disk.
+    with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+        shutil.copyfileobj(inf, tmp)
+        in_name = tmp.name
+    with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+        out_name = tmp.name
+    try:
+        py_compile.compile(in_name, out_name, name, doraise=True)
+        with open(out_name, 'rb') as f:
+            outzip.writestr(name + 'c', f.read())
+    finally:
+        os.remove(in_name)
+        os.remove(out_name)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('src_zip')
+    parser.add_argument('dst_zip')
+    args = parser.parse_args()
+
+    with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
+        with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
+            for name in inzip.namelist():
+                with inzip.open(name, mode='r') as inzipf:
+                    process_one_file(name, inzipf, outzip)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/python/test.go b/python/test.go
index fc5c211..fb8e918 100644
--- a/python/test.go
+++ b/python/test.go
@@ -32,6 +32,20 @@
 	ctx.RegisterModuleType("python_test", PythonTestFactory)
 }
 
+func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule {
+	return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+}
+
+func PythonTestHostFactory() android.Module {
+	return NewTest(android.HostSupportedNoCross).init()
+}
+
+func PythonTestFactory() android.Module {
+	module := NewTest(android.HostAndDeviceSupported)
+	module.multilib = android.MultilibBoth
+	return module.init()
+}
+
 type TestProperties struct {
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
 	// installed with the module.
@@ -52,76 +66,79 @@
 	Test_options android.CommonTestOptions
 }
 
-type testDecorator struct {
-	*binaryDecorator
+type PythonTestModule struct {
+	PythonBinaryModule
 
 	testProperties TestProperties
-
-	testConfig android.Path
-
-	data []android.DataPath
+	testConfig     android.Path
+	data           []android.DataPath
 }
 
-func (test *testDecorator) bootstrapperProps() []interface{} {
-	return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
+func (p *PythonTestModule) init() android.Module {
+	p.AddProperties(&p.properties, &p.protoProperties)
+	p.AddProperties(&p.binaryProperties)
+	p.AddProperties(&p.testProperties)
+	android.InitAndroidArchModule(p, p.hod, p.multilib)
+	android.InitDefaultableModule(p)
+	android.InitBazelModule(p)
+	if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+		p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+	}
+	return p
 }
 
-func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
-	test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
-		TestConfigProp:         test.testProperties.Test_config,
-		TestConfigTemplateProp: test.testProperties.Test_config_template,
-		TestSuites:             test.binaryDecorator.binaryProperties.Test_suites,
-		AutoGenConfig:          test.binaryDecorator.binaryProperties.Auto_gen_config,
+func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// We inherit from only the library's GenerateAndroidBuildActions, and then
+	// just use buildBinary() so that the binary is not installed into the location
+	// it would be for regular binaries.
+	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+	p.buildBinary(ctx)
+
+	p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+		TestConfigProp:         p.testProperties.Test_config,
+		TestConfigTemplateProp: p.testProperties.Test_config_template,
+		TestSuites:             p.binaryProperties.Test_suites,
+		AutoGenConfig:          p.binaryProperties.Auto_gen_config,
 		DeviceTemplate:         "${PythonBinaryHostTestConfigTemplate}",
 		HostTemplate:           "${PythonBinaryHostTestConfigTemplate}",
 	})
 
-	test.binaryDecorator.pythonInstaller.dir = "nativetest"
-	test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
+	p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource)
 
-	test.binaryDecorator.pythonInstaller.relative = ctx.ModuleName()
-
-	test.binaryDecorator.pythonInstaller.install(ctx, file)
-
-	dataSrcPaths := android.PathsForModuleSrc(ctx, test.testProperties.Data)
-
-	for _, dataSrcPath := range dataSrcPaths {
-		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
+		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
 		for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
-			test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+			p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
 		}
 	}
 }
 
-func NewTest(hod android.HostOrDeviceSupported) *Module {
-	module, binary := NewBinary(hod)
-
-	binary.pythonInstaller = NewPythonInstaller("nativetest", "nativetest64")
-
-	test := &testDecorator{binaryDecorator: binary}
-	if hod == android.HostSupportedNoCross && test.testProperties.Test_options.Unit_test == nil {
-		test.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
+	entriesList := p.PythonBinaryModule.AndroidMkEntries()
+	if len(entriesList) != 1 {
+		panic("Expected 1 entry")
 	}
+	entries := &entriesList[0]
 
-	module.bootstrapper = test
-	module.installer = test
+	entries.Class = "NATIVE_TESTS"
 
-	return module
-}
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			//entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+			if p.testConfig != nil {
+				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+			}
 
-func PythonTestHostFactory() android.Module {
-	module := NewTest(android.HostSupportedNoCross)
+			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
 
-	return module.init()
-}
+			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
 
-func PythonTestFactory() android.Module {
-	module := NewTest(android.HostAndDeviceSupported)
-	module.multilib = android.MultilibBoth
+			p.testProperties.Test_options.SetAndroidMkEntries(entries)
+		})
 
-	return module.init()
+	return entriesList
 }
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
index 56a5063..1e03f16 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -27,7 +27,10 @@
         failed = True
 
 assert_equal("__name__", __name__, "__main__")
-assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "par_test.py")
+fileName = os.path.basename(__file__)
+if fileName.endswith('.pyc'):
+    fileName = fileName[:-1]
+assert_equal("os.path.basename(__file__)", fileName, "par_test.py")
 
 archive = os.path.dirname(__file__)
 
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index ffad430..b513409 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -28,7 +28,10 @@
 archive = sys.modules["__main__"].__loader__.archive
 
 assert_equal("__name__", __name__, "testpkg.par_test")
-assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
+fileName = __file__
+if fileName.endswith('.pyc'):
+    fileName = fileName[:-1]
+assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
 
 # Python3 is returning None here for me, and I haven't found any problems caused by this.
 if sys.version_info[0] == 2:
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 17d80dd..e81ec6b 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -15,7 +15,6 @@
 package rust
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -30,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r468909b"
+	bindgenClangVersion = "clang-r475365b"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -178,10 +177,6 @@
 
 	if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" {
 		cflags = append(cflags, "-D__ANDROID_APEX__")
-		if ctx.Device() {
-			cflags = append(cflags, fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
-				ctx.RustModule().apexSdkVersion.FinalOrFutureInt()))
-		}
 	}
 
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/rust/compiler.go b/rust/compiler.go
index 6055158..8ec42f0 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"android/soong/cc"
 	"fmt"
 	"path/filepath"
 	"strings"
@@ -307,19 +308,7 @@
 	flags.EmitXrefs = ctx.Config().EmitXrefRules()
 
 	if ctx.Host() && !ctx.Windows() {
-		rpathPrefix := `\$$ORIGIN/`
-		if ctx.Darwin() {
-			rpathPrefix = "@loader_path/"
-		}
-
-		var rpath string
-		if ctx.toolchain().Is64Bit() {
-			rpath = "lib64"
-		} else {
-			rpath = "lib"
-		}
-		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
-		flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath)
+		flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
 	}
 
 	return flags
diff --git a/rust/config/global.go b/rust/config/global.go
index 81aec7e..7549969 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.64.0"
+	RustDefaultVersion = "1.65.0.p1"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/image.go b/rust/image.go
index dfc7f74..50bf02a 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -129,6 +129,10 @@
 	return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
 }
 
+func (ctx *moduleContext) SystemExtSpecific() bool {
+	return ctx.ModuleContext.SystemExtSpecific()
+}
+
 // Returns true when this module creates a vendor variant and wants to install the vendor variant
 // to the odm partition.
 func (c *Module) VendorVariantToOdm() bool {
@@ -158,22 +162,15 @@
 }
 
 func (mod *Module) OnlyInRamdisk() bool {
-	// TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InstallInRamdisk()
 }
 
 func (mod *Module) OnlyInRecovery() bool {
-	// TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InstallInRecovery()
 }
 
 func (mod *Module) OnlyInVendorRamdisk() bool {
-	return false
-}
-
-func (mod *Module) OnlyInProduct() bool {
-	//TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InstallInVendorRamdisk()
 }
 
 // Returns true when this module is configured to have core and vendor variants.
@@ -226,10 +223,7 @@
 	// Rust does not support installing to the product image yet.
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 
-	if mctx.ProductSpecific() {
-		mctx.PropertyErrorf("product_specific",
-			"Rust modules do not yet support installing to the product image.")
-	} else if Bool(mod.VendorProperties.Double_loadable) {
+	if Bool(mod.VendorProperties.Double_loadable) {
 		mctx.PropertyErrorf("double_loadable",
 			"Rust modules do not yet support double loading")
 	}
@@ -243,6 +237,11 @@
 			mctx.PropertyErrorf("vendor", "Vendor-only dylibs are not yet supported, use rust_library_rlib.")
 		}
 	}
+	if mctx.ProductSpecific() {
+		if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
+			mctx.PropertyErrorf("product", "Product-only dylibs are not yet supported, use rust_library_rlib.")
+		}
+	}
 
 	cc.MutateImage(mctx, mod)
 
diff --git a/rust/image_test.go b/rust/image_test.go
index 95e788f..8185872 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -103,3 +103,93 @@
 		}
        `)
 }
+
+func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
+	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+	partitionDefined := false
+	checkPartition := func(specific bool, partition string) {
+		if specific {
+			if expected != partition && !partitionDefined {
+				// The variant is installed to the 'partition'
+				t.Errorf("%s variant of %q must not be installed to %s partition", variant, name, partition)
+			}
+			partitionDefined = true
+		} else {
+			// The variant is not installed to the 'partition'
+			if expected == partition {
+				t.Errorf("%s variant of %q must be installed to %s partition", variant, name, partition)
+			}
+		}
+	}
+	socSpecific := func(m *Module) bool {
+		return m.SocSpecific()
+	}
+	deviceSpecific := func(m *Module) bool {
+		return m.DeviceSpecific()
+	}
+	productSpecific := func(m *Module) bool {
+		return m.ProductSpecific() || m.productSpecificModuleContext()
+	}
+	systemExtSpecific := func(m *Module) bool {
+		return m.SystemExtSpecific()
+	}
+	checkPartition(socSpecific(mod), "vendor")
+	checkPartition(deviceSpecific(mod), "odm")
+	checkPartition(productSpecific(mod), "product")
+	checkPartition(systemExtSpecific(mod), "system_ext")
+	if !partitionDefined && expected != "system" {
+		t.Errorf("%s variant of %q is expected to be installed to %s partition,"+
+			" but installed to system partition", variant, name, expected)
+	}
+}
+
+func TestInstallPartition(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := testRust(t, `
+		rust_binary {
+			name: "sample_system",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+		}
+		rust_binary {
+			name: "sample_system_ext",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			system_ext_specific: true,
+		}
+		rust_binary {
+			name: "sample_product",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			product_specific: true,
+		}
+		rust_binary {
+			name: "sample_vendor",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			vendor: true,
+		}
+		rust_binary {
+			name: "sample_odm",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			device_specific: true,
+		}
+		rust_binary {
+			name: "sample_all_available",
+			crate_name: "sample",
+			srcs: ["foo.rs"],
+			vendor_available: true,
+			product_available: true,
+		}
+	`)
+
+	checkInstallPartition(t, ctx, "sample_system", binaryCoreVariant, "system")
+	checkInstallPartition(t, ctx, "sample_system_ext", binaryCoreVariant, "system_ext")
+	checkInstallPartition(t, ctx, "sample_product", binaryProductVariant, "product")
+	checkInstallPartition(t, ctx, "sample_vendor", binaryVendorVariant, "vendor")
+	checkInstallPartition(t, ctx, "sample_odm", binaryVendorVariant, "odm")
+
+	checkInstallPartition(t, ctx, "sample_all_available", binaryCoreVariant, "system")
+}
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 97bd541..3bcd58c 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -83,6 +83,10 @@
 	rlibVendorVariant     = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
 	sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared"
 	rlibRecoveryVariant   = "android_recovery_arm64_armv8-a_rlib_rlib-std"
+	binaryCoreVariant     = "android_arm64_armv8-a"
+	binaryVendorVariant   = "android_vendor.29_arm64_armv8-a"
+	binaryProductVariant  = "android_product.29_arm64_armv8-a"
+	binaryRecoveryVariant = "android_recovery_arm64_armv8-a"
 )
 
 func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 4f8d22b..3236bce 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -31,9 +31,8 @@
 	Properties SourceProviderProperties
 
 	// The first file in OutputFiles must be the library entry point.
-	OutputFiles      android.Paths
-	subAndroidMkOnce map[SubAndroidMkProvider]bool
-	subName          string
+	OutputFiles android.Paths
+	subName     string
 }
 
 var _ SourceProvider = (*BaseSourceProvider)(nil)
diff --git a/rust/test.go b/rust/test.go
index 4f922b4..4b5296e 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -200,6 +200,7 @@
 	if ctx.Device() {
 		flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
 	}
+
 	return flags
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index 4796f69..24ca3d6 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -104,6 +104,7 @@
 			crate_name: "std",
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
+			product_available: true,
 			host_supported: true,
 			vendor_available: true,
 			vendor_ramdisk_available: true,
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index e46efe4..3d7c0fa 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -27,13 +27,15 @@
 
 
 def Proto(args):
-    json_content = ''
-    with open(args.source) as f:
-        for line in f:
-            if not line.lstrip().startswith('//'):
-                json_content += line
-    obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
-    pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
+    pb = linker_config_pb2.LinkerConfig()
+    for input in args.source.split(':'):
+        json_content = ''
+        with open(input) as f:
+            for line in f:
+                if not line.lstrip().startswith('//'):
+                    json_content += line
+        obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
+        ParseDict(obj, pb)
     with open(args.output, 'wb') as f:
         f.write(pb.SerializeToString())
 
@@ -104,7 +106,7 @@
         '--source',
         required=True,
         type=str,
-        help='Source linker configuration file in JSON.')
+        help='Colon-separated list of linker configuration files in JSON.')
     parser_proto.add_argument(
         '-o',
         '--output',
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
index 3650fa1..2ff0c5f 100644
--- a/scripts/hiddenapi/signature_trie.py
+++ b/scripts/hiddenapi/signature_trie.py
@@ -150,10 +150,6 @@
                                 f"wildcard '{last_element}' and "
                                 f"member signature '{member[0]}'")
             wildcard = [last_element]
-        elif last_element.islower():
-            raise Exception(f"Invalid signature '{signature}': last element "
-                            f"'{last_element}' is lower case but should be an "
-                            f"upper case class name or wildcard")
         else:
             packages = elements[0:-1]
             # Split the class name into outer / inner classes
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
index 6d4e660..bd4a9a8 100755
--- a/scripts/hiddenapi/signature_trie_test.py
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -117,14 +117,6 @@
         self.assertEqual(elements, self.signature_to_elements(signature))
         self.assertEqual(signature, self.elements_to_signature(elements))
 
-    def test_invalid_no_class_or_wildcard(self):
-        signature = "java/lang"
-        with self.assertRaises(Exception) as context:
-            self.signature_to_elements(signature)
-        self.assertIn(
-            "last element 'lang' is lower case but should be an "
-            "upper case class name or wildcard", str(context.exception))
-
     def test_non_standard_class_name(self):
         elements = [
             ("package", "javax"),
diff --git a/tests/apex_cc_module_arch_variant_tests.sh b/tests/apex_cc_module_arch_variant_tests.sh
index 97e6576..b0cade1 100755
--- a/tests/apex_cc_module_arch_variant_tests.sh
+++ b/tests/apex_cc_module_arch_variant_tests.sh
@@ -56,7 +56,7 @@
 
 # Number of CppCompile actions with arch variant flag
 actions_with_arch_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
-  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c "\-march=$ARCH_VARIANT_CFLAG")
+  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c \'-march=$ARCH_VARIANT_CFLAG\')
 
 # Number of all CppCompile actions
 all_cppcompile_actions_num=0
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index 412f84a..5007078 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -74,7 +74,7 @@
 # # Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg.
 call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs //system/apex/tools:deapexer
 DEBUGFS_PATH="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //external/e2fsprogs/debugfs))"
-DEAPEXER="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //system/apex/tools:deapexer))"
+DEAPEXER="bazel-bin/system/apex/tools/deapexer"
 DEAPEXER="$DEAPEXER --debugfs_path=$DEBUGFS_PATH"
 
 #######
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 17b4419..f3bad73 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -75,7 +75,6 @@
   grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
 }
 
-
 function test_add_android_bp() {
   setup
   run_soong
@@ -313,7 +312,6 @@
 
 }
 
-
 function test_add_file_to_soong_build() {
   setup
   run_soong
@@ -736,7 +734,6 @@
 
 }
 
-
 function test_bp2build_bazel_workspace_structure {
   setup
 
@@ -802,7 +799,7 @@
     || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
 }
 
-function test_bp2build_reports_multiple_errors {
+function test_bp2build_fails_fast {
   setup
 
   mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
@@ -830,7 +827,7 @@
   fi
 
   grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
-  grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
+  grep -q -v "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} found but not expected"
 }
 
 function test_bp2build_back_and_forth_null_build {
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 8c2ce48..6477dac 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -231,4 +231,11 @@
   fi
 }
 
+# Smoke test to verify api_bp2build worksapce does not contain any errors
+function test_api_bp2build_empty_build() {
+  setup
+  run_soong api_bp2build
+  run_bazel build --config=android --config=api_bp2build //:empty
+}
+
 scan_and_run_tests
diff --git a/tests/lib.sh b/tests/lib.sh
index 26bdc97..0973beb 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -94,6 +94,8 @@
   symlink_directory external/go-cmp
   symlink_directory external/golang-protobuf
   symlink_directory external/starlark-go
+  symlink_directory external/python
+  symlink_directory external/sqlite
 
   touch "$MOCK_TOP/Android.bp"
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index b928faa..cb7fe1e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -147,8 +147,10 @@
 	}
 }
 
-// fetchEnvConfig optionally fetches environment config from an
-// experiments system to control Soong features dynamically.
+// fetchEnvConfig optionally fetches a configuration file that can then subsequently be
+// loaded into Soong environment to control certain aspects of build behavior (e.g., enabling RBE).
+// If a configuration file already exists on disk, the fetch is run in the background
+// so as to NOT block the rest of the build execution.
 func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
 	configName := envConfigName + "." + jsonSuffix
 	expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
@@ -174,8 +176,13 @@
 		return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
 	}
 
+	configExists := false
+	outConfigFilePath := filepath.Join(config.OutDir(), configName)
+	if _, err := os.Stat(outConfigFilePath); err == nil {
+		configExists = true
+	}
+
 	tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
-	defer cancel()
 	fetchStart := time.Now()
 	cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
 		"-output_config_name", configName)
@@ -185,22 +192,39 @@
 		return err
 	}
 
-	if err := cmd.Wait(); err != nil {
-		status := smpb.ExpConfigFetcher_ERROR
-		expConfigFetcher.Status = &status
-		return err
-	}
-	fetchEnd := time.Now()
-	expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
-	outConfigFilePath := filepath.Join(config.OutDir(), configName)
-	expConfigFetcher.Filename = proto.String(outConfigFilePath)
-	if _, err := os.Stat(outConfigFilePath); err == nil {
+	fetchCfg := func() error {
+		if err := cmd.Wait(); err != nil {
+			status := smpb.ExpConfigFetcher_ERROR
+			expConfigFetcher.Status = &status
+			return err
+		}
+		fetchEnd := time.Now()
+		expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
+		expConfigFetcher.Filename = proto.String(outConfigFilePath)
+
+		if _, err := os.Stat(outConfigFilePath); err != nil {
+			status := smpb.ExpConfigFetcher_NO_CONFIG
+			expConfigFetcher.Status = &status
+			return err
+		}
 		status := smpb.ExpConfigFetcher_CONFIG
 		expConfigFetcher.Status = &status
-	} else {
-		status := smpb.ExpConfigFetcher_NO_CONFIG
-		expConfigFetcher.Status = &status
+		return nil
 	}
+
+	// If a config file does not exist, wait for the config file to be fetched. Otherwise
+	// fetch the config file in the background and return immediately.
+	if !configExists {
+		defer cancel()
+		return fetchCfg()
+	}
+
+	go func() {
+		defer cancel()
+		if err := fetchCfg(); err != nil {
+			ctx.Verbosef("Failed to fetch config file %v: %v\n", configName, err)
+		}
+	}()
 	return nil
 }
 
@@ -300,8 +324,8 @@
 	if bc != "" {
 		if err := fetchEnvConfig(ctx, ret, bc); err != nil {
 			ctx.Verbosef("Failed to fetch config file: %v\n", err)
-
-		} else if err := loadEnvConfig(ctx, ret, bc); err != nil {
+		}
+		if err := loadEnvConfig(ctx, ret, bc); err != nil {
 			ctx.Fatalln("Failed to parse env config files: %v", err)
 		}
 	}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 9f14bdd..9959e6f 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,16 +18,21 @@
 // another.
 
 import (
+	"bufio"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strconv"
+	"strings"
 	"time"
 
+	"android/soong/shared"
 	"android/soong/ui/metrics"
 
 	"google.golang.org/protobuf/proto"
 
+	bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
 	upload_proto "android/soong/ui/metrics/upload_proto"
 )
 
@@ -73,12 +78,113 @@
 	return metricsFiles
 }
 
+func parseTimingToNanos(str string) int64 {
+	millisString := removeDecimalPoint(str)
+	timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+	return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+	percentageString := removeDecimalPoint(str)
+	//remove the % at the end of the string
+	percentage := strings.ReplaceAll(percentageString, "%", "")
+	percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+	return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+	// The format is always 0.425 or 10.425
+	return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+	words := strings.Fields(line)
+	timing := words[3]
+	return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+	words := strings.Fields(line)
+	getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+		// Sample lines include:
+		// Total launch phase time   0.011 s    2.59%
+		// Total target pattern evaluation phase time  0.011 s    2.59%
+		var beginning int
+		var end int
+		for ind, word := range words {
+			if word == "Total" {
+				beginning = ind + 1
+			} else if beginning > 0 && word == "phase" {
+				end = ind
+				break
+			}
+		}
+		phaseName := strings.Join(words[beginning:end], " ")
+
+		// end is now "phase" - advance by 2 for timing and 4 for percentage
+		percentageString := words[end+4]
+		timingString := words[end+2]
+		timing := parseTimingToNanos(timingString)
+		percentagePortion := parsePercentageToTenThousandths(percentageString)
+		return phaseName, timing, percentagePortion
+	}
+
+	phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+	phaseTiming := bazel_metrics_proto.PhaseTiming{}
+	phaseTiming.DurationNanos = &timing
+	phaseTiming.PortionOfBuildTime = &portion
+
+	phaseTiming.PhaseName = &phaseName
+	return phaseTiming
+}
+
+func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
+	if bazelProfileFile == "" {
+		return
+	}
+
+	readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+		//serialize the proto, write it
+		bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+		file, err := os.ReadFile(filepath)
+		if err != nil {
+			ctx.Fatalln("Error reading metrics file\n", err)
+		}
+
+		scanner := bufio.NewScanner(strings.NewReader(string(file)))
+		scanner.Split(bufio.ScanLines)
+
+		var phaseTimings []*bazel_metrics_proto.PhaseTiming
+		for scanner.Scan() {
+			line := scanner.Text()
+			if strings.HasPrefix(line, "Total run time") {
+				total := parseTotal(line)
+				bazelMetrics.Total = &total
+			} else if strings.HasPrefix(line, "Total") {
+				phaseTiming := parsePhaseTiming(line)
+				phaseTimings = append(phaseTimings, &phaseTiming)
+			}
+		}
+		bazelMetrics.PhaseTimings = phaseTimings
+
+		return bazelMetrics
+	}
+
+	if _, err := os.Stat(bazelProfileFile); err != nil {
+		// We can assume bazel didn't run if the profile doesn't exist
+		return
+	}
+	bazelProto := readBazelProto(bazelProfileFile)
+	shared.Save(&bazelProto, bazelMetricsFile)
+}
+
 // UploadMetrics uploads a set of metrics files to a server for analysis.
 // The metrics files are first copied to a temporary directory
 // and the uploader is then executed in the background to allow the user/system
 // to continue working. Soong communicates to the uploader through the
 // upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
@@ -88,6 +194,7 @@
 		return
 	}
 
+	processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
 	// Several of the files might be directories.
 	metricsFiles := pruneMetricsFiles(paths)
 	if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 764a1e1..58d9237 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -29,6 +29,30 @@
 	"android/soong/ui/logger"
 )
 
+func writeBazelProfileFile(dir string) error {
+	contents := `
+
+=== PHASE SUMMARY INFORMATION ===
+
+Total launch phase time                              1.193 s   15.77%
+Total init phase time                                1.092 s   14.44%
+Total target pattern evaluation phase time           0.580 s    7.67%
+Total interleaved loading-and-analysis phase time    3.646 s   48.21%
+Total preparation phase time                         0.022 s    0.30%
+Total execution phase time                           0.993 s   13.13%
+Total finish phase time                              0.036 s    0.48%
+---------------------------------------------------------------------
+Total run time                                       7.563 s  100.00%
+
+Critical path (178 ms):
+       Time Percentage   Description
+     178 ms  100.00%   action 'BazelWorkspaceStatusAction stable-status.txt'
+
+`
+	file := filepath.Join(dir, "bazel_metrics.txt")
+	return os.WriteFile(file, []byte(contents), 0666)
+}
+
 func TestPruneMetricsFiles(t *testing.T) {
 	rootDir := t.TempDir()
 
@@ -84,12 +108,12 @@
 	}, {
 		description: "non-existent metrics files no upload",
 		uploader:    "echo",
-		files:       []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+		files:       []string{"metrics_file_1", "metrics_file_2", "metrics_file_3, bazel_metrics.pb"},
 	}, {
 		description: "trigger upload",
 		uploader:    "echo",
 		createFiles: true,
-		files:       []string{"metrics_file_1", "metrics_file_2"},
+		files:       []string{"metrics_file_1", "metrics_file_2, bazel_metrics.pb"},
 	}}
 
 	for _, tt := range tests {
@@ -130,6 +154,9 @@
 					}
 				}
 			}
+			if err := writeBazelProfileFile(outDir); err != nil {
+				t.Fatalf("failed to create bazel profile file in dir: %v", outDir)
+			}
 
 			config := Config{&configImpl{
 				environ: &Environment{
@@ -139,7 +166,7 @@
 				metricsUploader: tt.uploader,
 			}}
 
-			UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
+			UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
 		})
 	}
 }
@@ -194,8 +221,79 @@
 				metricsUploader: "echo",
 			}}
 
-			UploadMetrics(ctx, config, true, time.Now(), metricsFile)
+			UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
 			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
 		})
 	}
 }
+
+func TestParsePercentageToTenThousandths(t *testing.T) {
+	// 2.59% should be returned as 259 - representing 259/10000 of the build
+	percentage := parsePercentageToTenThousandths("2.59%")
+	if percentage != 259 {
+		t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 259, have %d\n", percentage)
+	}
+
+	// Test without a leading digit
+	percentage = parsePercentageToTenThousandths(".52%")
+	if percentage != 52 {
+		t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 52, have %d\n", percentage)
+	}
+}
+
+func TestParseTimingToNanos(t *testing.T) {
+	// This parses from seconds (with millis precision) and returns nanos
+	timingNanos := parseTimingToNanos("0.111")
+	if timingNanos != 111000000 {
+		t.Errorf("Error parsing timing. Expected 111000, have %d\n", timingNanos)
+	}
+
+	// Test without a leading digit
+	timingNanos = parseTimingToNanos(".112")
+	if timingNanos != 112000000 {
+		t.Errorf("Error parsing timing. Expected 112000, have %d\n", timingNanos)
+	}
+}
+
+func TestParsePhaseTiming(t *testing.T) {
+	// Sample lines include:
+	// Total launch phase time   0.011 s    2.59%
+	// Total target pattern evaluation phase time  0.012 s    4.59%
+
+	line1 := "Total launch phase time   0.011 s    2.59%"
+	timing := parsePhaseTiming(line1)
+
+	if timing.GetPhaseName() != "launch" {
+		t.Errorf("Failed to parse phase name. Expected launch, have %s\n", timing.GetPhaseName())
+	} else if timing.GetDurationNanos() != 11000000 {
+		t.Errorf("Failed to parse duration nanos. Expected 11000000, have %d\n", timing.GetDurationNanos())
+	} else if timing.GetPortionOfBuildTime() != 259 {
+		t.Errorf("Failed to parse portion of build time. Expected 259, have %d\n", timing.GetPortionOfBuildTime())
+	}
+
+	// Test with a multiword phase name
+	line2 := "Total target pattern evaluation phase  time  0.012 s    4.59%"
+
+	timing = parsePhaseTiming(line2)
+	if timing.GetPhaseName() != "target pattern evaluation" {
+		t.Errorf("Failed to parse phase name. Expected target pattern evaluation, have %s\n", timing.GetPhaseName())
+	} else if timing.GetDurationNanos() != 12000000 {
+		t.Errorf("Failed to parse duration nanos. Expected 12000000, have %d\n", timing.GetDurationNanos())
+	} else if timing.GetPortionOfBuildTime() != 459 {
+		t.Errorf("Failed to parse portion of build time. Expected 459, have %d\n", timing.GetPortionOfBuildTime())
+	}
+}
+
+func TestParseTotal(t *testing.T) {
+	// Total line is in the form of:
+	// Total run time                                       7.563 s  100.00%
+
+	line := "Total run time                                       7.563 s  100.00%"
+
+	total := parseTotal(line)
+
+	// Only the seconds field is parsed, as nanos
+	if total != 7563000000 {
+		t.Errorf("Failed to parse total build time. Expected 7563000000, have %d\n", total)
+	}
+}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 2301c56..bd1517c 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -22,6 +22,7 @@
     deps: [
         "golang-protobuf-proto",
         "soong-ui-bp2build_metrics_proto",
+        "soong-ui-bazel_metrics_proto",
         "soong-ui-metrics_upload_proto",
         "soong-ui-metrics_proto",
         "soong-ui-mk_metrics_proto",
@@ -73,6 +74,18 @@
 }
 
 bootstrap_go_package {
+    name: "soong-ui-bazel_metrics_proto",
+    pkgPath: "android/soong/ui/metrics/bazel_metrics_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+    srcs: [
+        "bazel_metrics_proto/bazel_metrics.pb.go",
+    ],
+}
+
+bootstrap_go_package {
     name: "soong-ui-mk_metrics_proto",
     pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
     deps: [
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 93f3471..4821043 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.9.1
+// 	protoc-gen-go v1.28.0
+// 	protoc        v3.21.7
 // source: bp2build_metrics.proto
 
 package bp2build_metrics_proto
@@ -45,6 +45,10 @@
 	HandCraftedModuleCount uint64 `protobuf:"varint,2,opt,name=handCraftedModuleCount,proto3" json:"handCraftedModuleCount,omitempty"`
 	// Total number of unconverted Soong modules
 	UnconvertedModuleCount uint64 `protobuf:"varint,3,opt,name=unconvertedModuleCount,proto3" json:"unconvertedModuleCount,omitempty"`
+	// Counts of symlinks in synthetic bazel workspace
+	WorkspaceSymlinkCount uint64 `protobuf:"varint,9,opt,name=workspaceSymlinkCount,proto3" json:"workspaceSymlinkCount,omitempty"`
+	// Counts of mkdir calls during creation of synthetic bazel workspace
+	WorkspaceMkDirCount uint64 `protobuf:"varint,10,opt,name=workspaceMkDirCount,proto3" json:"workspaceMkDirCount,omitempty"`
 	// Counts of generated Bazel targets per Bazel rule class
 	RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
 	// List of converted modules
@@ -111,6 +115,20 @@
 	return 0
 }
 
+func (x *Bp2BuildMetrics) GetWorkspaceSymlinkCount() uint64 {
+	if x != nil {
+		return x.WorkspaceSymlinkCount
+	}
+	return 0
+}
+
+func (x *Bp2BuildMetrics) GetWorkspaceMkDirCount() uint64 {
+	if x != nil {
+		return x.WorkspaceMkDirCount
+	}
+	return 0
+}
+
 func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
 	if x != nil {
 		return x.RuleClassCount
@@ -221,7 +239,7 @@
 	0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
 	0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
 	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xe9, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd1, 0x07, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
 	0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
 	0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
 	0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
@@ -232,60 +250,66 @@
 	0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76,
 	0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
 	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
-	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69,
-	0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43,
-	0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43,
-	0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
-	0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
-	0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
-	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75,
-	0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
-	0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
-	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
-	0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
-	0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32,
-	0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74,
-	0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
-	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06,
-	0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e,
-	0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c,
-	0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d,
+	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34,
+	0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69,
+	0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x77,
+	0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x6b, 0x44, 0x69,
+	0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69, 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c,
+	0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70,
+	0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75,
+	0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72,
+	0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e,
+	0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e,
+	0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01,
+	0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62,
+	0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+	0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
 	0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54,
-	0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74,
-	0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
-	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
-	0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-	0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
-	0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61,
-	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63,
+	0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
+	0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+	0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14,
+	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74,
+	0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f,
+	0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
+	0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+	0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
+	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+	0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76,
+	0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74,
+	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61,
+	0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54,
+	0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+	0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 19a7827..9ff4ae2 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -27,6 +27,12 @@
   // Total number of unconverted Soong modules
   uint64 unconvertedModuleCount = 3;
 
+  // Counts of symlinks in synthetic bazel workspace
+  uint64 workspaceSymlinkCount= 9;
+
+  // Counts of mkdir calls during creation of synthetic bazel workspace
+  uint64 workspaceMkDirCount= 10;
+
   // Counts of generated Bazel targets per Bazel rule class
   map<string, uint64> ruleClassCount = 4;
 
diff --git a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
index 356b188..f6c6df8 100644
--- a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
+++ b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
@@ -22,6 +22,6 @@
 
 py_proto_library(
     name = "bp2build_py_proto",
+    visibility = ["//build/bazel/scripts/bp2build_progress:__pkg__"],
     deps = [":bp2build_proto"],
-    visibility = ["//build/bazel/scripts/bp2build-progress:__pkg__"],
 )
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index cbc73ed..def76aa 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -163,6 +163,7 @@
 	parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
 	cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
 	traceFile := flags.String("trace", "", "write trace to file")
+	sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
 
 	flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
 	flags.Var(&listFiles{}, "l", "file containing list of files to zip")
@@ -224,6 +225,7 @@
 		WriteIfChanged:           *writeIfChanged,
 		StoreSymlinks:            *symlinks,
 		IgnoreMissingFiles:       *ignoreMissingFiles,
+		Sha256Checksum:           *sha256Checksum,
 	})
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index 955fe68..6f1a8ad 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -17,8 +17,11 @@
 import (
 	"bytes"
 	"compress/flate"
+	"crypto/sha256"
+	"encoding/binary"
 	"errors"
 	"fmt"
+	"hash"
 	"hash/crc32"
 	"io"
 	"io/ioutil"
@@ -38,6 +41,14 @@
 	"android/soong/third_party/zip"
 )
 
+// Sha256HeaderID is a custom Header ID for the `extra` field in
+// the file header to store the SHA checksum.
+const Sha256HeaderID = 0x4967
+
+// Sha256HeaderSignature is the signature to verify that the extra
+// data block is used to store the SHA checksum.
+const Sha256HeaderSignature = 0x9514
+
 // Block size used during parallel compression of a single file.
 const parallelBlockSize = 1 * 1024 * 1024 // 1MB
 
@@ -231,6 +242,8 @@
 
 	stderr io.Writer
 	fs     pathtools.FileSystem
+
+	sha256Checksum bool
 }
 
 type zipEntry struct {
@@ -257,6 +270,7 @@
 	WriteIfChanged           bool
 	StoreSymlinks            bool
 	IgnoreMissingFiles       bool
+	Sha256Checksum           bool
 
 	Stderr     io.Writer
 	Filesystem pathtools.FileSystem
@@ -280,6 +294,7 @@
 		ignoreMissingFiles: args.IgnoreMissingFiles,
 		stderr:             args.Stderr,
 		fs:                 args.Filesystem,
+		sha256Checksum:     args.Sha256Checksum,
 	}
 
 	if z.fs == nil {
@@ -782,15 +797,17 @@
 		// this based on actual buffer sizes in RateLimit.
 		ze.futureReaders = make(chan chan io.Reader, (fileSize/parallelBlockSize)+1)
 
-		// Calculate the CRC in the background, since reading the entire
-		// file could take a while.
+		// Calculate the CRC and SHA256 in the background, since reading
+		// the entire file could take a while.
 		//
 		// We could split this up into chunks as well, but it's faster
 		// than the compression. Due to the Go Zip API, we also need to
 		// know the result before we can begin writing the compressed
 		// data out to the zipfile.
+		//
+		// We calculate SHA256 only if `-sha256` is set.
 		wg.Add(1)
-		go z.crcFile(r, ze, compressChan, wg)
+		go z.checksumFileAsync(r, ze, compressChan, wg)
 
 		for start := int64(0); start < fileSize; start += parallelBlockSize {
 			sr := io.NewSectionReader(r, start, parallelBlockSize)
@@ -829,20 +846,53 @@
 	return nil
 }
 
-func (z *ZipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *ZipWriter) checksumFileAsync(r io.ReadSeeker, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
 	defer wg.Done()
 	defer z.cpuRateLimiter.Finish()
 
+	z.checksumFile(r, ze)
+
+	resultChan <- ze
+	close(resultChan)
+}
+
+func (z *ZipWriter) checksumFile(r io.ReadSeeker, ze *zipEntry) {
 	crc := crc32.NewIEEE()
-	_, err := io.Copy(crc, r)
+	writers := []io.Writer{crc}
+
+	var shaHasher hash.Hash
+	if z.sha256Checksum && !ze.fh.Mode().IsDir() {
+		shaHasher = sha256.New()
+		writers = append(writers, shaHasher)
+	}
+
+	w := io.MultiWriter(writers...)
+
+	_, err := io.Copy(w, r)
 	if err != nil {
 		z.errors <- err
 		return
 	}
 
 	ze.fh.CRC32 = crc.Sum32()
-	resultChan <- ze
-	close(resultChan)
+	if shaHasher != nil {
+		z.appendSHAToExtra(ze, shaHasher.Sum(nil))
+	}
+}
+
+func (z *ZipWriter) appendSHAToExtra(ze *zipEntry, checksum []byte) {
+	// The block of SHA256 checksum consist of:
+	// - Header ID, equals to Sha256HeaderID (2 bytes)
+	// - Data size (2 bytes)
+	// - Data block:
+	//   - Signature, equals to Sha256HeaderSignature (2 bytes)
+	//   - Data, SHA checksum value
+	var buf []byte
+	buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderID)
+	buf = binary.LittleEndian.AppendUint16(buf, uint16(len(checksum)+2))
+	buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderSignature)
+	buf = append(buf, checksum...)
+	ze.fh.Extra = append(ze.fh.Extra, buf...)
 }
 
 func (z *ZipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
@@ -894,17 +944,9 @@
 }
 
 func (z *ZipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
+	z.checksumFile(r, ze)
 
-	crc := crc32.NewIEEE()
-	_, err := io.Copy(crc, r)
-	if err != nil {
-		z.errors <- err
-		return
-	}
-
-	ze.fh.CRC32 = crc.Sum32()
-
-	_, err = r.Seek(0, 0)
+	_, err := r.Seek(0, 0)
 	if err != nil {
 		z.errors <- err
 		return
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c4832dc..e7fdea8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"bytes"
+	"encoding/hex"
 	"hash/crc32"
 	"io"
 	"os"
@@ -35,6 +36,10 @@
 	fileEmpty    = []byte("")
 	fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
 
+	sha256FileA = "d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6"
+	sha256FileB = "430c56c5818e62bcb6d478901ef86284e97714c138f3c86aa14fd6a84b7ce5d3"
+	sha256FileC = "31c5ab6111f1d6aa13c2c4e92bb3c0f7c76b61b42d141af1e846eb7f6586a51c"
+
 	fileCustomManifest  = []byte("Custom manifest: true\n")
 	customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
 )
@@ -67,6 +72,20 @@
 	}
 }
 
+func fhWithSHA256(name string, contents []byte, method uint16, sha256 string) zip.FileHeader {
+	h := fh(name, contents, method)
+	// The extra field contains 38 bytes, including 2 bytes of header ID, 2 bytes
+	// of size, 2 bytes of signature, and 32 bytes of checksum data block.
+	var extra [38]byte
+	// The first 6 bytes contains Sha256HeaderID (0x4967), size (unit(34)) and
+	// Sha256HeaderSignature (0x9514)
+	copy(extra[0:], []byte{103, 73, 34, 0, 20, 149})
+	sha256Bytes, _ := hex.DecodeString(sha256)
+	copy(extra[6:], sha256Bytes)
+	h.Extra = append(h.Extra, extra[:]...)
+	return h
+}
+
 func fhManifest(contents []byte) zip.FileHeader {
 	return zip.FileHeader{
 		Name:               "META-INF/MANIFEST.MF",
@@ -87,13 +106,18 @@
 	}
 }
 
-func fhDir(name string) zip.FileHeader {
+type fhDirOptions struct {
+	extra []byte
+}
+
+func fhDir(name string, opts fhDirOptions) zip.FileHeader {
 	return zip.FileHeader{
 		Name:               name,
 		Method:             zip.Store,
 		CRC32:              crc32.ChecksumIEEE(nil),
 		UncompressedSize64: 0,
 		ExternalAttrs:      (syscall.S_IFDIR|0755)<<16 | 0x10,
+		Extra:              opts.extra,
 	}
 }
 
@@ -114,6 +138,7 @@
 		manifest           string
 		storeSymlinks      bool
 		ignoreMissingFiles bool
+		sha256Checksum     bool
 
 		files []zip.FileHeader
 		err   error
@@ -320,10 +345,10 @@
 			emulateJar:       true,
 
 			files: []zip.FileHeader{
-				fhDir("META-INF/"),
+				fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
 				fhManifest(fileManifest),
-				fhDir("a/"),
-				fhDir("a/a/"),
+				fhDir("a/", fhDirOptions{}),
+				fhDir("a/a/", fhDirOptions{}),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 			},
@@ -338,10 +363,10 @@
 			manifest:         "manifest.txt",
 
 			files: []zip.FileHeader{
-				fhDir("META-INF/"),
+				fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
 				fhManifest(customManifestAfter),
-				fhDir("a/"),
-				fhDir("a/a/"),
+				fhDir("a/", fhDirOptions{}),
+				fhDir("a/a/", fhDirOptions{}),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 			},
@@ -355,8 +380,8 @@
 			dirEntries:       true,
 
 			files: []zip.FileHeader{
-				fhDir("a/"),
-				fhDir("a/a/"),
+				fhDir("a/", fhDirOptions{}),
+				fhDir("a/a/", fhDirOptions{}),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 			},
@@ -412,6 +437,23 @@
 				fh("a/a/a", fileA, zip.Deflate),
 			},
 		},
+		{
+			name: "generate SHA256 checksum",
+			args: fileArgsBuilder().
+				File("a/a/a").
+				File("a/a/b").
+				File("a/a/c").
+				File("c"),
+			compressionLevel: 9,
+			sha256Checksum:   true,
+
+			files: []zip.FileHeader{
+				fhWithSHA256("a/a/a", fileA, zip.Deflate, sha256FileA),
+				fhWithSHA256("a/a/b", fileB, zip.Deflate, sha256FileB),
+				fhWithSHA256("a/a/c", fileC, zip.Deflate, sha256FileC),
+				fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
+			},
+		},
 
 		// errors
 		{
@@ -465,6 +507,7 @@
 			args.ManifestSourcePath = test.manifest
 			args.StoreSymlinks = test.storeSymlinks
 			args.IgnoreMissingFiles = test.ignoreMissingFiles
+			args.Sha256Checksum = test.sha256Checksum
 			args.Filesystem = mockFs
 			args.Stderr = &bytes.Buffer{}
 
@@ -555,6 +598,11 @@
 					t.Errorf("incorrect file %s method want %v got %v", want.Name,
 						want.Method, got.Method)
 				}
+
+				if !bytes.Equal(want.Extra, got.Extra) {
+					t.Errorf("incorrect file %s extra want %v got %v", want.Name,
+						want.Extra, got.Extra)
+				}
 			}
 		})
 	}