Merge "Support trimmed variant build in soong"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 87554a2..0c1be6e 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -669,6 +669,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 +1325,6 @@
 		"libc_musl_sysroot",
 	}
 
-	Bp2buildCcLibraryStaticOnlyList = []string{}
-
 	MixedBuildsDisabledList = []string{
 		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
 
@@ -1379,5 +1384,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/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..4a495f0 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
 }
 
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 1ecb0af..e151521 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -496,13 +496,15 @@
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	relativeRootPath := filepath.Join("execroot", "__main__", relativeRoot)
-	if pathComponents := strings.Split(path, "/"); len(pathComponents) >= 3 &&
+	var relativeRootPath string
+	if pathComponents := strings.SplitN(path, "/", 4); len(pathComponents) >= 3 &&
 		pathComponents[0] == "bazel-out" && pathComponents[2] == "bin" {
 		// If the path starts with something like: bazel-out/linux_x86_64-fastbuild-ST-b4ef1c4402f9/bin/
 		// make it relative to that folder. bazel-out/volatile-status.txt is an example
 		// of something that starts with bazel-out but is not relative to the bin folder
 		relativeRootPath = filepath.Join("execroot", "__main__", pathComponents[0], pathComponents[1], pathComponents[2], relativeRoot)
+	} else {
+		relativeRootPath = filepath.Join("execroot", "__main__", relativeRoot)
 	}
 
 	var relPath string
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 32131fc..087bae1 100644
--- a/android/config.go
+++ b/android/config.go
@@ -583,32 +583,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
diff --git a/android/mutator.go b/android/mutator.go
index d92b87c..4e55609 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -409,7 +409,7 @@
 
 	return &bottomUpMutatorContext{
 		bp:                ctx,
-		baseModuleContext: a.base().baseModuleContextFactory(ctx),
+		baseModuleContext: moduleContext,
 		finalPhase:        finalPhase,
 	}
 }
diff --git a/android/sdk_version.go b/android/sdk_version.go
index d73c912..8953eae 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -51,6 +51,7 @@
 	SdkModule
 	SdkSystemServer
 	SdkPrivate
+	SdkToolchain // API surface provided by ART to compile other API domains
 )
 
 // String returns the string representation of this SdkKind
@@ -76,6 +77,8 @@
 		return "module-lib"
 	case SdkSystemServer:
 		return "system-server"
+	case SdkToolchain:
+		return "toolchain"
 	default:
 		return "invalid"
 	}
diff --git a/android/variable.go b/android/variable.go
index 27c5ef1..e714fc4 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -704,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 {
@@ -947,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
@@ -974,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..aadccb7 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -317,14 +317,14 @@
 func (a *apexBundle) androidMkForType() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			moduleNames := []string{}
 			apexType := a.properties.ApexType
-			if a.installable() {
-				apexName := proptools.StringDefault(a.properties.Apex_name, name)
-				moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
-			}
 
 			if apexType == flattenedApex {
+				var moduleNames []string = nil
+				if a.installable() {
+					apexName := proptools.StringDefault(a.properties.Apex_name, name)
+					moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
+				}
 				// Only image APEXes can be flattened.
 				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle.flat")
 				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
@@ -366,7 +366,7 @@
 				}
 
 				android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
-				a.writeRequiredModules(w, moduleNames)
+				a.writeRequiredModules(w, nil)
 
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 
diff --git a/apex/apex.go b/apex/apex.go
index 2e2c2cb..1a13986 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1708,7 +1708,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)
@@ -1956,9 +1956,15 @@
 		return
 	}
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
-	a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedOutput)
-	a.outputFile = a.outputApexFile
+
+	// Set the output file to .apex or .capex depending on the compression configuration.
 	a.setCompression(ctx)
+	if a.isCompressed {
+		a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedCompressedOutput)
+	} else {
+		a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedOutput)
+	}
+	a.outputFile = a.outputApexFile
 
 	// TODO(b/257829940): These are used by the apex_keys_text singleton; would probably be a clearer
 	// interface if these were set in a provider rather than the module itself
@@ -2192,7 +2198,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))
 				}
@@ -3470,7 +3476,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
@@ -3489,6 +3495,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
@@ -3527,11 +3537,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
+	var minSdkVersion bazel.StringAttribute
 	if a.properties.Min_sdk_version != nil {
-		minSdkVersion = a.properties.Min_sdk_version
+		minSdkVersion.SetValue(*a.properties.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..395da95 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -526,7 +526,6 @@
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
 
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
 
 	optFlags := apexRule.Args["opt_flags"]
@@ -2996,7 +2995,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex libc.vendor libm.vendor libdl.vendor\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc.vendor libm.vendor libdl.vendor\n")
 }
 
 func TestAndroidMkWritesCommonProperties(t *testing.T) {
@@ -3329,17 +3328,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 +3344,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) {
@@ -4153,7 +4147,6 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
 }
 
@@ -5687,12 +5680,6 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
 
 	flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
@@ -5726,7 +5713,7 @@
 	var builder strings.Builder
 	mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex myapex.flattened\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := myapex.flattened\n")
 }
 
 func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
@@ -6519,12 +6506,6 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
@@ -7116,7 +7097,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex a b\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := a b\n")
 	ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES := c d\n")
 	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES := e f\n")
 }
@@ -7287,6 +7268,9 @@
 				"myapex",
 				"//apex_available:platform",
 			],
+			stubs: {
+				versions: ["current"],
+			},
 		}
 	`)
 
@@ -7296,11 +7280,10 @@
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
 	// `myotherlib` is added to `myapex` as symlink
-	ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
 	// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := myotherlib\n")
 }
 
 func TestApexWithJniLibs(t *testing.T) {
@@ -8813,7 +8796,7 @@
 
 	// The make level dependency needs to be on otherlib - prebuilt_otherlib isn't
 	// a thing there.
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex otherlib\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := otherlib\n")
 }
 
 func TestExcludeDependency(t *testing.T) {
@@ -9207,7 +9190,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
 }
 
 func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) {
@@ -9283,7 +9266,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex otherapex")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := otherapex")
 }
 
 func TestAndroidMk_RequiredDeps(t *testing.T) {
@@ -9307,7 +9290,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo\n")
 
 	flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
 	flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 58f30bd..01afa52 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -42,15 +42,16 @@
 				OutputBaseDir: outputBaseDir,
 				LabelToApexInfo: map[string]cquery.ApexInfo{
 					"//:foo": cquery.ApexInfo{
-						SignedOutput:          "signed_out.apex",
-						UnsignedOutput:        "unsigned_out.apex",
-						BundleKeyInfo:         []string{"public_key", "private_key"},
-						ContainerKeyInfo:      []string{"container_cert", "container_private"},
-						SymbolsUsedByApex:     "foo_using.txt",
-						JavaSymbolsUsedByApex: "foo_using.xml",
-						BundleFile:            "apex_bundle.zip",
-						InstalledFiles:        "installed-files.txt",
-						RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+						SignedOutput:           "signed_out.apex",
+						SignedCompressedOutput: "signed_out.capex",
+						UnsignedOutput:         "unsigned_out.apex",
+						BundleKeyInfo:          []string{"public_key", "private_key"},
+						ContainerKeyInfo:       []string{"container_cert", "container_private"},
+						SymbolsUsedByApex:      "foo_using.txt",
+						JavaSymbolsUsedByApex:  "foo_using.xml",
+						BundleFile:             "apex_bundle.zip",
+						InstalledFiles:         "installed-files.txt",
+						RequiresLibs:           []string{"//path/c:c", "//path/d:d"},
 
 						// unused
 						PackageName:  "pkg_name",
@@ -115,6 +116,68 @@
 	}
 }
 
+func TestCompressedApexImageInMixedBuilds(t *testing.T) {
+	bp := `
+apex_key{
+	name: "foo_key",
+}
+apex {
+	name: "foo",
+	key: "foo_key",
+	updatable: true,
+	min_sdk_version: "31",
+	file_contexts: ":myapex-file_contexts",
+	bazel_module: { label: "//:foo" },
+	test_only_force_compression: true, // force compression
+}`
+
+	outputBaseDir := "out/bazel"
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: outputBaseDir,
+				LabelToApexInfo: map[string]cquery.ApexInfo{
+					"//:foo": cquery.ApexInfo{
+						SignedOutput:           "signed_out.apex",
+						SignedCompressedOutput: "signed_out.capex",
+						BundleKeyInfo:          []string{"public_key", "private_key"},
+						ContainerKeyInfo:       []string{"container_cert", "container_private"},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+
+	m := result.ModuleForTests("foo", "android_common_foo_image").Module()
+	ab, ok := m.(*apexBundle)
+	if !ok {
+		t.Fatalf("Expected module to be an apexBundle, was not")
+	}
+
+	if w, g := "out/bazel/execroot/__main__/signed_out.capex", ab.outputFile.String(); w != g {
+		t.Errorf("Expected output file to be compressed apex %q, got %q", w, g)
+	}
+
+	mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+	var builder strings.Builder
+	mkData.Custom(&builder, "foo", "BAZEL_TARGET_", "", mkData)
+
+	data := builder.String()
+
+	expectedAndroidMk := []string{
+		"LOCAL_PREBUILT_MODULE_FILE := out/bazel/execroot/__main__/signed_out.capex",
+
+		// Check that the source install file is the capex. The dest is not important.
+		"LOCAL_SOONG_INSTALL_PAIRS := out/bazel/execroot/__main__/signed_out.capex:",
+	}
+	for _, androidMk := range expectedAndroidMk {
+		if !strings.Contains(data, androidMk) {
+			t.Errorf("Expected %q in androidmk data, but did not find %q", androidMk, data)
+		}
+	}
+}
+
 func TestOverrideApexImageInMixedBuilds(t *testing.T) {
 	bp := `
 apex_key{
diff --git a/apex/builder.go b/apex/builder.go
index 984e7f0..4331d3e 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -1181,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/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 7c9ae3b..81c60d9 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -232,8 +232,14 @@
   fail("%s did not provide ApexInfo" % id_string)
 bundle_key_info = info.bundle_key_info
 container_key_info = info.container_key_info
+
+signed_compressed_output = "" # no .capex if the apex is not compressible, cannot be None as it needs to be json encoded.
+if info.signed_compressed_output:
+    signed_compressed_output = info.signed_compressed_output.path
+
 return json_encode({
     "signed_output": info.signed_output.path,
+    "signed_compressed_output": signed_compressed_output,
     "unsigned_output": info.unsigned_output.path,
     "provides_native_libs": [str(lib) for lib in info.provides_native_libs],
     "requires_native_libs": [str(lib) for lib in info.requires_native_libs],
@@ -249,18 +255,19 @@
 }
 
 type ApexInfo struct {
-	SignedOutput          string   `json:"signed_output"`
-	UnsignedOutput        string   `json:"unsigned_output"`
-	ProvidesLibs          []string `json:"provides_native_libs"`
-	RequiresLibs          []string `json:"requires_native_libs"`
-	BundleKeyInfo         []string `json:"bundle_key_info"`
-	ContainerKeyInfo      []string `json:"container_key_info"`
-	PackageName           string   `json:"package_name"`
-	SymbolsUsedByApex     string   `json:"symbols_used_by_apex"`
-	JavaSymbolsUsedByApex string   `json:"java_symbols_used_by_apex"`
-	BackingLibs           string   `json:"backing_libs"`
-	BundleFile            string   `json:"bundle_file"`
-	InstalledFiles        string   `json:"installed_files"`
+	SignedOutput           string   `json:"signed_output"`
+	SignedCompressedOutput string   `json:"signed_compressed_output"`
+	UnsignedOutput         string   `json:"unsigned_output"`
+	ProvidesLibs           []string `json:"provides_native_libs"`
+	RequiresLibs           []string `json:"requires_native_libs"`
+	BundleKeyInfo          []string `json:"bundle_key_info"`
+	ContainerKeyInfo       []string `json:"container_key_info"`
+	PackageName            string   `json:"package_name"`
+	SymbolsUsedByApex      string   `json:"symbols_used_by_apex"`
+	JavaSymbolsUsedByApex  string   `json:"java_symbols_used_by_apex"`
+	BackingLibs            string   `json:"backing_libs"`
+	BundleFile             string   `json:"bundle_file"`
+	InstalledFiles         string   `json:"installed_files"`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
diff --git a/bazel/properties.go b/bazel/properties.go
index 76450dc..9be21eb 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -152,7 +152,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...)
 	}
 }
 
@@ -814,6 +814,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 +888,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 +1090,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 +1105,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 +1134,7 @@
 		return sa.Value
 	case arch, os, osArch, productVariables:
 		if v, ok := sa.ConfigurableValues[axis][config]; ok {
-			return &v
+			return v
 		} else {
 			return nil
 		}
@@ -1154,7 +1165,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 +1192,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 +1200,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
 }
@@ -1367,6 +1393,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..244ca9c 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",
@@ -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/configurability.go b/bp2build/configurability.go
index 987c903..4244956 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}
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/bp2build.go b/cc/bp2build.go
index d331d89..35419af 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
 			}
 		}
 	}
@@ -703,49 +703,49 @@
 	linkerAttrs := linkerAttributes{}
 
 	for axis, configs := range axisToConfigs {
-		for config, _ := range configs {
+		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 +813,8 @@
 	features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
 	features.DeduplicateAxesFromBase()
 
+	addMuslSystemDynamicDeps(ctx, linkerAttrs)
+
 	return baseAttributes{
 		compilerAttrs,
 		linkerAttrs,
@@ -823,6 +825,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 +979,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 +1028,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 +1067,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 +1211,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 +1350,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..b194360 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1859,6 +1859,10 @@
 var (
 	mixedBuildSupportedCcTest = []string{
 		"adbd_test",
+		"adb_crypto_test",
+		"adb_pairing_auth_test",
+		"adb_pairing_connection_test",
+		"adb_tls_connection_test",
 	}
 )
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 6dfd395..8293f2d 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4483,6 +4483,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..1291f5c 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")
 					}
@@ -1034,7 +1026,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 +1239,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 +1896,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 +1905,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 +1916,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_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/sanitize.go b/cc/sanitize.go
index 8e2d161..c61e5e4 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -78,6 +78,9 @@
 	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
 		"export_memory_stats=0", "max_malloc_fill_size=4096", "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..4b968dc 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)
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/filesystem/Android.bp b/filesystem/Android.bp
index dfcd405..07d57c9 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -13,6 +13,7 @@
     ],
     srcs: [
         "avb_add_hash_footer.go",
+        "avb_gen_vbmeta_image.go",
         "bootimg.go",
         "filesystem.go",
         "logical_partition.go",
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index 2ee420c..f3fecd0 100644
--- a/filesystem/avb_add_hash_footer.go
+++ b/filesystem/avb_add_hash_footer.go
@@ -67,6 +67,9 @@
 
 	// List of properties to add to the footer
 	Props []avbProp
+
+	// Include descriptors from images
+	Include_descriptors_from_images []string `android:"path,arch_variant"`
 }
 
 // The AVB footer adds verification information to the image.
@@ -116,6 +119,11 @@
 	}
 	cmd.FlagWithArg("--salt ", proptools.String(a.properties.Salt))
 
+	imagePaths := android.PathsForModuleSrc(ctx, a.properties.Include_descriptors_from_images)
+	for _, imagePath := range imagePaths {
+		cmd.FlagWithInput("--include_descriptors_from_image ", imagePath)
+	}
+
 	for _, prop := range a.properties.Props {
 		addAvbProp(ctx, cmd, prop)
 	}
diff --git a/filesystem/avb_gen_vbmeta_image.go b/filesystem/avb_gen_vbmeta_image.go
new file mode 100644
index 0000000..0f331f9
--- /dev/null
+++ b/filesystem/avb_gen_vbmeta_image.go
@@ -0,0 +1,108 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+	"fmt"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+type avbGenVbmetaImage struct {
+	android.ModuleBase
+
+	properties avbGenVbmetaImageProperties
+
+	output     android.OutputPath
+	installDir android.InstallPath
+}
+
+type avbGenVbmetaImageProperties struct {
+	// Source file of this image. Can reference a genrule type module with the ":module" syntax.
+	Src *string `android:"path,arch_variant"`
+
+	// Name of the image partition. Defaults to the name of this module.
+	Partition_name *string
+
+	// The salt in hex. Required for reproducible builds.
+	Salt *string
+}
+
+// The avbGenVbmetaImage generates an unsigned VBMeta image output for the given image.
+func avbGenVbmetaImageFactory() android.Module {
+	module := &avbGenVbmetaImage{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+func (a *avbGenVbmetaImage) installFileName() string {
+	return a.Name() + ".img"
+}
+
+func (a *avbGenVbmetaImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	builder := android.NewRuleBuilder(pctx, ctx)
+	cmd := builder.Command().BuiltTool("avbtool").Text("add_hash_footer")
+	cmd.Flag("--dynamic_partition_size")
+	cmd.Flag("--do_not_append_vbmeta_image")
+
+	partition_name := proptools.StringDefault(a.properties.Partition_name, a.Name())
+	cmd.FlagWithArg("--partition_name ", partition_name)
+
+	if a.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing source file")
+		return
+	}
+	input := android.PathForModuleSrc(ctx, proptools.String(a.properties.Src))
+	cmd.FlagWithInput("--image ", input)
+
+	if a.properties.Salt == nil {
+		ctx.PropertyErrorf("salt", "missing salt value")
+		return
+	}
+	cmd.FlagWithArg("--salt ", proptools.String(a.properties.Salt))
+
+	a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
+	cmd.FlagWithOutput("--output_vbmeta_image ", a.output)
+	builder.Build("avbGenVbmetaImage", fmt.Sprintf("avbGenVbmetaImage %s", ctx.ModuleName()))
+}
+
+var _ android.AndroidMkEntriesProvider = (*avbGenVbmetaImage)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (a *avbGenVbmetaImage) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(a.output),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", a.installDir.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", a.installFileName())
+			},
+		},
+	}}
+}
+
+var _ android.OutputFileProducer = (*avbGenVbmetaImage)(nil)
+
+// Implements android.OutputFileProducer
+func (a *avbGenVbmetaImage) OutputFiles(tag string) (android.Paths, error) {
+	if tag == "" {
+		return []android.Path{a.output}, nil
+	}
+	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index c73c4a4..25b8fe8 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -35,6 +35,7 @@
 	ctx.RegisterModuleType("android_filesystem", filesystemFactory)
 	ctx.RegisterModuleType("android_system_image", systemImageFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
+	ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory)
 }
 
 type filesystem struct {
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 9bfcc3d..444ffd0 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -126,8 +126,34 @@
 	android.AssertDeepEquals(t, "entries should have foo only", []string{"components/foo"}, module.entries)
 }
 
+func TestAvbGenVbmetaImage(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		avb_gen_vbmeta_image {
+			name: "input_hashdesc",
+			src: "input.img",
+			partition_name: "input_partition_name",
+			salt: "2222",
+		}`)
+	cmd := result.ModuleForTests("input_hashdesc", "android_arm64_armv8-a").Rule("avbGenVbmetaImage").RuleParams.Command
+	android.AssertStringDoesContain(t, "Can't find correct --partition_name argument",
+		cmd, "--partition_name input_partition_name")
+	android.AssertStringDoesContain(t, "Can't find --do_not_append_vbmeta_image",
+		cmd, "--do_not_append_vbmeta_image")
+	android.AssertStringDoesContain(t, "Can't find --output_vbmeta_image",
+		cmd, "--output_vbmeta_image ")
+	android.AssertStringDoesContain(t, "Can't find --salt argument",
+		cmd, "--salt 2222")
+}
+
 func TestAvbAddHashFooter(t *testing.T) {
 	result := fixture.RunTestWithBp(t, `
+		avb_gen_vbmeta_image {
+			name: "input_hashdesc",
+			src: "input.img",
+			partition_name: "input",
+			salt: "2222",
+		}
+
 		avb_add_hash_footer {
 			name: "myfooter",
 			src: "input.img",
@@ -145,6 +171,7 @@
 					file: "value_file",
 				},
 			],
+			include_descriptors_from_images: ["input_hashdesc"],
 		}
 	`)
 	cmd := result.ModuleForTests("myfooter", "android_arm64_armv8-a").Rule("avbAddHashFooter").RuleParams.Command
@@ -158,4 +185,6 @@
 		cmd, "--prop 'prop1:value1'")
 	android.AssertStringDoesContain(t, "Can't find --prop_from_file argument",
 		cmd, "--prop_from_file 'prop2:value_file'")
+	android.AssertStringDoesContain(t, "Can't find --include_descriptors_from_image",
+		cmd, "--include_descriptors_from_image ")
 }
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/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/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..b6fe109 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/java/config"
 	"android/soong/remoteexec"
 )
 
@@ -89,7 +90,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 +134,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 +181,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,12 +254,37 @@
 	})
 
 	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...)
+	r8Flags = append(r8Flags, flags.processorPath.FormJavaClassPath("-libraryjars"))
+	r8Deps = append(r8Deps, flags.processorPath...)
+
+	errorProneClasspath := classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
+	r8Flags = append(r8Flags, errorProneClasspath.FormJavaClassPath("-libraryjars"))
+	r8Deps = append(r8Deps, errorProneClasspath...)
+
+	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 +372,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 +386,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/droidstubs.go b/java/droidstubs.go
index 066f0d6..7ea8d30 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)
@@ -877,6 +902,7 @@
 		"module_lib":    android.SdkModule,
 		"module-lib":    android.SdkModule,
 		"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..04a112c 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -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/jdeps.go b/java/jdeps.go
index 3734335..a52b867 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -52,6 +52,11 @@
 			return
 		}
 
+		// Prevent including both prebuilts and matching source modules when one replaces the other.
+		if !android.IsModulePreferred(module) {
+			return
+		}
+
 		ideInfoProvider, ok := module.(android.IDEInfo)
 		if !ok {
 			return
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..4584f1e 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -11,11 +11,10 @@
         "soong-tradefed",
     ],
     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..95eb2c6 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,11 +18,12 @@
 
 import (
 	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
 
 	"android/soong/android"
-	"android/soong/bazel"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -33,63 +34,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,52 +62,61 @@
 	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) {
+	depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx)
 	main := ""
-	if binary.autorun() {
-		main = binary.getPyMainFile(ctx, srcsPathMappings)
+	if p.autorun() {
+		main = p.getPyMainFile(ctx, p.srcsPathMappings)
 	}
 
 	var launcherPath android.OptionalPath
+	embeddedLauncher := p.isEmbeddedLauncherEnabled()
 	if embeddedLauncher {
 		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
 			if provider, ok := m.(IntermPathProvider); ok {
@@ -175,15 +128,137 @@
 			}
 		})
 	}
-	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
-		binary.getHostInterpreterName(ctx, actualVersion),
-		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+	p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+		p.getHostInterpreterName(ctx, p.properties.Actual_version),
+		main, p.getStem(ctx), append(android.Paths{p.srcsZip}, depsSrcsZips...))
 
-	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)
+
+	versionVariation := []blueprint.Variation{
+		{"python_version", p.properties.Actual_version},
+	}
+
+	// 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.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.autorun() {
+				launcherModule = "py2-launcher-autorun"
+			}
+
+			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
+
+		case pyVersion3:
+			stdLib = "py3-stdlib"
+
+			launcherModule = "py3-launcher"
+			if p.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...)
+	}
+}
+
+// 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 +275,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 +294,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/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..2b71e83 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
@@ -173,110 +148,16 @@
 
 	// the zip filepath for zipping current module 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
 }
 
 // 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 {
@@ -286,37 +167,31 @@
 }
 
 // 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)
+func (p *PythonLibraryModule) getBaseProperties() *BaseProperties {
+	return &p.properties
+}
 
-var _ android.AndroidMkEntriesProvider = (*Module)(nil)
+var _ pythonDependency = (*PythonLibraryModule)(nil)
 
-func (p *Module) init(additionalProps ...interface{}) android.Module {
+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
 }
 
@@ -350,24 +225,29 @@
 	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 +256,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 +268,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 +278,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 +286,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 +301,15 @@
 	// 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...)
 }
 
-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)
-	}
-
-	// 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))
-		}
-
-		p.installer.setAndroidMkSharedLibs(sharedLibs)
-
-		// Install the par file from installSource
-		if p.installSource.Valid() {
-			p.installer.install(ctx, p.installSource.Path())
-		}
-	}
-}
-
-// 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)
@@ -607,7 +360,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,7 +395,7 @@
 }
 
 // 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...)
 
@@ -654,13 +407,8 @@
 		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)
 		}
 	}
 	var zips android.Paths
@@ -736,30 +484,20 @@
 	}
 }
 
-// isPythonLibModule returns whether the given module is a Python library Module or not
+// 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) android.Paths {
 	// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
 	// check duplicates.
 	destToPySrcs := make(map[string]string)
@@ -773,6 +511,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 +541,11 @@
 				checkForDuplicateOutputPath(ctx, destToPyData,
 					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
 			}
-			p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
+			result = append(result, dep.getSrcsZip())
 		}
 		return true
 	})
+	return result
 }
 
 // chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
@@ -825,18 +566,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..6f4223a 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -312,10 +312,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",
-					},
 				},
 			},
 		},
@@ -346,17 +342,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 +365,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/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/rust/bindgen.go b/rust/bindgen.go
index 17d80dd..1ad33a1 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -15,7 +15,6 @@
 package rust
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -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..26e2d06 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"
 	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/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
new file mode 100755
index 0000000..97e6576
--- /dev/null
+++ b/tests/apex_cc_module_arch_variant_tests.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -uo pipefail
+
+# Integration test for verifying arch variant cflags set on cc modules included
+# in Bazel-built apexes in the real source tree.
+
+if [ ! -e "build/make/core/Makefile" ]; then
+  echo "$0 must be run from the top of the Android source tree."
+  exit 1
+fi
+
+############
+# Test Setup
+############
+
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
+
+export TARGET_PRODUCT="aosp_arm64"
+[ "$#" -ge 1 ] && export TARGET_PRODUCT="$1"
+ARCH_VARIANT_CFLAG="armv8-a"
+[ "$#" -ge 2 ] && ARCH_VARIANT_CFLAG="$2"
+CPU_VARIANT_CFLAG=""
+[ "$#" -ge 3 ] && CPU_VARIANT_CFLAG="$3"
+
+function call_bazel() {
+  build/bazel/bin/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
+}
+
+function cleanup {
+  # call bazel clean because some bazel outputs don't have w bits.
+  call_bazel clean
+  rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+######################
+# Run bp2build / Bazel
+######################
+build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
+
+# 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")
+
+# Number of all CppCompile actions
+all_cppcompile_actions_num=0
+aquery_summary=$(call_bazel aquery --config=bp2build --config=ci --config=android --output=summary \
+  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' \
+  | egrep -o '.*opt-ST.*: ([0-9]+)$' \
+  | cut -d: -f2 -)
+
+while read -r num;
+do
+  all_cppcompile_actions_num=$(($all_cppcompile_actions_num + $num))
+done <<< "$aquery_summary"
+
+if [ $actions_with_arch_variant_num -eq $all_cppcompile_actions_num ]
+then
+  echo "Pass: arch variant is set."
+else
+  echo "Error: number of CppCompile actions with arch variant set: actual=$actions_with_arch_variant_num, expected=$all_cppcompile_actions_num"
+  exit 1
+fi
+
+if [ $CPU_VARIANT_CFLAG ]
+then
+  # Number of CppCompiler actions with cpu variant flag
+  actions_with_cpu_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 "\-mcpu=$CPU_VARIANT_CFLAG")
+
+  if [ $actions_with_cpu_variant_num -eq $all_cppcompile_actions_num ]
+  then
+    echo "Pass: cpu variant is set."
+  else
+    echo "Error: number of CppCompile actions with cpu variant set: actual=$actions_with_cpu_variant_num, expected=$all_cppcompile_actions_num"
+    exit 1
+  fi
+fi
\ No newline at end of file
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/run_integration_tests.sh b/tests/run_integration_tests.sh
index 7a71b27..eb76a46 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -14,3 +14,7 @@
 # mock client.
 "$TOP/build/soong/tests/apex_comparison_tests.sh"
 "$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
+
+"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
+"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
+"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
\ No newline at end of file
diff --git a/ui/build/config.go b/ui/build/config.go
index 61f6b1c..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,33 +192,45 @@
 		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
-}
-
-func loadEnvConfig(ctx Context, config *configImpl) error {
-	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
-	if bc == "" {
 		return nil
 	}
 
-	if err := fetchEnvConfig(ctx, config, bc); err != nil {
-		ctx.Verbosef("Failed to fetch config file: %v\n", err)
+	// 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
+}
+
+func loadEnvConfig(ctx Context, config *configImpl, bc string) error {
+	if bc == "" {
+		return nil
 	}
 
 	configDirs := []string{
@@ -262,6 +281,12 @@
 		environ:       OsEnvironment(),
 		sandboxConfig: &SandboxConfig{},
 	}
+	srcDir := absPath(ctx, ".")
+	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
+	if err := loadEnvConfig(ctx, ret, bc); err != nil {
+		ctx.Fatalln("Failed to parse env config files: %v", err)
+	}
+	ret.metricsUploader = GetMetricsUploader(srcDir, ret.environ)
 	return Config{ret}
 }
 
@@ -294,8 +319,15 @@
 
 	// loadEnvConfig needs to know what the OUT_DIR is, so it should
 	// be called after we determine the appropriate out directory.
-	if err := loadEnvConfig(ctx, ret); err != nil {
-		ctx.Fatalln("Failed to parse env config files: %v", err)
+	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
+
+	if bc != "" {
+		if err := fetchEnvConfig(ctx, ret, bc); err != nil {
+			ctx.Verbosef("Failed to fetch config file: %v\n", err)
+		}
+		if err := loadEnvConfig(ctx, ret, bc); err != nil {
+			ctx.Fatalln("Failed to parse env config files: %v", err)
+		}
 	}
 
 	if distDir, ok := ret.environ.Get("DIST_DIR"); ok {
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)
+				}
 			}
 		})
 	}