Merge "Fixes host_required_modules for test_module_config" into main
diff --git a/Android.bp b/Android.bp
index 432c7fc..535246e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,8 @@
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_visibility: [
+        "//build/soong:__subpackages__",
+    ],
 }
 
 subdirs = [
@@ -23,6 +26,8 @@
     srcs: [
         "doc.go",
     ],
+    // Used by plugins, though probably shouldn't be.
+    visibility: ["//visibility:public"],
 }
 
 //
@@ -40,6 +45,7 @@
             enabled: true,
         },
     },
+    defaults_visibility: ["//visibility:public"],
 }
 
 //
@@ -51,6 +57,7 @@
     vendor: true,
     recovery_available: true,
     min_sdk_version: "apex_inherit",
+    visibility: ["//visibility:public"],
 }
 
 cc_genrule {
@@ -75,6 +82,7 @@
     cmd: "$(location) -s $(out) $(in)",
     srcs: [":linker"],
     out: ["linker.s"],
+    visibility: ["//bionic/libc"],
 }
 
 cc_genrule {
@@ -99,11 +107,13 @@
     cmd: "$(location) -T $(out) $(in)",
     srcs: [":linker"],
     out: ["linker.script"],
+    visibility: ["//visibility:public"],
 }
 
 // Instantiate the dex_bootjars singleton module.
 dex_bootjars {
     name: "dex_bootjars",
+    visibility: ["//visibility:public"],
 }
 
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@@ -123,6 +133,7 @@
 // container for apex_contributions selected using build flags
 all_apex_contributions {
     name: "all_apex_contributions",
+    visibility: ["//visibility:public"],
 }
 
 product_config {
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 250fd56..d9a862c 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -88,6 +88,13 @@
 		ctx.PropertyErrorf("container", "missing container property")
 	}
 
+	// treating system_ext as system partition as we are combining them as one container
+	// TODO remove this logic once we start enforcing that system_ext cannot be specified as
+	// container in the container field.
+	if module.properties.Container == "system_ext" {
+		module.properties.Container = "system"
+	}
+
 	// Add a dependency on the aconfig_value_sets defined in
 	// RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that
 	// match our package.
diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go
index 7ba76c0..d72ec48 100644
--- a/aconfig/aconfig_value_set.go
+++ b/aconfig/aconfig_value_set.go
@@ -16,6 +16,9 @@
 
 import (
 	"android/soong/android"
+	"fmt"
+	"strings"
+
 	"github.com/google/blueprint"
 )
 
@@ -27,6 +30,9 @@
 	properties struct {
 		// aconfig_values modules
 		Values []string
+
+		// Paths to the Android.bp files where the aconfig_values modules are defined.
+		Srcs []string
 	}
 }
 
@@ -56,7 +62,35 @@
 
 var valueSetProviderKey = blueprint.NewProvider[valueSetProviderData]()
 
+func (module *ValueSetModule) FindAconfigValuesFromSrc(ctx android.BottomUpMutatorContext) map[string]android.Path {
+	moduleDir := ctx.ModuleDir()
+	srcs := android.PathsForModuleSrcExcludes(ctx, module.properties.Srcs, []string{ctx.BlueprintsFile()})
+
+	aconfigValuesPrefix := strings.Replace(module.Name(), "aconfig_value_set", "aconfig-values", 1)
+	moduleNamesSrcMap := make(map[string]android.Path)
+	for _, src := range srcs {
+		subDir := strings.TrimPrefix(src.String(), moduleDir+"/")
+		packageName, _, found := strings.Cut(subDir, "/")
+		if found {
+			moduleName := fmt.Sprintf("%s-%s-all", aconfigValuesPrefix, packageName)
+			moduleNamesSrcMap[moduleName] = src
+		}
+	}
+	return moduleNamesSrcMap
+}
+
 func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+
+	// TODO: b/366285733 - Replace the file path based solution with more robust solution.
+	aconfigValuesMap := module.FindAconfigValuesFromSrc(ctx)
+	for _, moduleName := range android.SortedKeys(aconfigValuesMap) {
+		if ctx.OtherModuleExists(moduleName) {
+			ctx.AddDependency(ctx.Module(), valueSetTag, moduleName)
+		} else {
+			ctx.ModuleErrorf("module %q not found. Rename the aconfig_values module defined in %q to %q", moduleName, aconfigValuesMap[moduleName], moduleName)
+		}
+	}
+
 	deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
 	for _, dep := range deps {
 		_, ok := dep.(*ValuesModule)
diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go
index 32c31cb..3b7281e 100644
--- a/aconfig/aconfig_value_set_test.go
+++ b/aconfig/aconfig_value_set_test.go
@@ -18,6 +18,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 func TestAconfigValueSet(t *testing.T) {
@@ -41,3 +43,112 @@
 	depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey)
 	android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String())
 }
+
+func TestAconfigValueSetBpGlob(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				// .../some_release/android.foo/
+				"some_release/android.foo/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android.foo-all",
+					package: "android.foo",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.foo/flag.textproto": nil,
+
+				// .../some_release/android.bar/
+				"some_release/android.bar/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android.bar-all",
+					package: "android.bar",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.bar/flag.textproto": nil,
+
+				// .../some_release/
+				"some_release/Android.bp": []byte(`
+				aconfig_value_set {
+					name: "aconfig_value_set-platform_build_release-some_release",
+					srcs: [
+						"*/Android.bp",
+					],
+				}
+				`),
+			},
+		),
+	).RunTest(t)
+
+	checkModuleHasDependency := func(name, variant, dep string) bool {
+		t.Helper()
+		module := result.ModuleForTests(name, variant).Module()
+		depFound := false
+		result.VisitDirectDeps(module, func(m blueprint.Module) {
+			if m.Name() == dep {
+				depFound = true
+			}
+		})
+		return depFound
+	}
+	android.AssertBoolEquals(t,
+		"aconfig_value_set expected to depend on aconfig_value via srcs",
+		true,
+		checkModuleHasDependency(
+			"aconfig_value_set-platform_build_release-some_release",
+			"",
+			"aconfig-values-platform_build_release-some_release-android.foo-all",
+		),
+	)
+	android.AssertBoolEquals(t,
+		"aconfig_value_set expected to depend on aconfig_value via srcs",
+		true,
+		checkModuleHasDependency(
+			"aconfig_value_set-platform_build_release-some_release",
+			"",
+			"aconfig-values-platform_build_release-some_release-android.bar-all",
+		),
+	)
+}
+
+func TestAconfigValueSetBpGlobError(t *testing.T) {
+	android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				// .../some_release/android.bar/
+				"some_release/android.bar/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android_bar-all",
+					package: "android.bar",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.bar/flag.textproto": nil,
+
+				// .../some_release/
+				"some_release/Android.bp": []byte(`
+				aconfig_value_set {
+					name: "aconfig_value_set-platform_build_release-some_release",
+					srcs: [
+						"*/Android.bp",
+					],
+				}
+				`),
+			},
+		),
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`module "aconfig_value_set-platform_build_release-some_release": module ` +
+			`"aconfig-values-platform_build_release-some_release-android.bar-all" not found. ` +
+			`Rename the aconfig_values module defined in "some_release/android.bar/Android.bp" ` +
+			`to "aconfig-values-platform_build_release-some_release-android.bar-all"`),
+	).RunTest(t)
+}
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index 87b54a4..d8372f3 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -260,7 +260,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations_bar",
 				package: "com.example.package.bar",
-				container: "system_ext",
+				container: "vendor",
 				srcs: ["bar.aconfig"],
 			}
 
diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp
index ec21504..07472a4 100644
--- a/aidl_library/Android.bp
+++ b/aidl_library/Android.bp
@@ -29,4 +29,5 @@
         "aidl_library_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/android/Android.bp b/android/Android.bp
index 16a34b7..2adedfe 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -88,6 +88,7 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "product_config.go",
+        "product_config_to_bp.go",
         "proto.go",
         "provider.go",
         "raw_files.go",
@@ -154,4 +155,6 @@
         "vintf_fragment_test.go",
         "visibility_test.go",
     ],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index cb88e01..d2a9622 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -233,7 +233,8 @@
 	} else if m.ProductSpecific() {
 		container = "product"
 	} else if m.SystemExtSpecific() {
-		container = "system_ext"
+		// system_ext and system partitions should be treated as one container
+		container = "system"
 	}
 
 	paths = append(paths, aconfigFiles[container]...)
diff --git a/android/androidmk.go b/android/androidmk.go
index 5fb0cd1..cac2cfe 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -34,7 +34,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
@@ -815,8 +814,6 @@
 		switch x := mod.(type) {
 		case AndroidMkDataProvider:
 			err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
-		case bootstrap.GoBinaryTool:
-			err = translateGoBinaryModule(ctx, w, mod, x)
 		case AndroidMkEntriesProvider:
 			err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
 		default:
@@ -831,23 +828,6 @@
 	return err
 }
 
-// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
-// m by making them phony targets.
-func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	goBinary bootstrap.GoBinaryTool) error {
-
-	name := ctx.ModuleName(mod)
-	fmt.Fprintln(w, ".PHONY:", name)
-	fmt.Fprintln(w, name+":", goBinary.InstallPath())
-	fmt.Fprintln(w, "")
-	// Assuming no rules in make include go binaries in distributables.
-	// If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
-	// In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
-	// `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
-
-	return nil
-}
-
 func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
 	// Get the preamble content through AndroidMkEntries logic.
 	data.Entries = AndroidMkEntries{
diff --git a/android/arch.go b/android/arch.go
index 6d896e5..942727a 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -23,7 +23,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -397,45 +396,21 @@
 // device_supported and host_supported properties to determine which OsTypes are enabled for this
 // module, then searches through the Targets to determine which have enabled Targets for this
 // module.
-func osMutator(bpctx blueprint.BottomUpMutatorContext) {
-	var module Module
-	var ok bool
-	if module, ok = bpctx.Module().(Module); !ok {
-		// The module is not a Soong module, it is a Blueprint module.
-		if bootstrap.IsBootstrapModule(bpctx.Module()) {
-			// Bootstrap Go modules are always the build OS or linux bionic.
-			config := bpctx.Config().(Config)
-			osNames := []string{config.BuildOSTarget.OsVariation()}
-			for _, hostCrossTarget := range config.Targets[LinuxBionic] {
-				if hostCrossTarget.Arch.ArchType == config.BuildOSTarget.Arch.ArchType {
-					osNames = append(osNames, hostCrossTarget.OsVariation())
-				}
-			}
-			osNames = FirstUniqueStrings(osNames)
-			bpctx.CreateVariations(osNames...)
-		}
-		return
-	}
+type osTransitionMutator struct{}
 
-	// Bootstrap Go module support above requires this mutator to be a
-	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
-	// filters out non-Soong modules.  Now that we've handled them, create a
-	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
-	defer bottomUpMutatorContextPool.Put(mctx)
+type allOsInfo struct {
+	Os         map[string]OsType
+	Variations []string
+}
 
-	base := module.base()
+var allOsProvider = blueprint.NewMutatorProvider[*allOsInfo]("os_propagate")
 
-	// Nothing to do for modules that are not architecture specific (e.g. a genrule).
-	if !base.ArchSpecific() {
-		return
-	}
-
-	// Collect a list of OSTypes supported by this module based on the HostOrDevice value
-	// passed to InitAndroidArchModule and the device_supported and host_supported properties.
+// moduleOSList collects a list of OSTypes supported by this module based on the HostOrDevice
+// value passed to InitAndroidArchModule and the device_supported and host_supported properties.
+func moduleOSList(ctx ConfigContext, base *ModuleBase) []OsType {
 	var moduleOSList []OsType
 	for _, os := range osTypeList {
-		for _, t := range mctx.Config().Targets[os] {
+		for _, t := range ctx.Config().Targets[os] {
 			if base.supportsTarget(t) {
 				moduleOSList = append(moduleOSList, os)
 				break
@@ -443,53 +418,91 @@
 		}
 	}
 
-	createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
+	if base.commonProperties.CreateCommonOSVariant {
+		// A CommonOS variant was requested so add it to the list of OS variants to
+		// create. It needs to be added to the end because it needs to depend on the
+		// the other variants and inter variant dependencies can only be created from a
+		// later variant in that list to an earlier one. That is because variants are
+		// always processed in the order in which they are created.
+		moduleOSList = append(moduleOSList, CommonOS)
+	}
+
+	return moduleOSList
+}
+
+func (o *osTransitionMutator) Split(ctx BaseModuleContext) []string {
+	module := ctx.Module()
+	base := module.base()
+
+	// Nothing to do for modules that are not architecture specific (e.g. a genrule).
+	if !base.ArchSpecific() {
+		return []string{""}
+	}
+
+	moduleOSList := moduleOSList(ctx, base)
 
 	// If there are no supported OSes then disable the module.
-	if len(moduleOSList) == 0 && !createCommonOSVariant {
+	if len(moduleOSList) == 0 {
 		base.Disable()
-		return
+		return []string{""}
 	}
 
 	// Convert the list of supported OsTypes to the variation names.
 	osNames := make([]string, len(moduleOSList))
+	osMapping := make(map[string]OsType, len(moduleOSList))
 	for i, os := range moduleOSList {
 		osNames[i] = os.String()
+		osMapping[osNames[i]] = os
 	}
 
-	if createCommonOSVariant {
-		// A CommonOS variant was requested so add it to the list of OS variants to
-		// create. It needs to be added to the end because it needs to depend on the
-		// the other variants in the list returned by CreateVariations(...) and inter
-		// variant dependencies can only be created from a later variant in that list to
-		// an earlier one. That is because variants are always processed in the order in
-		// which they are returned from CreateVariations(...).
-		osNames = append(osNames, CommonOS.Name)
-		moduleOSList = append(moduleOSList, CommonOS)
+	SetProvider(ctx, allOsProvider, &allOsInfo{
+		Os:         osMapping,
+		Variations: osNames,
+	})
+
+	return osNames
+}
+
+func (o *osTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (o *osTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	module := ctx.Module()
+	base := module.base()
+
+	if !base.ArchSpecific() {
+		return ""
 	}
 
-	// Create the variations, annotate each one with which OS it was created for, and
+	return incomingVariation
+}
+
+func (o *osTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	module := ctx.Module()
+	base := module.base()
+
+	if variation == "" {
+		return
+	}
+
+	allOsInfo, ok := ModuleProvider(ctx, allOsProvider)
+	if !ok {
+		panic(fmt.Errorf("missing allOsProvider"))
+	}
+
+	// Annotate this variant with which OS it was created for, and
 	// squash the appropriate OS-specific properties into the top level properties.
-	modules := mctx.CreateVariations(osNames...)
-	for i, m := range modules {
-		m.base().commonProperties.CompileOS = moduleOSList[i]
-		m.base().setOSProperties(mctx)
-	}
+	base.commonProperties.CompileOS = allOsInfo.Os[variation]
+	base.setOSProperties(ctx)
 
-	if createCommonOSVariant {
+	if variation == CommonOS.String() {
 		// A CommonOS variant was requested so add dependencies from it (the last one in
 		// the list) to the OS type specific variants.
-		last := len(modules) - 1
-		commonOSVariant := modules[last]
-		commonOSVariant.base().commonProperties.CommonOSVariant = true
-		for _, module := range modules[0:last] {
-			// Ignore modules that are enabled. Note, this will only avoid adding
-			// dependencies on OsType variants that are explicitly disabled in their
-			// properties. The CommonOS variant will still depend on disabled variants
-			// if they are disabled afterwards, e.g. in archMutator if
-			if module.Enabled(mctx) {
-				mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module)
-			}
+		osList := allOsInfo.Variations[:len(allOsInfo.Variations)-1]
+		for _, os := range osList {
+			variation := []blueprint.Variation{{"os", os}}
+			ctx.AddVariationDependencies(variation, commonOsToOsSpecificVariantTag, ctx.ModuleName())
 		}
 	}
 }
@@ -522,7 +535,7 @@
 
 var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
 
-// archMutator splits a module into a variant for each Target requested by the module.  Target selection
+// archTransitionMutator splits a module into a variant for each Target requested by the module.  Target selection
 // for a module is in three levels, OsClass, multilib, and then Target.
 // OsClass selection is determined by:
 //   - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
@@ -553,41 +566,32 @@
 //
 // Modules can be initialized with InitAndroidMultiTargetsArchModule, in which case they will be split by OsClass,
 // but will have a common Target that is expected to handle all other selected Targets via ctx.MultiTargets().
-func archMutator(bpctx blueprint.BottomUpMutatorContext) {
-	var module Module
-	var ok bool
-	if module, ok = bpctx.Module().(Module); !ok {
-		if bootstrap.IsBootstrapModule(bpctx.Module()) {
-			// Bootstrap Go modules are always the build architecture.
-			bpctx.CreateVariations(bpctx.Config().(Config).BuildOSTarget.ArchVariation())
-		}
-		return
-	}
+type archTransitionMutator struct{}
 
-	// Bootstrap Go module support above requires this mutator to be a
-	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
-	// filters out non-Soong modules.  Now that we've handled them, create a
-	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
-	defer bottomUpMutatorContextPool.Put(mctx)
+type allArchInfo struct {
+	Targets      map[string]Target
+	MultiTargets []Target
+	Primary      string
+	Multilib     string
+}
 
+var allArchProvider = blueprint.NewMutatorProvider[*allArchInfo]("arch_propagate")
+
+func (a *archTransitionMutator) Split(ctx BaseModuleContext) []string {
+	module := ctx.Module()
 	base := module.base()
 
 	if !base.ArchSpecific() {
-		return
+		return []string{""}
 	}
 
 	os := base.commonProperties.CompileOS
 	if os == CommonOS {
-		// Make sure that the target related properties are initialized for the
-		// CommonOS variant.
-		addTargetProperties(module, commonTargetMap[os.Name], nil, true)
-
 		// Do not create arch specific variants for the CommonOS variant.
-		return
+		return []string{""}
 	}
 
-	osTargets := mctx.Config().Targets[os]
+	osTargets := ctx.Config().Targets[os]
 
 	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported.
@@ -614,19 +618,18 @@
 	prefer32 := os == Windows
 
 	// Determine the multilib selection for this module.
-	ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice()
-	multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice)
+	multilib, extraMultilib := decodeMultilib(ctx, base)
 
 	// Convert the multilib selection into a list of Targets.
 	targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
 	if err != nil {
-		mctx.ModuleErrorf("%s", err.Error())
+		ctx.ModuleErrorf("%s", err.Error())
 	}
 
 	// If there are no supported targets disable the module.
 	if len(targets) == 0 {
 		base.Disable()
-		return
+		return []string{""}
 	}
 
 	// If the module is using extraMultilib, decode the extraMultilib selection into
@@ -635,7 +638,7 @@
 	if extraMultilib != "" {
 		multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
 		if err != nil {
-			mctx.ModuleErrorf("%s", err.Error())
+			ctx.ModuleErrorf("%s", err.Error())
 		}
 		multiTargets = filterHostCross(multiTargets, targets[0].HostCross)
 	}
@@ -643,7 +646,7 @@
 	// Recovery is always the primary architecture, filter out any other architectures.
 	// Common arch is also allowed
 	if image == RecoveryVariation {
-		primaryArch := mctx.Config().DevicePrimaryArchType()
+		primaryArch := ctx.Config().DevicePrimaryArchType()
 		targets = filterToArch(targets, primaryArch, Common)
 		multiTargets = filterToArch(multiTargets, primaryArch, Common)
 	}
@@ -651,37 +654,109 @@
 	// If there are no supported targets disable the module.
 	if len(targets) == 0 {
 		base.Disable()
-		return
+		return []string{""}
 	}
 
 	// Convert the targets into a list of arch variation names.
 	targetNames := make([]string, len(targets))
+	targetMapping := make(map[string]Target, len(targets))
 	for i, target := range targets {
 		targetNames[i] = target.ArchVariation()
+		targetMapping[targetNames[i]] = targets[i]
 	}
 
-	// Create the variations, annotate each one with which Target it was created for, and
-	// squash the appropriate arch-specific properties into the top level properties.
-	modules := mctx.CreateVariations(targetNames...)
-	for i, m := range modules {
-		addTargetProperties(m, targets[i], multiTargets, i == 0)
-		m.base().setArchProperties(mctx)
+	SetProvider(ctx, allArchProvider, &allArchInfo{
+		Targets:      targetMapping,
+		MultiTargets: multiTargets,
+		Primary:      targetNames[0],
+		Multilib:     multilib,
+	})
+	return targetNames
+}
 
-		// Install support doesn't understand Darwin+Arm64
-		if os == Darwin && targets[i].HostCross {
-			m.base().commonProperties.SkipInstall = true
+func (a *archTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (a *archTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	module := ctx.Module()
+	base := module.base()
+
+	if !base.ArchSpecific() {
+		return ""
+	}
+
+	os := base.commonProperties.CompileOS
+	if os == CommonOS {
+		// Do not create arch specific variants for the CommonOS variant.
+		return ""
+	}
+
+	if incomingVariation == "" {
+		multilib, _ := decodeMultilib(ctx, base)
+		if multilib == "common" {
+			return "common"
 		}
 	}
+	return incomingVariation
+}
+
+func (a *archTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	module := ctx.Module()
+	base := module.base()
+	os := base.commonProperties.CompileOS
+
+	if os == CommonOS {
+		// Make sure that the target related properties are initialized for the
+		// CommonOS variant.
+		addTargetProperties(module, commonTargetMap[os.Name], nil, true)
+		return
+	}
+
+	if variation == "" {
+		return
+	}
+
+	if !base.ArchSpecific() {
+		panic(fmt.Errorf("found variation %q for non arch specifc module", variation))
+	}
+
+	allArchInfo, ok := ModuleProvider(ctx, allArchProvider)
+	if !ok {
+		return
+	}
+
+	target, ok := allArchInfo.Targets[variation]
+	if !ok {
+		panic(fmt.Errorf("missing Target for %q", variation))
+	}
+	primary := variation == allArchInfo.Primary
+	multiTargets := allArchInfo.MultiTargets
+
+	// Annotate the new variant with which Target it was created for, and
+	// squash the appropriate arch-specific properties into the top level properties.
+	addTargetProperties(ctx.Module(), target, multiTargets, primary)
+	base.setArchProperties(ctx)
+
+	// Install support doesn't understand Darwin+Arm64
+	if os == Darwin && target.HostCross {
+		base.commonProperties.SkipInstall = true
+	}
 
 	// Create a dependency for Darwin Universal binaries from the primary to secondary
 	// architecture. The module itself will be responsible for calling lipo to merge the outputs.
 	if os == Darwin {
-		if multilib == "darwin_universal" && len(modules) == 2 {
-			mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0])
-		} else if multilib == "darwin_universal_common_first" && len(modules) == 3 {
-			mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1])
+		isUniversalBinary := (allArchInfo.Multilib == "darwin_universal" && len(allArchInfo.Targets) == 2) ||
+			allArchInfo.Multilib == "darwin_universal_common_first" && len(allArchInfo.Targets) == 3
+		isPrimary := variation == ctx.Config().BuildArch.String()
+		hasSecondaryConfigured := len(ctx.Config().Targets[Darwin]) > 1
+		if isUniversalBinary && isPrimary && hasSecondaryConfigured {
+			secondaryArch := ctx.Config().Targets[Darwin][1].Arch.String()
+			variation := []blueprint.Variation{{"arch", secondaryArch}}
+			ctx.AddVariationDependencies(variation, DarwinUniversalVariantTag, ctx.ModuleName())
 		}
 	}
+
 }
 
 // addTargetProperties annotates a variant with the Target is is being compiled for, the list
@@ -698,7 +773,9 @@
 // multilib from the factory's call to InitAndroidArchModule if none was set.  For modules that
 // called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
 // the actual multilib in extraMultilib.
-func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) {
+func decodeMultilib(ctx ConfigContext, base *ModuleBase) (multilib, extraMultilib string) {
+	os := base.commonProperties.CompileOS
+	ignorePrefer32OnDevice := ctx.Config().IgnorePrefer32OnDevice()
 	// First check the "android.compile_multilib" or "host.compile_multilib" properties.
 	switch os.Class {
 	case Device:
diff --git a/android/arch_test.go b/android/arch_test.go
index 6134a06..57c9010 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -451,7 +451,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
-			if tt.goOS != runtime.GOOS {
+			if tt.goOS != "" && tt.goOS != runtime.GOOS {
 				t.Skipf("requries runtime.GOOS %s", tt.goOS)
 			}
 
diff --git a/android/config.go b/android/config.go
index 368e573..00fc823 100644
--- a/android/config.go
+++ b/android/config.go
@@ -238,6 +238,11 @@
 	return c.config.productVariables.ReleaseAconfigFlagDefaultPermission
 }
 
+// Enable object size sanitizer
+func (c Config) ReleaseBuildObjectSizeSanitizer() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_BUILD_OBJECT_SIZE_SANITIZER")
+}
+
 // The flag indicating behavior for the tree wrt building modules or using prebuilts
 // derived from RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE
 func (c Config) ReleaseDefaultModuleBuildFromSource() bool {
@@ -1965,6 +1970,10 @@
 	return val, ok
 }
 
+func (c *config) UseOptimizedResourceShrinkingByDefault() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT")
+}
+
 func (c *config) UseResourceProcessorByDefault() bool {
 	return c.productVariables.GetBuildFlagBool("RELEASE_USE_RESOURCE_PROCESSOR_BY_DEFAULT")
 }
diff --git a/android/defaults.go b/android/defaults.go
index 0d51d9d..3d06c69 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -69,7 +69,7 @@
 
 	// Apply defaults from the supplied Defaults to the property structures supplied to
 	// setProperties(...).
-	applyDefaults(TopDownMutatorContext, []Defaults)
+	applyDefaults(BottomUpMutatorContext, []Defaults)
 
 	// Set the hook to be called after any defaults have been applied.
 	//
@@ -101,6 +101,7 @@
 // A restricted subset of context methods, similar to LoadHookContext.
 type DefaultableHookContext interface {
 	EarlyModuleContext
+	OtherModuleProviderContext
 
 	CreateModule(ModuleFactory, ...interface{}) Module
 	AddMissingDependencies(missingDeps []string)
@@ -209,7 +210,7 @@
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
 
-func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
+func (defaultable *DefaultableModuleBase) applyDefaults(ctx BottomUpMutatorContext,
 	defaultsList []Defaults) {
 
 	for _, defaults := range defaultsList {
@@ -226,7 +227,7 @@
 // Product variable properties need special handling, the type of the filtered product variable
 // property struct may not be identical between the defaults module and the defaultable module.
 // Use PrependMatchingProperties to apply whichever properties match.
-func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx BottomUpMutatorContext,
 	defaults Defaults, defaultableProp interface{}) {
 	if defaultableProp == nil {
 		return
@@ -254,7 +255,7 @@
 	}
 }
 
-func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx BottomUpMutatorContext,
 	defaults Defaults, defaultableProp interface{}) {
 
 	for _, def := range defaults.properties() {
@@ -273,7 +274,7 @@
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
-	ctx.TopDown("defaults", defaultsMutator).Parallel()
+	ctx.BottomUp("defaults", defaultsMutator).Parallel()
 }
 
 func defaultsDepsMutator(ctx BottomUpMutatorContext) {
@@ -282,8 +283,12 @@
 	}
 }
 
-func defaultsMutator(ctx TopDownMutatorContext) {
+func defaultsMutator(ctx BottomUpMutatorContext) {
 	if defaultable, ok := ctx.Module().(Defaultable); ok {
+		if _, isDefaultsModule := ctx.Module().(Defaults); isDefaultsModule {
+			// Don't squash transitive defaults into defaults modules
+			return
+		}
 		defaults := defaultable.defaults().Defaults
 		if len(defaults) > 0 {
 			var defaultsList []Defaults
diff --git a/android/init.go b/android/init.go
index d5b486b..b462292 100644
--- a/android/init.go
+++ b/android/init.go
@@ -18,5 +18,6 @@
 
 func init() {
 	gob.Register(ModuleOutPath{})
+	gob.Register(PhonyPath{})
 	gob.Register(unstableInfo{})
 }
diff --git a/android/module.go b/android/module.go
index 47fdc23..1a3f328 100644
--- a/android/module.go
+++ b/android/module.go
@@ -443,12 +443,6 @@
 	// Set at module initialization time by calling InitCommonOSAndroidMultiTargetsArchModule
 	CreateCommonOSVariant bool `blueprint:"mutated"`
 
-	// If set to true then this variant is the CommonOS variant that has dependencies on its
-	// OsType specific variants.
-	//
-	// Set by osMutator.
-	CommonOSVariant bool `blueprint:"mutated"`
-
 	// When set to true, this module is not installed to the full install path (ex: under
 	// out/target/product/<name>/<partition>). It can be installed only to the packaging
 	// modules like android_filesystem.
@@ -1221,7 +1215,7 @@
 
 // True if the current variant is a CommonOS variant, false otherwise.
 func (m *ModuleBase) IsCommonOSVariant() bool {
-	return m.commonProperties.CommonOSVariant
+	return m.commonProperties.CompileOS == CommonOS
 }
 
 // supportsTarget returns true if the given Target is supported by the current module.
@@ -1346,6 +1340,14 @@
 	return m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
 }
 
+// Returns a copy of the enabled property, useful for passing it on to sub-modules
+func (m *ModuleBase) EnabledProperty() proptools.Configurable[bool] {
+	if m.commonProperties.ForcedDisabled {
+		return proptools.NewSimpleConfigurable(false)
+	}
+	return m.commonProperties.Enabled.Clone()
+}
+
 func (m *ModuleBase) Disable() {
 	m.commonProperties.ForcedDisabled = true
 }
@@ -2204,9 +2206,15 @@
 	return proptools.Bool(m.commonProperties.Native_bridge_supported)
 }
 
+type ConfigContext interface {
+	Config() Config
+}
+
 type ConfigurableEvaluatorContext interface {
+	OtherModuleProviderContext
 	Config() Config
 	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
+	HasMutatorFinished(mutatorName string) bool
 }
 
 type configurationEvalutor struct {
@@ -2228,6 +2236,12 @@
 func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
 	ctx := e.ctx
 	m := e.m
+
+	if !ctx.HasMutatorFinished("defaults") {
+		ctx.OtherModulePropertyErrorf(m, property, "Cannot evaluate configurable property before the defaults mutator has run")
+		return proptools.ConfigurableValueUndefined()
+	}
+
 	switch condition.FunctionName() {
 	case "release_flag":
 		if condition.NumArgs() != 1 {
diff --git a/android/module_context.go b/android/module_context.go
index 54fe0be..2bf2a8f 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -85,7 +85,9 @@
 type ModuleContext interface {
 	BaseModuleContext
 
-	blueprintModuleContext() blueprint.ModuleContext
+	// BlueprintModuleContext returns the blueprint.ModuleContext that the ModuleContext wraps.  It may only be
+	// used by the golang module types that need to call into the bootstrap module types.
+	BlueprintModuleContext() blueprint.ModuleContext
 
 	// Deprecated: use ModuleContext.Build instead.
 	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
@@ -779,7 +781,7 @@
 	m.uncheckedModule = true
 }
 
-func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
+func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
 
diff --git a/android/mutator.go b/android/mutator.go
index 38fb857..9404945 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -148,9 +148,9 @@
 }
 
 func registerArchMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUpBlueprint("os", osMutator).Parallel()
+	ctx.Transition("os", &osTransitionMutator{})
 	ctx.Transition("image", &imageTransitionMutator{})
-	ctx.BottomUpBlueprint("arch", archMutator).Parallel()
+	ctx.Transition("arch", &archTransitionMutator{})
 }
 
 var preDeps = []RegisterMutatorFunc{
@@ -193,16 +193,16 @@
 	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
 	// AddDependency or OtherModuleName until after this mutator pass is complete.
 	Rename(name string)
+
+	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
+	// the specified property structs to it as if the properties were set in a blueprint file.
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
 	BaseMutatorContext
-
-	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
-	// the specified property structs to it as if the properties were set in a blueprint file.
-	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 type topDownMutatorContext struct {
@@ -516,6 +516,9 @@
 }
 
 func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
+	if a.finalPhase {
+		panic("TransitionMutator not allowed in FinalDepsMutators")
+	}
 	if m, ok := ctx.Module().(Module); ok {
 		moduleContext := m.base().baseModuleContextFactory(ctx)
 		return a.mutator.Split(&moduleContext)
@@ -739,6 +742,14 @@
 	b.Module().base().commonProperties.DebugName = name
 }
 
+func (b *bottomUpMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
+	return b.bp.CreateModule(factory, name, props...)
+}
+
+func (b *bottomUpMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+	return createModule(b, factory, "_bottomUpMutatorModule", props...)
+}
+
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 21eebd2..b3ef00f 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -81,6 +81,40 @@
 	AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps)
 }
 
+type testTransitionMutator struct {
+	split              func(ctx BaseModuleContext) []string
+	outgoingTransition func(ctx OutgoingTransitionContext, sourceVariation string) string
+	incomingTransition func(ctx IncomingTransitionContext, incomingVariation string) string
+	mutate             func(ctx BottomUpMutatorContext, variation string)
+}
+
+func (t *testTransitionMutator) Split(ctx BaseModuleContext) []string {
+	if t.split != nil {
+		return t.split(ctx)
+	}
+	return []string{""}
+}
+
+func (t *testTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	if t.outgoingTransition != nil {
+		return t.outgoingTransition(ctx, sourceVariation)
+	}
+	return sourceVariation
+}
+
+func (t *testTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	if t.incomingTransition != nil {
+		return t.incomingTransition(ctx, incomingVariation)
+	}
+	return incomingVariation
+}
+
+func (t *testTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	if t.mutate != nil {
+		t.mutate(ctx, variation)
+	}
+}
+
 func TestModuleString(t *testing.T) {
 	bp := `
 		test {
@@ -94,9 +128,11 @@
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 
 			ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.CreateVariations("a", "b")
+				ctx.Transition("pre_arch", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"a", "b"}
+					},
 				})
 				ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
@@ -105,16 +141,23 @@
 			})
 
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.CreateVariations("c", "d")
+				ctx.Transition("pre_deps", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"c", "d"}
+					},
 				})
 			})
 
 			ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.CreateLocalVariations("e", "f")
+				ctx.Transition("post_deps", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"e", "f"}
+					},
+					outgoingTransition: func(ctx OutgoingTransitionContext, sourceVariation string) string {
+						return ""
+					},
 				})
 				ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
@@ -138,15 +181,15 @@
 		"foo{pre_arch:b}",
 		"foo{pre_arch:a}",
 
-		// After rename_top_down.
-		"foo_renamed1{pre_arch:a}",
+		// After rename_top_down (reversed because pre_deps TransitionMutator.Split is TopDown).
 		"foo_renamed1{pre_arch:b}",
+		"foo_renamed1{pre_arch:a}",
 
-		// After pre_deps.
-		"foo_renamed1{pre_arch:a,pre_deps:c}",
-		"foo_renamed1{pre_arch:a,pre_deps:d}",
-		"foo_renamed1{pre_arch:b,pre_deps:c}",
+		// After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown).
 		"foo_renamed1{pre_arch:b,pre_deps:d}",
+		"foo_renamed1{pre_arch:b,pre_deps:c}",
+		"foo_renamed1{pre_arch:a,pre_deps:d}",
+		"foo_renamed1{pre_arch:a,pre_deps:c}",
 
 		// After post_deps.
 		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
@@ -202,8 +245,10 @@
 						ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
 					}
 				})
-				ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
-					ctx.CreateLocalVariations("a", "b")
+				ctx.Transition("variant", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
 				})
 			})
 
@@ -243,27 +288,20 @@
 }
 
 func TestNoCreateVariationsInFinalDeps(t *testing.T) {
-	checkErr := func() {
-		if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
-			panic("Expected FinalDepsMutators consistency check to fail")
-		}
-	}
-
 	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
-					defer checkErr()
-					ctx.CreateVariations("a", "b")
-				})
-				ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
-					defer checkErr()
-					ctx.CreateLocalVariations("a", "b")
+				ctx.Transition("vars", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
 				})
 			})
 
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(`test {name: "foo"}`),
-	).RunTest(t)
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")).
+		RunTest(t)
 }
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 0f7bb39..f5b1020 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -118,6 +118,7 @@
 	}
 
 	result := GroupFixturePreparers(
+		PrepareForTestWithDefaults,
 		PrepareForTestWithArchMutator,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("component", componentTestModuleFactory)
diff --git a/android/paths.go b/android/paths.go
index 6322f75..0d94f03 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -27,7 +27,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -92,6 +91,7 @@
 // the Path methods that rely on module dependencies having been resolved.
 type ModuleWithDepsPathContext interface {
 	EarlyModulePathContext
+	OtherModuleProviderContext
 	VisitDirectDepsBlueprint(visit func(blueprint.Module))
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 	HasMutatorFinished(mutatorName string) bool
@@ -555,13 +555,6 @@
 	return ret
 }
 
-// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
-func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
-	goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin")
-	rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
-	return goBinaryInstallDir.Join(ctx, rel)
-}
-
 // Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
 // If the dependency is not found, a missingErrorDependency is returned.
 // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
@@ -573,10 +566,6 @@
 	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" {
-		goBinaryPath := PathForGoBinary(ctx, goBinary)
-		return Paths{goBinaryPath}, nil
-	}
 	outputFiles, err := outputFilesForModule(ctx, module, tag)
 	if outputFiles != nil && err == nil {
 		return outputFiles, nil
diff --git a/android/product_config.go b/android/product_config.go
index 04180bf..ce3acc9 100644
--- a/android/product_config.go
+++ b/android/product_config.go
@@ -14,7 +14,9 @@
 
 package android
 
-import "github.com/google/blueprint/proptools"
+import (
+	"github.com/google/blueprint/proptools"
+)
 
 func init() {
 	ctx := InitRegistrationContext
diff --git a/android/product_config_to_bp.go b/android/product_config_to_bp.go
new file mode 100644
index 0000000..680328f
--- /dev/null
+++ b/android/product_config_to_bp.go
@@ -0,0 +1,35 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+func init() {
+	ctx := InitRegistrationContext
+	ctx.RegisterParallelSingletonType("product_config_to_bp_singleton", productConfigToBpSingletonFactory)
+}
+
+type productConfigToBpSingleton struct{}
+
+func (s *productConfigToBpSingleton) GenerateBuildActions(ctx SingletonContext) {
+	// TODO: update content from make-based product config
+	var content string
+	generatedBp := PathForOutput(ctx, "soong_generated_product_config.bp")
+	WriteFileRule(ctx, generatedBp, content)
+	ctx.Phony("product_config_to_bp", generatedBp)
+}
+
+// productConfigToBpSingleton generates a bp file from make-based product config
+func productConfigToBpSingletonFactory() Singleton {
+	return &productConfigToBpSingleton{}
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index 3b1bf39..3b8c6b2 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -96,12 +96,6 @@
 	}
 }
 
-func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
-	if _, ok := ctx.Module().(*testSingletonModule); ok {
-		ctx.CreateVariations("a", "b")
-	}
-}
-
 func TestVariantSingletonModule(t *testing.T) {
 	if testing.Short() {
 		t.Skip("test fails with data race enabled")
@@ -116,7 +110,11 @@
 		prepareForSingletonModuleTest,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+				ctx.Transition("test_singleton_module_mutator", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
+				})
 			})
 		}),
 	).
diff --git a/android/team_proto/Android.bp b/android/team_proto/Android.bp
index 7e2a4c1..5faaaf1 100644
--- a/android/team_proto/Android.bp
+++ b/android/team_proto/Android.bp
@@ -40,4 +40,8 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//tools/asuite/team_build_scripts",
+    ],
 }
diff --git a/android/testing.go b/android/testing.go
index 1ee6e4c..196b22e 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1330,6 +1330,10 @@
 	return ctx.ctx.HasMutatorFinished(mutatorName)
 }
 
+func (ctx *panickingConfigAndErrorContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
+	return ctx.ctx.otherModuleProvider(m, p)
+}
+
 func PanickingConfigAndErrorContext(ctx *TestContext) ConfigurableEvaluatorContext {
 	return &panickingConfigAndErrorContext{
 		ctx: ctx,
diff --git a/android/util.go b/android/util.go
index 3c0af2f..2d269b7 100644
--- a/android/util.go
+++ b/android/util.go
@@ -177,6 +177,41 @@
 	return m
 }
 
+// PrettyConcat returns the formatted concatenated string suitable for displaying user-facing
+// messages.
+func PrettyConcat(list []string, quote bool, lastSep string) string {
+	if len(list) == 0 {
+		return ""
+	}
+
+	quoteStr := func(v string) string {
+		if !quote {
+			return v
+		}
+		return fmt.Sprintf("%q", v)
+	}
+
+	if len(list) == 1 {
+		return quoteStr(list[0])
+	}
+
+	var sb strings.Builder
+	for i, val := range list {
+		if i > 0 {
+			sb.WriteString(", ")
+		}
+		if i == len(list)-1 {
+			sb.WriteString(lastSep)
+			if lastSep != "" {
+				sb.WriteString(" ")
+			}
+		}
+		sb.WriteString(quoteStr(val))
+	}
+
+	return sb.String()
+}
+
 // ListSetDifference checks if the two lists contain the same elements. It returns
 // a boolean which is true if there is a difference, and then returns lists of elements
 // that are in l1 but not l2, and l2 but not l1.
diff --git a/android/util_test.go b/android/util_test.go
index 6537d69..b76ffcf 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -867,3 +867,51 @@
 		})
 	}
 }
+
+var prettyConcatTestCases = []struct {
+	name          string
+	list          []string
+	quote         bool
+	lastSeparator string
+	expected      string
+}{
+	{
+		name:          "empty",
+		list:          []string{},
+		quote:         false,
+		lastSeparator: "and",
+		expected:      ``,
+	},
+	{
+		name:          "single",
+		list:          []string{"a"},
+		quote:         true,
+		lastSeparator: "and",
+		expected:      `"a"`,
+	},
+	{
+		name:          "with separator",
+		list:          []string{"a", "b", "c"},
+		quote:         true,
+		lastSeparator: "or",
+		expected:      `"a", "b", or "c"`,
+	},
+	{
+		name:          "without separator",
+		list:          []string{"a", "b", "c"},
+		quote:         false,
+		lastSeparator: "",
+		expected:      `a, b, c`,
+	},
+}
+
+func TestPrettyConcat(t *testing.T) {
+	for _, testCase := range prettyConcatTestCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			concatString := PrettyConcat(testCase.list, testCase.quote, testCase.lastSeparator)
+			if !reflect.DeepEqual(concatString, testCase.expected) {
+				t.Errorf("expected %#v, got %#v", testCase.expected, concatString)
+			}
+		})
+	}
+}
diff --git a/android/visibility.go b/android/visibility.go
index 89c0adc..61f2200 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -283,7 +283,7 @@
 
 // This must be registered after the deps have been resolved.
 func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
-	ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
+	ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
 }
 
 // Checks the per-module visibility rule lists before defaults expansion.
@@ -507,7 +507,7 @@
 	return true, pkg, name
 }
 
-func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
+func visibilityRuleEnforcer(ctx BottomUpMutatorContext) {
 	qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.Module())
 
 	// Visit all the dependencies making sure that this module has access to them all.
diff --git a/apex/Android.bp b/apex/Android.bp
index ef2f755..4848513 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -42,4 +42,6 @@
         "systemserver_classpath_fragment_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/apex/apex.go b/apex/apex.go
index c12d1e4..9e3f288 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -55,11 +55,10 @@
 }
 
 func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("prebuilt_apex_module_creator", prebuiltApexModuleCreatorMutator).Parallel()
+	ctx.BottomUp("prebuilt_apex_module_creator", prebuiltApexModuleCreatorMutator).Parallel()
 }
 
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
 	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 2b97728..0d5bad4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4885,236 +4885,6 @@
 func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) {
 }
 
-// These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
-// propagation of paths to dex implementation jars from the former to the latter.
-func TestPrebuiltExportDexImplementationJars(t *testing.T) {
-	transform := android.NullFixturePreparer
-
-	checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
-		t.Helper()
-		// Make sure the import has been given the correct path to the dex jar.
-		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
-		dexJarBuildPath := p.DexJarBuildPath(moduleErrorfTestCtx{}).PathOrNil()
-		stem := android.RemoveOptionalPrebuiltPrefix(name)
-		android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.",
-			".intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
-			android.NormalizePathForTesting(dexJarBuildPath))
-	}
-
-	checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) {
-		t.Helper()
-		// Make sure the import has been given the correct path to the dex jar.
-		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
-		dexJarBuildPath := p.DexJarInstallPath()
-		stem := android.RemoveOptionalPrebuiltPrefix(name)
-		android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.",
-			"target/product/test_device/apex/myapex/javalib/"+stem+".jar",
-			android.NormalizePathForTesting(dexJarBuildPath))
-	}
-
-	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
-		t.Helper()
-		// Make sure that an apex variant is not created for the source module.
-		android.AssertArrayString(t, "Check if there is no source variant",
-			[]string{"android_common"},
-			ctx.ModuleVariantsForTests(name))
-	}
-
-	t.Run("prebuilt only", func(t *testing.T) {
-		bp := `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["libfoo", "libbar"],
-		}
-
-		java_import {
-			name: "libfoo",
-			jars: ["libfoo.jar"],
-			sdk_version: "core_current",
-		}
-
-		java_sdk_library_import {
-			name: "libbar",
-			public: {
-				jars: ["libbar.jar"],
-			},
-		}
-	`
-
-		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-
-		deapexerName := deapexerModuleName("prebuilt_myapex")
-		android.AssertStringEquals(t, "APEX module name from deapexer name", "prebuilt_myapex", apexModuleName(deapexerName))
-
-		// Make sure that the deapexer has the correct input APEX.
-		deapexer := ctx.ModuleForTests(deapexerName, "android_common")
-		rule := deapexer.Rule("deapexer")
-		if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) {
-			t.Errorf("expected: %q, found: %q", expected, actual)
-		}
-
-		// Make sure that the prebuilt_apex has the correct input APEX.
-		prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex")
-		rule = prebuiltApex.Rule("android/soong/android.Cp")
-		if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
-			t.Errorf("expected: %q, found: %q", expected, actual)
-		}
-
-		checkDexJarBuildPath(t, ctx, "libfoo")
-		checkDexJarInstallPath(t, ctx, "libfoo")
-
-		checkDexJarBuildPath(t, ctx, "libbar")
-		checkDexJarInstallPath(t, ctx, "libbar")
-	})
-
-	t.Run("prebuilt with source preferred", func(t *testing.T) {
-
-		bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: false,
-			java_libs: [
-				"libfoo",
-				"libbar",
-			],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["libfoo", "libbar"],
-		}
-
-		java_import {
-			name: "libfoo",
-			jars: ["libfoo.jar"],
-			apex_available: [
-				"myapex",
-			],
-			compile_dex: true,
-			sdk_version: "core_current",
-		}
-
-		java_library {
-			name: "libfoo",
-			srcs: ["foo/bar/MyClass.java"],
-			apex_available: [
-				"myapex",
-			],
-			compile_dex: true,
-			sdk_version: "core_current",
-		}
-
-		java_sdk_library_import {
-			name: "libbar",
-			public: {
-				jars: ["libbar.jar"],
-			},
-			apex_available: [
-				"myapex",
-			],
-			compile_dex: true,
-		}
-
-		java_sdk_library {
-			name: "libbar",
-			srcs: ["foo/bar/MyClass.java"],
-			unsafe_ignore_missing_latest_api: true,
-			apex_available: [
-				"myapex",
-			],
-			compile_dex: true,
-			sdk_version: "core_current",
-		}
-	`
-
-		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
-	})
-
-	t.Run("prebuilt preferred with source", func(t *testing.T) {
-		bp := `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["libfoo", "libbar"],
-		}
-
-		java_import {
-			name: "libfoo",
-			prefer: true,
-			jars: ["libfoo.jar"],
-		}
-
-		java_library {
-			name: "libfoo",
-			sdk_version: "core_current",
-		}
-
-		java_sdk_library_import {
-			name: "libbar",
-			prefer: true,
-			public: {
-				jars: ["libbar.jar"],
-			},
-		}
-
-		java_sdk_library {
-			name: "libbar",
-			srcs: ["foo/bar/MyClass.java"],
-			unsafe_ignore_missing_latest_api: true,
-		}
-	`
-
-		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(t, ctx, "libfoo")
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
-		ensureNoSourceVariant(t, ctx, "libbar")
-	})
-}
-
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"),
@@ -5243,18 +5013,10 @@
 		apex_set {
 			name: "myapex",
 			set: "myapex.apks",
-			exported_java_libs: ["myjavalib"],
 			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
 			exported_systemserverclasspath_fragments: ["my-systemserverclasspath-fragment"],
 		}
 
-		java_import {
-			name: "myjavalib",
-			jars: ["myjavalib.jar"],
-			apex_available: ["myapex"],
-			permitted_packages: ["javalib"],
-		}
-
 		prebuilt_bootclasspath_fragment {
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
@@ -9618,42 +9380,6 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex 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) {
-	ctx := testApex(t, `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["foo"],
-		}
-
-		java_import {
-			name: "foo",
-			jars: ["foo.jar"],
-			apex_available: ["myapex"],
-		}
-	`,
-		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
-	)
-
-	prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
-	entriesList := android.AndroidMkEntriesForTest(t, ctx, prebuilt)
-	mainModuleEntries := entriesList[0]
-	android.AssertArrayString(t,
-		"LOCAL_REQUIRED_MODULES",
-		mainModuleEntries.EntryMap["LOCAL_REQUIRED_MODULES"],
-		[]string{
-			"foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex",
-			"foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex",
-		})
-}
-
 func TestAndroidMk_RequiredModules(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -11930,3 +11656,110 @@
 		)
 	})
 }
+
+func TestSdkLibraryTransitiveClassLoaderContext(t *testing.T) {
+	// This test case tests that listing the impl lib instead of the top level java_sdk_library
+	// in libs of android_app and java_library does not lead to class loader context device/host
+	// path mismatch errors.
+	android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureModifyEnv(func(env map[string]string) {
+			env["DISABLE_CONTAINER_CHECK"] = "true"
+		}),
+		withFiles(filesForSdkLibrary),
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.android.foo30-file_contexts": nil,
+		}),
+	).RunTestWithBp(t, `
+		apex {
+		name: "com.android.foo30",
+		key: "myapex.key",
+		updatable: true,
+		bootclasspath_fragments: [
+			"foo-bootclasspath-fragment",
+		],
+		java_libs: [
+			"bar",
+		],
+		apps: [
+			"bar-app",
+		],
+		min_sdk_version: "30",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		bootclasspath_fragment {
+			name: "foo-bootclasspath-fragment",
+			contents: [
+				"framework-foo",
+			],
+			apex_available: [
+				"com.android.foo30",
+			],
+			hidden_api: {
+				split_packages: ["*"]
+			},
+		}
+
+		java_sdk_library {
+			name: "framework-foo",
+			srcs: [
+				"A.java"
+			],
+			unsafe_ignore_missing_latest_api: true,
+			apex_available: [
+				"com.android.foo30",
+			],
+			compile_dex: true,
+			sdk_version: "core_current",
+			shared_library: false,
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [
+				"A.java"
+			],
+			libs: [
+				"framework-foo.impl",
+			],
+			apex_available: [
+				"com.android.foo30",
+			],
+			sdk_version: "core_current",
+		}
+
+		java_library {
+			name: "baz",
+			srcs: [
+				"A.java"
+			],
+			libs: [
+				"bar",
+			],
+			sdk_version: "core_current",
+		}
+
+		android_app {
+			name: "bar-app",
+			srcs: [
+				"A.java"
+			],
+			libs: [
+				"baz",
+				"framework-foo.impl",
+			],
+			apex_available: [
+				"com.android.foo30",
+			],
+			sdk_version: "core_current",
+			min_sdk_version: "30",
+			manifest: "AndroidManifest.xml",
+		}
+       `)
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index f1a134e..f6f3efb 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -107,11 +107,6 @@
 	// from PRODUCT_PACKAGES.
 	Overrides []string
 
-	// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
-	// APEX bundle will create an APEX variant and provide dex implementation jars for use by
-	// dexpreopt and boot jars package check.
-	Exported_java_libs []string
-
 	// List of bootclasspath fragments inside this prebuilt APEX bundle and for which this APEX
 	// bundle will create an APEX variant.
 	Exported_bootclasspath_fragments []string
@@ -265,7 +260,7 @@
 // prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
 // apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
 type prebuiltApexModuleCreator interface {
-	createPrebuiltApexModules(ctx android.TopDownMutatorContext)
+	createPrebuiltApexModules(ctx android.BottomUpMutatorContext)
 }
 
 // prebuiltApexModuleCreatorMutator is the mutator responsible for invoking the
@@ -275,7 +270,7 @@
 // will need to access dependencies added by that (exported modules) but must run before the
 // DepsMutator so that the deapexer module it creates can add dependencies onto itself from the
 // exported modules.
-func prebuiltApexModuleCreatorMutator(ctx android.TopDownMutatorContext) {
+func prebuiltApexModuleCreatorMutator(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
 	if creator, ok := module.(prebuiltApexModuleCreator); ok {
 		creator.createPrebuiltApexModules(ctx)
@@ -283,8 +278,7 @@
 }
 
 func (p *prebuiltCommon) hasExportedDeps() bool {
-	return len(p.prebuiltCommonProperties.Exported_java_libs) > 0 ||
-		len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
+	return len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
 		len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
 }
 
@@ -292,11 +286,6 @@
 func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
 
-	for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
-		prebuiltDep := android.PrebuiltNameFromSource(dep)
-		ctx.AddDependency(module, exportedJavaLibTag, prebuiltDep)
-	}
-
 	for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
 		prebuiltDep := android.PrebuiltNameFromSource(dep)
 		ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep)
@@ -543,7 +532,7 @@
 	return module
 }
 
-func createApexSelectorModule(ctx android.TopDownMutatorContext, name string, apexFileProperties *ApexFileProperties) {
+func createApexSelectorModule(ctx android.BottomUpMutatorContext, name string, apexFileProperties *ApexFileProperties) {
 	props := struct {
 		Name *string
 	}{
@@ -559,9 +548,9 @@
 // createDeapexerModuleIfNeeded will create a deapexer module if it is needed.
 //
 // A deapexer module is only needed when the prebuilt apex specifies one or more modules in either
-// the `exported_java_libs` or `exported_bootclasspath_fragments` properties as that indicates that
+// the `exported_bootclasspath_fragments` properties as that indicates that
 // the listed modules need access to files from within the prebuilt .apex file.
-func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) {
+func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.BottomUpMutatorContext, deapexerName string, apexFileSource string) {
 	// Only create the deapexer module if it is needed.
 	if !p.hasExportedDeps() {
 		return
@@ -666,7 +655,6 @@
 var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
 
 var (
-	exportedJavaLibTag                       = exportedDependencyTag{name: "exported_java_libs"}
 	exportedBootclasspathFragmentTag         = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
 	exportedSystemserverclasspathFragmentTag = exportedDependencyTag{name: "exported_systemserverclasspath_fragments"}
 )
@@ -677,7 +665,7 @@
 // build.
 //
 // If this needs to make files from within a `.apex` file available for use by other Soong modules,
-// e.g. make dex implementation jars available for java_import modules listed in exported_java_libs,
+// e.g. make dex implementation jars available for `contents` listed in exported_bootclasspath_fragments,
 // it does so as follows:
 //
 //  1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
@@ -703,7 +691,7 @@
 //     /         |         \
 //     V            V            V
 //     selector  <---  deapexer  <---  exported java lib
-func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
+func (p *Prebuilt) createPrebuiltApexModules(ctx android.BottomUpMutatorContext) {
 	apexSelectorModuleName := apexSelectorModuleName(p.Name())
 	createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
 
@@ -958,7 +946,7 @@
 	return module
 }
 
-func createApexExtractorModule(ctx android.TopDownMutatorContext, name string, apexExtractorProperties *ApexExtractorProperties) {
+func createApexExtractorModule(ctx android.BottomUpMutatorContext, name string, apexExtractorProperties *ApexExtractorProperties) {
 	props := struct {
 		Name *string
 	}{
@@ -984,7 +972,7 @@
 // prebuilt_apex except that instead of creating a selector module which selects one .apex file
 // from those provided this creates an extractor module which extracts the appropriate .apex file
 // from the zip file containing them.
-func (a *ApexSet) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
+func (a *ApexSet) createPrebuiltApexModules(ctx android.BottomUpMutatorContext) {
 	apexExtractorModuleName := apexExtractorModuleName(a.Name())
 	createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
 
diff --git a/apex/vndk.go b/apex/vndk.go
index 781aa3c..3ececc5 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -54,30 +54,6 @@
 	Vndk_version *string
 }
 
-func apexVndkMutator(mctx android.TopDownMutatorContext) {
-	if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex {
-		if ab.IsNativeBridgeSupported() {
-			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
-		}
-
-		vndkVersion := ab.vndkVersion()
-		if vndkVersion != "" {
-			apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
-			if err != nil {
-				mctx.PropertyErrorf("vndk_version", "%s", err.Error())
-				return
-			}
-
-			targets := mctx.MultiTargets()
-			if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
-				// Disable VNDK APEXes for VNDK versions less than the minimum supported API
-				// level for the primary architecture.
-				ab.Disable()
-			}
-		}
-	}
-}
-
 func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) {
 		vndkVersion := m.VndkVersion()
@@ -93,8 +69,27 @@
 			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName)
 		}
 	} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
-		vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
-		mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...)
+		if a.IsNativeBridgeSupported() {
+			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
+		}
+
+		vndkVersion := a.vndkVersion()
+		if vndkVersion != "" {
+			apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+			if err != nil {
+				mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+				return
+			}
+
+			targets := mctx.MultiTargets()
+			if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
+				// Disable VNDK APEXes for VNDK versions less than the minimum supported API
+				// level for the primary architecture.
+				a.Disable()
+			} else {
+				mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...)
+			}
+		}
 	}
 }
 
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 73c8800..8679821 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -56,6 +56,7 @@
 )
 
 func registerBpfBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("bpf_defaults", defaultsFactory)
 	ctx.RegisterModuleType("bpf", BpfFactory)
 }
 
@@ -77,10 +78,16 @@
 	// the C/C++ module.
 	Cflags []string
 
-	// directories (relative to the root of the source tree) that will
-	// be added to the include paths using -I.
+	// list of directories relative to the root of the source tree that
+	// will be added to the include paths using -I.
+	// If possible, don't use this. If adding paths from the current
+	// directory, use local_include_dirs. If adding paths from other
+	// modules, use export_include_dirs in that module.
 	Include_dirs []string
 
+	// list of directories relative to the Blueprint file that will be
+	// added to the include path using -I.
+	Local_include_dirs []string
 	// optional subdirectory under which this module is installed into.
 	Sub_dir string
 
@@ -94,7 +101,7 @@
 
 type bpf struct {
 	android.ModuleBase
-
+	android.DefaultableModuleBase
 	properties BpfProperties
 
 	objs android.Paths
@@ -163,6 +170,10 @@
 		"-I " + ctx.ModuleDir(),
 	}
 
+	for _, dir := range android.PathsForModuleSrc(ctx, bpf.properties.Local_include_dirs) {
+		cflags = append(cflags, "-I "+dir.String())
+	}
+
 	for _, dir := range android.PathsForSource(ctx, bpf.properties.Include_dirs) {
 		cflags = append(cflags, "-I "+dir.String())
 	}
@@ -264,6 +275,26 @@
 	}
 }
 
+type Defaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func defaultsFactory() android.Module {
+	return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+	module := &Defaults{}
+
+	module.AddProperties(props...)
+	module.AddProperties(&BpfProperties{})
+
+	android.InitDefaultsModule(module)
+
+	return module
+}
+
 func (bpf *bpf) SubDir() string {
 	return bpf.properties.Sub_dir
 }
@@ -274,5 +305,7 @@
 	module.AddProperties(&module.properties)
 
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
 	return module
 }
diff --git a/bpf/libbpf/libbpf_prog.go b/bpf/libbpf/libbpf_prog.go
index 1fdb3d6..ac61510 100644
--- a/bpf/libbpf/libbpf_prog.go
+++ b/bpf/libbpf/libbpf_prog.go
@@ -61,6 +61,7 @@
 )
 
 func registerLibbpfProgBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("libbpf_defaults", defaultsFactory)
 	ctx.RegisterModuleType("libbpf_prog", LibbpfProgFactory)
 }
 
@@ -88,14 +89,17 @@
 	// be added to the include path using -I
 	Local_include_dirs []string `android:"arch_variant"`
 
+	Header_libs []string `android:"arch_variant"`
+
 	// optional subdirectory under which this module is installed into.
 	Relative_install_path string
 }
 
 type libbpfProg struct {
 	android.ModuleBase
+	android.DefaultableModuleBase
 	properties LibbpfProgProperties
-	objs android.Paths
+	objs       android.Paths
 }
 
 var _ android.ImageInterface = (*libbpfProg)(nil)
@@ -139,6 +143,7 @@
 
 func (libbpf *libbpfProg) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddDependency(ctx.Module(), libbpfProgDepTag, "libbpf_headers")
+	ctx.AddVariationDependencies(nil, cc.HeaderDepTag(), libbpf.properties.Header_libs...)
 }
 
 func (libbpf *libbpfProg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -180,6 +185,11 @@
 				depName := ctx.OtherModuleName(dep)
 				ctx.ModuleErrorf("module %q is not a genrule", depName)
 			}
+		} else if depTag == cc.HeaderDepTag() {
+			depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
+			for _, dir := range depExporterInfo.IncludeDirs {
+				cflags = append(cflags, "-I "+dir.String())
+			}
 		}
 	})
 
@@ -269,10 +279,32 @@
 	}
 }
 
+type Defaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func defaultsFactory() android.Module {
+	return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+	module := &Defaults{}
+
+	module.AddProperties(props...)
+	module.AddProperties(&LibbpfProgProperties{})
+
+	android.InitDefaultsModule(module)
+
+	return module
+}
+
 func LibbpfProgFactory() android.Module {
 	module := &libbpfProg{}
 
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+
 	return module
 }
diff --git a/build_kzip.bash b/build_kzip.bash
index 4c42048..850aeda 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -40,6 +40,7 @@
   merge_zips
   xref_cxx
   xref_java
+  xref_kotlin
   # TODO: b/286390153 - reenable rust
   # xref_rust
 )
diff --git a/cc/Android.bp b/cc/Android.bp
index 2952614..3688c8a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -116,4 +116,6 @@
         "cmake_module_cc.txt",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/cc/TEST_MAPPING b/cc/TEST_MAPPING
new file mode 100644
index 0000000..be2809d
--- /dev/null
+++ b/cc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "bionic"
+    }
+  ]
+}
diff --git a/cc/cc.go b/cc/cc.go
index 6e16142..96795d3 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -59,10 +59,10 @@
 			san.registerMutators(ctx)
 		}
 
-		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
+		ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
 		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
 
-		ctx.TopDown("fuzz_deps", fuzzMutatorDeps)
+		ctx.BottomUp("fuzz_deps", fuzzMutatorDeps)
 
 		ctx.Transition("coverage", &coverageTransitionMutator{})
 
@@ -73,7 +73,7 @@
 		ctx.Transition("lto", &ltoTransitionMutator{})
 
 		ctx.BottomUp("check_linktype", checkLinkTypeMutator).Parallel()
-		ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
+		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries).Parallel()
 	})
 
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -2783,7 +2783,7 @@
 // If a library has a vendor variant and is a (transitive) dependency of an LLNDK library,
 // it is subject to be double loaded. Such lib should be explicitly marked as double_loadable: true
 // or as vndk-sp (vndk: { enabled: true, support_system_process: true}).
-func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) {
+func checkDoubleLoadableLibraries(ctx android.BottomUpMutatorContext) {
 	check := func(child, parent android.Module) bool {
 		to, ok := child.(*Module)
 		if !ok {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 93630db..3f3347b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -49,17 +49,30 @@
 
 func registerTestMutators(ctx android.RegistrationContext) {
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("apex", testApexMutator).Parallel()
+		ctx.Transition("apex", &testApexTransitionMutator{})
 	})
 }
 
-func testApexMutator(mctx android.BottomUpMutatorContext) {
-	modules := mctx.CreateVariations(apexVariationName)
+type testApexTransitionMutator struct{}
+
+func (t *testApexTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{apexVariationName}
+}
+
+func (t *testApexTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (t *testApexTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	return incomingVariation
+}
+
+func (t *testApexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
 		MinSdkVersion:     android.ApiLevelForTest(apexVersion),
 	}
-	mctx.SetVariationProvider(modules[0], android.ApexInfoProvider, apexInfo)
+	android.SetProvider(ctx, android.ApexInfoProvider, apexInfo)
 }
 
 // testCcWithConfig runs tests using the prepareForCcTest
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 289409f..f514db6 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -35,4 +35,8 @@
     testSrcs: [
         "tidy_test.go",
     ],
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//prebuilts/clang/host/linux-x86/soong",
+    ],
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index c838357..9d3de6d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -286,6 +286,8 @@
 		// New warnings to be fixed after clang-r468909
 		"-Wno-error=deprecated-builtins", // http://b/241601211
 		"-Wno-error=deprecated",          // in external/googletest/googletest
+		// Disabling until the warning is fixed in libc++abi header files b/366180429
+		"-Wno-deprecated-dynamic-exception-spec",
 		// New warnings to be fixed after clang-r475365
 		"-Wno-error=enum-constexpr-conversion", // http://b/243964282
 		// New warnings to be fixed after clang-r522817
diff --git a/cc/fuzz.go b/cc/fuzz.go
index d9e221b..3f21bc6 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -57,7 +57,7 @@
 	return []interface{}{&fuzzer.Properties}
 }
 
-func fuzzMutatorDeps(mctx android.TopDownMutatorContext) {
+func fuzzMutatorDeps(mctx android.BottomUpMutatorContext) {
 	currentModule, ok := mctx.Module().(*Module)
 	if !ok {
 		return
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index b105a30..c1f2c10 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -20,4 +20,5 @@
         "//apex_available:anyapex",
     ],
     vendor_available: true,
+    visibility: ["//visibility:public"],
 }
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 8202cc0..2706261 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -46,7 +46,7 @@
 
 		if m, ok := module.(*Module); ok {
 			if installer, ok := m.installer.(*stubDecorator); ok {
-				if canDumpAbi(ctx.Config(), ctx.ModuleDir(module)) {
+				if installer.hasAbiDump {
 					depPaths = append(depPaths, installer.abiDumpPath)
 				}
 			}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index bd6dfa3..94e0452 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -125,6 +125,7 @@
 	parsedCoverageXmlPath android.ModuleOutPath
 	installPath           android.Path
 	abiDumpPath           android.OutputPath
+	hasAbiDump            bool
 	abiDiffPaths          android.Paths
 
 	apiLevel         android.ApiLevel
@@ -330,11 +331,11 @@
 }
 
 // Feature flag.
-func canDumpAbi(config android.Config, moduleDir string) bool {
+func (this *stubDecorator) canDumpAbi(ctx ModuleContext) bool {
 	if runtime.GOOS == "darwin" {
 		return false
 	}
-	if strings.HasPrefix(moduleDir, "bionic/") {
+	if strings.HasPrefix(ctx.ModuleDir(), "bionic/") {
 		// Bionic has enough uncommon implementation details like ifuncs and asm
 		// code that the ABI tracking here has a ton of false positives. That's
 		// causing pretty extreme friction for development there, so disabling
@@ -343,8 +344,14 @@
 		// http://b/358653811
 		return false
 	}
+
+	if this.apiLevel.IsCurrent() {
+		// "current" (AKA 10000) is not tracked.
+		return false
+	}
+
 	// http://b/156513478
-	return config.ReleaseNdkAbiMonitored()
+	return ctx.Config().ReleaseNdkAbiMonitored()
 }
 
 // Feature flag to disable diffing against prebuilts.
@@ -357,6 +364,7 @@
 	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
 		this.libraryName(ctx), "abi.stg")
+	this.hasAbiDump = true
 	headersList := getNdkABIHeadersFile(ctx)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        stg,
@@ -478,7 +486,7 @@
 	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
 	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 	c.versionScriptPath = nativeAbiResult.versionScript
-	if canDumpAbi(ctx.Config(), ctx.ModuleDir()) {
+	if c.canDumpAbi(ctx) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
 		if canDiffAbi(ctx.Config()) {
 			c.diffAbi(ctx)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 7b0652c..a8722a0 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -79,7 +79,7 @@
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
-	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
+	memtagStackCommonFlags = []string{"-Xclang -target-feature -Xclang +mte"}
 	memtagStackLlvmFlags   = []string{"-dom-tree-reachability-max-bbs-to-explore=128"}
 
 	hostOnlySanitizeFlags   = []string{"-fno-sanitize-recover=all"}
@@ -176,7 +176,7 @@
 	switch t {
 	case cfi, Hwasan, Asan, tsan, Fuzzer, scs, Memtag_stack:
 		sanitizer := &sanitizerSplitMutator{t}
-		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
+		ctx.BottomUp(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
 	case Memtag_heap, Memtag_globals, intOverflow:
 		// do nothing
@@ -1153,7 +1153,7 @@
 // If an APEX is sanitized or not depends on whether it contains at least one
 // sanitized module. Transition mutators cannot propagate information up the
 // dependency graph this way, so we need an auxiliary mutator to do so.
-func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.TopDownMutatorContext) {
+func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.BottomUpMutatorContext) {
 	if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
 		enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
 		ctx.VisitDirectDeps(func(dep android.Module) {
@@ -1355,7 +1355,7 @@
 }
 
 // Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
-func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+func sanitizerRuntimeDepsMutator(mctx android.BottomUpMutatorContext) {
 	// Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
 	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
 		if c.sanitize.Properties.ForceDisable {
@@ -1437,11 +1437,11 @@
 					//"null",
 					//"shift-base",
 					//"signed-integer-overflow",
-					// TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
-					// https://llvm.org/PR19302
-					// http://reviews.llvm.org/D6974
-					// "object-size",
 				)
+
+				if mctx.Config().ReleaseBuildObjectSizeSanitizer() {
+					sanitizers = append(sanitizers, "object-size")
+				}
 			}
 			sanitizers = append(sanitizers, sanProps.Misc_undefined...)
 		}
diff --git a/cmd/extract_apks/bundle_proto/Android.bp b/cmd/extract_apks/bundle_proto/Android.bp
index e56c0fb..0abf1e2 100644
--- a/cmd/extract_apks/bundle_proto/Android.bp
+++ b/cmd/extract_apks/bundle_proto/Android.bp
@@ -10,4 +10,8 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//tools/mainline:__subpackages__",
+    ],
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 24a44b4..577c6cc 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -212,7 +212,14 @@
 }
 
 // Check if there are changes to the environment file, product variable file and
-// soong_build binary, in which case no incremental will be performed.
+// soong_build binary, in which case no incremental will be performed. For env
+// variables we check the used env file, which will be removed in soong ui if
+// there is any changes to the env variables used last time, in which case the
+// check below will fail and a full build will be attempted. If any new env
+// variables are added in the new run, soong ui won't be able to detect it, the
+// used env file check below will pass. But unless there is a soong build code
+// change, in which case the soong build binary check will fail, otherwise the
+// new env variables shouldn't have any affect.
 func incrementalValid(config android.Config, configCacheFile string) (*ConfigCache, bool) {
 	var newConfigCache ConfigCache
 	data, err := os.ReadFile(shared.JoinPath(topDir, usedEnvFile))
diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp
index e3ae6ed..272e806 100644
--- a/cmd/symbols_map/Android.bp
+++ b/cmd/symbols_map/Android.bp
@@ -30,4 +30,5 @@
     srcs: [
         "symbols_map_proto/symbols_map.pb.go",
     ],
+    visibility: ["//visibility:public"],
 }
diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp
index 3ef7668..7f9b165 100644
--- a/cmd/zip2zip/Android.bp
+++ b/cmd/zip2zip/Android.bp
@@ -27,4 +27,6 @@
         "zip2zip.go",
     ],
     testSrcs: ["zip2zip_test.go"],
+    // Used by genrules
+    visibility: ["//visibility:public"],
 }
diff --git a/compliance/license_metadata_proto/Android.bp b/compliance/license_metadata_proto/Android.bp
index 3c041e4..4761285 100644
--- a/compliance/license_metadata_proto/Android.bp
+++ b/compliance/license_metadata_proto/Android.bp
@@ -24,4 +24,8 @@
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
     ],
+    visibility: [
+        "//build/make/tools/compliance:__subpackages__",
+        "//build/soong:__subpackages__",
+    ],
 }
diff --git a/compliance/project_metadata_proto/Android.bp b/compliance/project_metadata_proto/Android.bp
index 56e76e7..0c807b2 100644
--- a/compliance/project_metadata_proto/Android.bp
+++ b/compliance/project_metadata_proto/Android.bp
@@ -24,4 +24,5 @@
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
     ],
+    visibility: ["//build/make/tools/compliance:__subpackages__"],
 }
diff --git a/docs/tidy.md b/docs/tidy.md
index ae0ca93..2e4c957 100644
--- a/docs/tidy.md
+++ b/docs/tidy.md
@@ -38,7 +38,7 @@
 clang-tidy is enabled explicitly and with a different check list:
 ```
 cc_defaults {
-    name: "bpf_defaults",
+    name: "bpf_cc_defaults",
     // snipped
     tidy: true,
     tidy_checks: [
@@ -52,7 +52,7 @@
 }
 ```
 That means in normal builds, even without `WITH_TIDY=1`,
-the modules that use `bpf_defaults` _should_ run clang-tidy
+the modules that use `bpf_cc_defaults` _should_ run clang-tidy
 over C/C++ source files with the given `tidy_checks`.
 
 However since clang-tidy warnings and its runtime cost might
diff --git a/etc/Android.bp b/etc/Android.bp
index f02c12a..580c54f 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -20,4 +20,6 @@
         "install_symlink_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 0b39062..0353992 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -147,14 +147,14 @@
 func filesystemFactory() android.Module {
 	module := &filesystem{}
 	module.filterPackagingSpec = module.filterInstallablePackagingSpec
-	initFilesystemModule(module)
+	initFilesystemModule(module, module)
 	return module
 }
 
-func initFilesystemModule(module *filesystem) {
-	module.AddProperties(&module.properties)
-	android.InitPackageModule(module)
-	module.PackagingBase.DepsCollectFirstTargetOnly = true
+func initFilesystemModule(module android.DefaultableModule, filesystemModule *filesystem) {
+	module.AddProperties(&filesystemModule.properties)
+	android.InitPackageModule(filesystemModule)
+	filesystemModule.PackagingBase.DepsCollectFirstTargetOnly = true
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 }
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index a8fd368..63cb627 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -27,7 +27,7 @@
 
 type systemImageProperties struct {
 	// Path to the input linker config json file.
-	Linker_config_src *string
+	Linker_config_src *string `android:"path"`
 }
 
 // android_system_image is a specialization of android_filesystem for the 'system' partition.
@@ -38,7 +38,7 @@
 	module.AddProperties(&module.properties)
 	module.filesystem.buildExtraFiles = module.buildExtraFiles
 	module.filesystem.filterPackagingSpec = module.filterPackagingSpec
-	initFilesystemModule(&module.filesystem)
+	initFilesystemModule(module, &module.filesystem)
 	return module
 }
 
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 7331741..f4197e6 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -22,4 +22,6 @@
         "genrule_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/genrule/genrule.go b/genrule/genrule.go
index fd72d3c..a48038b 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -25,7 +25,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -365,11 +364,6 @@
 						tools = append(tools, path.Path())
 						addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
 					}
-				case bootstrap.GoBinaryTool:
-					// A GoBinaryTool provides the install path to a tool, which will be copied.
-					p := android.PathForGoBinary(ctx, t)
-					tools = append(tools, p)
-					addLocationLabel(tag.label, toolLocation{android.Paths{p}})
 				default:
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
 					return
diff --git a/golang/Android.bp b/golang/Android.bp
new file mode 100644
index 0000000..3eae94f
--- /dev/null
+++ b/golang/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-golang",
+    pkgPath: "android/soong/golang",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "blueprint-bootstrap",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "golang.go",
+    ],
+    testSrcs: [
+        "golang_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/golang/golang.go b/golang/golang.go
new file mode 100644
index 0000000..618a085
--- /dev/null
+++ b/golang/golang.go
@@ -0,0 +1,136 @@
+// Copyright 2024 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 golang wraps the blueprint blueprint_go_binary and bootstrap_go_binary module types in versions
+// that implement android.Module that are used when building in Soong.  This simplifies the code in Soong
+// so it can always assume modules are an android.Module.
+// The original blueprint blueprint_go_binary and bootstrap_go_binary module types are still used during
+// bootstrapping, so the Android.bp entries for these module types must be compatible with both the
+// original blueprint module types and these wrapped module types.
+package golang
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
+)
+
+func init() {
+	// Wrap the blueprint Go module types with Soong ones that interoperate with the rest of the Soong modules.
+	bootstrap.GoModuleTypesAreWrapped()
+	RegisterGoModuleTypes(android.InitRegistrationContext)
+}
+
+func RegisterGoModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("bootstrap_go_package", goPackageModuleFactory)
+	ctx.RegisterModuleType("blueprint_go_binary", goBinaryModuleFactory)
+}
+
+// A GoPackage is a module for building Go packages.
+type GoPackage struct {
+	android.ModuleBase
+	bootstrap.GoPackage
+}
+
+func goPackageModuleFactory() android.Module {
+	module := &GoPackage{}
+	module.AddProperties(module.Properties()...)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func (g *GoPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// The embedded ModuleBase and bootstrap.GoPackage each implement GenerateBuildActions,
+	// the delegation has to be implemented manually to disambiguate.  Call ModuleBase's
+	// GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call
+	// bootstrap.GoPackage.GenerateBuildActions.
+	g.ModuleBase.GenerateBuildActions(ctx)
+}
+
+func (g *GoPackage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	g.GoPackage.GenerateBuildActions(ctx.BlueprintModuleContext())
+}
+
+// A GoBinary is a module for building executable binaries from Go sources.
+type GoBinary struct {
+	android.ModuleBase
+	bootstrap.GoBinary
+
+	outputFile android.Path
+}
+
+func goBinaryModuleFactory() android.Module {
+	module := &GoBinary{}
+	module.AddProperties(module.Properties()...)
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
+	return module
+}
+
+func (g *GoBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// The embedded ModuleBase and bootstrap.GoBinary each implement GenerateBuildActions,
+	// the delegation has to be implemented manually to disambiguate.  Call ModuleBase's
+	// GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call
+	// bootstrap.GoBinary.GenerateBuildActions.
+	g.ModuleBase.GenerateBuildActions(ctx)
+}
+
+func (g *GoBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Install the file in Soong instead of blueprint so that Soong knows about the install rules.
+	g.GoBinary.SetSkipInstall()
+
+	// Run the build actions from the wrapped blueprint bootstrap module.
+	g.GoBinary.GenerateBuildActions(ctx.BlueprintModuleContext())
+
+	// Translate the bootstrap module's string path into a Path
+	outputFile := android.PathForArbitraryOutput(ctx, android.Rel(ctx, ctx.Config().OutDir(), g.IntermediateFile())).WithoutRel()
+	g.outputFile = outputFile
+
+	// Don't create install rules for modules used by bootstrap, the install command line will differ from
+	// what was used during bootstrap, which will cause ninja to rebuild the module on the next run,
+	// triggering reanalysis.
+	if !usedByBootstrap(ctx.ModuleName()) {
+		installPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), outputFile)
+
+		// Modules in an unexported namespace have no install rule, only add modules in the exported namespaces
+		// to the blueprint_tools phony rules.
+		if !ctx.Config().KatiEnabled() || g.ExportedToMake() {
+			ctx.Phony("blueprint_tools", installPath)
+		}
+	}
+
+	ctx.SetOutputFiles(android.Paths{outputFile}, "")
+}
+
+func usedByBootstrap(name string) bool {
+	switch name {
+	case "loadplugins", "soong_build":
+		return true
+	default:
+		return false
+	}
+}
+
+func (g *GoBinary) HostToolPath() android.OptionalPath {
+	return android.OptionalPathForPath(g.outputFile)
+}
+
+func (g *GoBinary) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		{
+			Class:      "EXECUTABLES",
+			OutputFile: android.OptionalPathForPath(g.outputFile),
+			Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
+		},
+	}
+}
diff --git a/golang/golang_test.go b/golang/golang_test.go
new file mode 100644
index 0000000..b512144
--- /dev/null
+++ b/golang/golang_test.go
@@ -0,0 +1,51 @@
+// Copyright 2024 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 golang
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint/bootstrap"
+	"path/filepath"
+	"testing"
+)
+
+func TestGolang(t *testing.T) {
+	bp := `
+		bootstrap_go_package {
+			name: "gopkg",
+			pkgPath: "test/pkg",
+		}
+
+		blueprint_go_binary {
+			name: "gobin",
+			deps: ["gopkg"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		android.PrepareForTestWithArchMutator,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			RegisterGoModuleTypes(ctx)
+			ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps)
+			})
+		}),
+	).RunTestWithBp(t, bp)
+
+	bin := result.ModuleForTests("gobin", result.Config.BuildOSTarget.String())
+
+	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "bin/go/gobin/obj/gobin")
+	android.AssertPathsRelativeToTopEquals(t, "output files", []string{expected}, bin.OutputFiles(result.TestContext, t, ""))
+}
diff --git a/java/Android.bp b/java/Android.bp
index 9603815..926a294 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -120,4 +120,5 @@
         "test_spec_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/java/base.go b/java/base.go
index ef299b2..86ed0e7 100644
--- a/java/base.go
+++ b/java/base.go
@@ -535,7 +535,8 @@
 	linter
 
 	// list of the xref extraction files
-	kytheFiles android.Paths
+	kytheFiles       android.Paths
+	kytheKotlinFiles android.Paths
 
 	hideApexVariantFromMake bool
 
@@ -1370,7 +1371,7 @@
 
 		kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
-		kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+		j.kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
 			return
 		}
@@ -1789,14 +1790,14 @@
 				classesJar:    outputFile,
 				jarName:       jarName,
 			}
-			if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() {
+			if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting(ctx) {
 				ctx.PropertyErrorf("enable_profile_rewriting",
 					"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.",
 				)
 			}
-			if j.EnableProfileRewriting() {
-				profile := j.GetProfile()
-				if profile == "" || !j.GetProfileGuided() {
+			if j.EnableProfileRewriting(ctx) {
+				profile := j.GetProfile(ctx)
+				if profile == "" || !j.GetProfileGuided(ctx) {
 					ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true")
 				}
 				params.artProfileInput = &profile
diff --git a/java/config/Android.bp b/java/config/Android.bp
index bfe83ab..6217390 100644
--- a/java/config/Android.bp
+++ b/java/config/Android.bp
@@ -17,4 +17,8 @@
         "kotlin.go",
         "makevars.go",
     ],
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//external/error_prone/soong",
+    ],
 }
diff --git a/java/config/config.go b/java/config/config.go
index c28e070..4c1c723 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -145,6 +145,7 @@
 	pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
 	pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
 	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
+	pctx.SourcePathVariable("KotlinKytheExtractor", "prebuilts/build-tools/${hostPrebuiltTag}/bin/kotlinc_extractor")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
 	pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar")
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index e5e187c..302d021 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -50,4 +50,11 @@
 	}, " "))
 
 	pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{}, " "))
+	// Use KotlincKytheGlobalFlags to prevent kotlinc version skew issues between android and
+	// g3 kythe indexers.
+	// This is necessary because there might be instances of kotlin code in android
+	// platform that are not fully compatible with the kotlinc used in g3 kythe indexers.
+	// e.g. uninitialized variables are a warning in 1.*, but an error in 2.*
+	// https://github.com/JetBrains/kotlin/blob/master/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt#L748
+	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{"-language-version 1.9"}, " "))
 }
diff --git a/java/dex.go b/java/dex.go
index 7d42efc..e0e642c 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -120,7 +120,7 @@
 }
 
 func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool {
-	return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources)
+	return d.resourceShrinkingEnabled(ctx) && BoolDefault(d.Optimize.Optimized_shrink_resources, ctx.Config().UseOptimizedResourceShrinkingByDefault())
 }
 
 func (d *dexer) optimizeOrObfuscateEnabled() bool {
@@ -245,6 +245,16 @@
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
+	if !Bool(d.dexProperties.No_dex_container) && effectiveVersion.FinalOrFutureInt() >= 36 {
+		// W is 36, but we have not bumped the SDK version yet, so check for both.
+		if ctx.Config().PlatformSdkVersion().FinalInt() >= 36 ||
+			ctx.Config().PlatformSdkCodename() == "Wear" {
+			// TODO(b/329465418): Skip this module since it causes issue with app DRM
+			if ctx.ModuleName() != "framework-minus-apex" {
+				flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...)
+			}
+		}
+	}
 
 	// If the specified SDK level is 10000, then configure the compiler to use the
 	// current platform SDK level and to compile the build as a platform build.
@@ -390,7 +400,7 @@
 		r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String())
 		r8Deps = append(r8Deps, d.resourcesInput.Path())
 		r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String())
-		if Bool(opt.Optimized_shrink_resources) {
+		if d.dexProperties.optimizedResourceShrinkingEnabled(ctx) {
 			r8Flags = append(r8Flags, "--optimized-resource-shrinking")
 		}
 	}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 4734357..63a8634 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -147,25 +147,25 @@
 type DexpreoptProperties struct {
 	Dex_preopt struct {
 		// If false, prevent dexpreopting.  Defaults to true.
-		Enabled *bool
+		Enabled proptools.Configurable[bool] `android:"replace_instead_of_append"`
 
 		// If true, generate an app image (.art file) for this module.
-		App_image *bool
+		App_image proptools.Configurable[bool] `android:"replace_instead_of_append"`
 
 		// If true, use a checked-in profile to guide optimization.  Defaults to false unless
 		// a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
 		// that matches the name of this module, in which case it is defaulted to true.
-		Profile_guided *bool
+		Profile_guided proptools.Configurable[bool] `android:"replace_instead_of_append"`
 
 		// If set, provides the path to profile relative to the Android.bp file.  If not set,
 		// defaults to searching for a file that matches the name of this module in the default
 		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
-		Profile *string `android:"path"`
+		Profile proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 
 		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
 		// the optimized dex.
 		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
-		Enable_profile_rewriting *bool
+		Enable_profile_rewriting proptools.Configurable[bool] `android:"replace_instead_of_append"`
 	}
 
 	Dex_preopt_result struct {
@@ -244,7 +244,7 @@
 		return true
 	}
 
-	if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
+	if !d.dexpreoptProperties.Dex_preopt.Enabled.GetOrDefault(ctx, true) {
 		return true
 	}
 
@@ -433,12 +433,12 @@
 
 	if d.inputProfilePathOnHost != nil {
 		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
-	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
+	} else if d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, true) && !forPrebuiltApex(ctx) {
 		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
-		if d.EnableProfileRewriting() {
+		if d.EnableProfileRewriting(ctx) {
 			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
 			profileIsTextListing = true
-		} else if profile := d.GetProfile(); profile != "" {
+		} else if profile := d.GetProfile(ctx); profile != "" {
 			// If dex_preopt.profile_guided is not set, default it based on the existence of the
 			// dexprepot.profile option or the profile class listing.
 			profileClassListing = android.OptionalPathForPath(
@@ -458,6 +458,8 @@
 	// Use the dexJar to create a unique scope for each
 	dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext())
 
+	appImage := d.dexpreoptProperties.Dex_preopt.App_image.Get(ctx)
+
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
 		Name:            libName,
@@ -486,8 +488,8 @@
 		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
 		PreoptBootClassPathDexLocations: dexLocations,
 
-		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
-		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
+		NoCreateAppImage:    !appImage.GetOrDefault(true),
+		ForceCreateAppImage: appImage.GetOrDefault(false),
 
 		PresignedPrebuilt: d.isPresignedPrebuilt,
 	}
@@ -657,16 +659,16 @@
 	d.shouldDisableDexpreopt = true
 }
 
-func (d *dexpreopter) EnableProfileRewriting() bool {
-	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting)
+func (d *dexpreopter) EnableProfileRewriting(ctx android.BaseModuleContext) bool {
+	return d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting.GetOrDefault(ctx, false)
 }
 
-func (d *dexpreopter) GetProfile() string {
-	return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile)
+func (d *dexpreopter) GetProfile(ctx android.BaseModuleContext) string {
+	return d.dexpreoptProperties.Dex_preopt.Profile.GetOrDefault(ctx, "")
 }
 
-func (d *dexpreopter) GetProfileGuided() bool {
-	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided)
+func (d *dexpreopter) GetProfileGuided(ctx android.BaseModuleContext) bool {
+	return d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, false)
 }
 
 func (d *dexpreopter) GetRewrittenProfile() android.Path {
diff --git a/java/java.go b/java/java.go
index 95f4fd8..d63bbe6 100644
--- a/java/java.go
+++ b/java/java.go
@@ -356,12 +356,17 @@
 // TODO(jungjw): Move this to kythe.go once it's created.
 type xref interface {
 	XrefJavaFiles() android.Paths
+	XrefKotlinFiles() android.Paths
 }
 
 func (j *Module) XrefJavaFiles() android.Paths {
 	return j.kytheFiles
 }
 
+func (j *Module) XrefKotlinFiles() android.Paths {
+	return j.kytheKotlinFiles
+}
+
 func (d dependencyTag) PropagateAconfigValidation() bool {
 	return d.static
 }
@@ -3304,15 +3309,20 @@
 
 func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var xrefTargets android.Paths
+	var xrefKotlinTargets android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
 		if javaModule, ok := module.(xref); ok {
 			xrefTargets = append(xrefTargets, javaModule.XrefJavaFiles()...)
+			xrefKotlinTargets = append(xrefKotlinTargets, javaModule.XrefKotlinFiles()...)
 		}
 	})
 	// TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
 	if len(xrefTargets) > 0 {
 		ctx.Phony("xref_java", xrefTargets...)
 	}
+	if len(xrefKotlinTargets) > 0 {
+		ctx.Phony("xref_kotlin", xrefKotlinTargets...)
+	}
 }
 
 var Bool = proptools.Bool
@@ -3335,6 +3345,10 @@
 	if lib, ok := depModule.(SdkLibraryDependency); ok && lib.sharedLibrary() {
 		// A shared SDK library. This should be added as a top-level CLC element.
 		sdkLib = &depName
+	} else if lib, ok := depModule.(SdkLibraryComponentDependency); ok && lib.OptionalSdkLibraryImplementation() != nil {
+		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation())+".impl" {
+			sdkLib = lib.OptionalSdkLibraryImplementation()
+		}
 	} else if ulib, ok := depModule.(ProvidesUsesLib); ok {
 		// A non-SDK library disguised as an SDK library by the means of `provides_uses_lib`
 		// property. This should be handled in the same way as a shared SDK library.
diff --git a/java/kotlin.go b/java/kotlin.go
index c28bc3f..f42d163 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -64,6 +64,28 @@
 	"kotlincFlags", "classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "classesDir",
 	"headerClassesDir", "headerJar", "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name")
 
+var kotlinKytheExtract = pctx.AndroidStaticRule("kotlinKythe",
+	blueprint.RuleParams{
+		Command: `rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` +
+			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" -f "*.kt" $srcJars && ` +
+			`${config.KotlinKytheExtractor} -corpus ${kytheCorpus} --srcs @$out.rsp --srcs @"$srcJarDir/list" $commonSrcFilesList --cp @$classpath -o $out --kotlin_out $outJar ` +
+			// wrap the additional kotlin args.
+			// Skip Xbuild file, pass the cp explicitly.
+			// Skip header jars, those should not have an effect on kythe results.
+			` --args '${config.KotlincGlobalFlags} ` +
+			` ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
+			` $kotlincFlags -jvm-target $kotlinJvmTarget ` +
+			`${config.KotlincKytheGlobalFlags}'`,
+		CommandDeps: []string{
+			"${config.KotlinKytheExtractor}",
+			"${config.ZipSyncCmd}",
+		},
+		Rspfile:        "$out.rsp",
+		RspfileContent: "$in",
+	},
+	"classpath", "kotlincFlags", "commonSrcFilesList", "kotlinJvmTarget", "outJar", "srcJars", "srcJarDir",
+)
+
 func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Paths) android.OptionalPath {
 	if len(commonSrcFiles) > 0 {
 		// The list of common_srcs may be too long to put on the command line, but
@@ -81,7 +103,7 @@
 }
 
 // kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile.
-func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath,
+func (j *Module) kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath,
 	srcFiles, commonSrcFiles, srcJars android.Paths,
 	flags javaBuilderFlags) {
 
@@ -127,6 +149,31 @@
 			"name":              kotlinName,
 		},
 	})
+
+	// Emit kythe xref rule
+	if (ctx.Config().EmitXrefRules()) && ctx.Module() == ctx.PrimaryModule() {
+		extractionFile := outputFile.ReplaceExtension(ctx, "kzip")
+		args := map[string]string{
+			"classpath":       classpathRspFile.String(),
+			"kotlincFlags":    flags.kotlincFlags,
+			"kotlinJvmTarget": flags.javaVersion.StringForKotlinc(),
+			"outJar":          outputFile.String(),
+			"srcJars":         strings.Join(srcJars.Strings(), " "),
+			"srcJarDir":       android.PathForModuleOut(ctx, "kotlinc", "srcJars.xref").String(),
+		}
+		if commonSrcsList.Valid() {
+			args["commonSrcFilesList"] = "--common_srcs @" + commonSrcsList.String()
+		}
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        kotlinKytheExtract,
+			Description: "kotlinKythe",
+			Output:      extractionFile,
+			Inputs:      srcFiles,
+			Implicits:   deps,
+			Args:        args,
+		})
+		j.kytheKotlinFiles = append(j.kytheKotlinFiles, extractionFile)
+	}
 }
 
 var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupports{Goma: true},
diff --git a/java/metalava/Android.bp b/java/metalava/Android.bp
index ccbd191..6bf1832 100644
--- a/java/metalava/Android.bp
+++ b/java/metalava/Android.bp
@@ -15,4 +15,5 @@
 filegroup {
     name: "metalava-config-files",
     srcs: ["*-config.xml"],
+    visibility: ["//visibility:public"],
 }
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 67ed84e..5b145c6 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -110,6 +110,7 @@
 	p.installConfigFile = android.PathForModuleInstall(ctx, "etc", "compatconfig", p.configFile.Base())
 	rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
 	ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile)
+	ctx.SetOutputFiles(android.Paths{p.configFile}, "")
 }
 
 func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/java/ravenwood.go b/java/ravenwood.go
index bb136cf..9239bbd 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -33,8 +33,8 @@
 var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"}
 var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"}
 var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"}
-var ravenwoodDataTag = dependencyTag{name: "ravenwooddata"}
 var ravenwoodTestResourceApkTag = dependencyTag{name: "ravenwoodtestresapk"}
+var ravenwoodTestInstResourceApkTag = dependencyTag{name: "ravenwoodtest-inst-res-apk"}
 
 const ravenwoodUtilsName = "ravenwood-utils"
 const ravenwoodRuntimeName = "ravenwood-runtime"
@@ -57,11 +57,17 @@
 	Jni_libs []string
 
 	// Specify another android_app module here to copy it to the test directory, so that
-	// the ravenwood test can access it.
+	// the ravenwood test can access it. This APK will be loaded as resources of the test
+	// target app.
 	// TODO: For now, we simply refer to another android_app module and copy it to the
 	// test directory. Eventually, android_ravenwood_test should support all the resource
 	// related properties and build resources from the `res/` directory.
 	Resource_apk *string
+
+	// Specify another android_app module here to copy it to the test directory, so that
+	// the ravenwood test can access it. This APK will be loaded as resources of the test
+	// instrumentation app itself.
+	Inst_resource_apk *string
 }
 
 type ravenwoodTest struct {
@@ -128,6 +134,10 @@
 	if resourceApk := proptools.String(r.ravenwoodTestProperties.Resource_apk); resourceApk != "" {
 		ctx.AddVariationDependencies(nil, ravenwoodTestResourceApkTag, resourceApk)
 	}
+
+	if resourceApk := proptools.String(r.ravenwoodTestProperties.Inst_resource_apk); resourceApk != "" {
+		ctx.AddVariationDependencies(nil, ravenwoodTestInstResourceApkTag, resourceApk)
+	}
 }
 
 func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -195,13 +205,16 @@
 	}
 
 	resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks")
-	if resApk := ctx.GetDirectDepsWithTag(ravenwoodTestResourceApkTag); len(resApk) > 0 {
-		for _, installFile := range android.OtherModuleProviderOrDefault(
-			ctx, resApk[0], android.InstallFilesProvider).InstallFiles {
-			installResApk := ctx.InstallFile(resApkInstallPath, "ravenwood-res.apk", installFile)
+
+	copyResApk := func(tag blueprint.DependencyTag, toFileName string) {
+		if resApk := ctx.GetDirectDepsWithTag(tag); len(resApk) > 0 {
+			installFile := android.OutputFileForModule(ctx, resApk[0], "")
+			installResApk := ctx.InstallFile(resApkInstallPath, toFileName, installFile)
 			installDeps = append(installDeps, installResApk)
 		}
 	}
+	copyResApk(ravenwoodTestResourceApkTag, "ravenwood-res.apk")
+	copyResApk(ravenwoodTestInstResourceApkTag, "ravenwood-inst-res.apk")
 
 	// Install our JAR with all dependencies
 	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
@@ -228,7 +241,10 @@
 	Jni_libs []string
 
 	// We use this to copy framework-res.apk to the ravenwood runtime directory.
-	Data []string
+	Data []string `android:"path,arch_variant"`
+
+	// We use this to copy font files to the ravenwood runtime directory.
+	Fonts []string `android:"path,arch_variant"`
 }
 
 type ravenwoodLibgroup struct {
@@ -267,9 +283,6 @@
 	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
-	for _, data := range r.ravenwoodLibgroupProperties.Data {
-		ctx.AddVariationDependencies(nil, ravenwoodDataTag, data)
-	}
 }
 
 func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -309,12 +322,17 @@
 	}
 
 	dataInstallPath := installPath.Join(ctx, "ravenwood-data")
-	for _, data := range r.ravenwoodLibgroupProperties.Data {
-		libModule := ctx.GetDirectDepWithTag(data, ravenwoodDataTag)
-		file := android.OutputFileForModule(ctx, libModule, "")
+	data := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Data)
+	for _, file := range data {
 		ctx.InstallFile(dataInstallPath, file.Base(), file)
 	}
 
+	fontsInstallPath := installPath.Join(ctx, "fonts")
+	fonts := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Fonts)
+	for _, file := range fonts {
+		ctx.InstallFile(fontsInstallPath, file.Base(), file)
+	}
+
 	// Normal build should perform install steps
 	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
 }
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index d26db93..753a118 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -19,6 +19,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/etc"
 )
 
 var prepareRavenwoodRuntime = android.GroupFixturePreparers(
@@ -59,11 +60,19 @@
 		}
 		android_app {
 			name: "app1",
-            sdk_version: "current",
+			sdk_version: "current",
 		}
 		android_app {
 			name: "app2",
-            sdk_version: "current",
+			sdk_version: "current",
+		}
+		android_app {
+			name: "app3",
+			sdk_version: "current",
+		}
+		prebuilt_font {
+			name: "Font.ttf",
+			src: "Font.ttf",
 		}
 		android_ravenwood_libgroup {
 			name: "ravenwood-runtime",
@@ -76,7 +85,10 @@
 				"ravenwood-runtime-jni2",
 			],
 			data: [
-				"app1",
+				":app1",
+			],
+			fonts: [
+				":Font.ttf"
 			],
 		}
 		android_ravenwood_libgroup {
@@ -97,6 +109,7 @@
 
 	ctx := android.GroupFixturePreparers(
 		PrepareForIntegrationTestWithJava,
+		etc.PrepareForTestWithPrebuiltEtc,
 		prepareRavenwoodRuntime,
 	).RunTest(t)
 
@@ -114,6 +127,7 @@
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/ravenwood-data/app1.apk")
+	runtime.Output(installPathPrefix + "/ravenwood-runtime/fonts/Font.ttf")
 	utils := ctx.ModuleForTests("ravenwood-utils", "android_common")
 	utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar")
 }
@@ -125,29 +139,30 @@
 
 	ctx := android.GroupFixturePreparers(
 		PrepareForIntegrationTestWithJava,
+		etc.PrepareForTestWithPrebuiltEtc,
 		prepareRavenwoodRuntime,
 	).RunTestWithBp(t, `
-	cc_library_shared {
-		name: "jni-lib1",
-		host_supported: true,
-		srcs: ["jni.cpp"],
-	}
-	cc_library_shared {
-		name: "jni-lib2",
-		host_supported: true,
-		srcs: ["jni.cpp"],
-		stem: "libblue",
-		shared_libs: [
-			"jni-lib3",
-		],
-	}
-	cc_library_shared {
-		name: "jni-lib3",
-		host_supported: true,
-		srcs: ["jni.cpp"],
-		stem: "libpink",
-	}
-	android_ravenwood_test {
+		cc_library_shared {
+			name: "jni-lib1",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+		}
+		cc_library_shared {
+			name: "jni-lib2",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+			stem: "libblue",
+			shared_libs: [
+				"jni-lib3",
+			],
+		}
+		cc_library_shared {
+			name: "jni-lib3",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+			stem: "libpink",
+		}
+		android_ravenwood_test {
 			name: "ravenwood-test",
 			srcs: ["Test.java"],
 			jni_libs: [
@@ -156,6 +171,7 @@
 				"ravenwood-runtime-jni2",
 			],
 			resource_apk: "app2",
+			inst_resource_apk: "app3",
 			sdk_version: "test_current",
 		}
 	`)
@@ -183,6 +199,7 @@
 	module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so")
 	module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-res.apk")
+	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-inst-res.apk")
 
 	// ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
 	for _, o := range module.AllOutputs() {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 725aaed..9f0564a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1727,6 +1727,7 @@
 	staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
 	props := struct {
 		Name           *string
+		Enabled        proptools.Configurable[bool]
 		Visibility     []string
 		Libs           []string
 		Static_libs    proptools.Configurable[[]string]
@@ -1734,6 +1735,7 @@
 		Stem           *string
 	}{
 		Name:       proptools.StringPtr(module.implLibraryModuleName()),
+		Enabled:    module.EnabledProperty(),
 		Visibility: visibility,
 
 		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
@@ -1762,6 +1764,7 @@
 
 type libraryProperties struct {
 	Name           *string
+	Enabled        proptools.Configurable[bool]
 	Visibility     []string
 	Srcs           []string
 	Installable    *bool
@@ -1788,6 +1791,7 @@
 
 func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
 	props := libraryProperties{}
+	props.Enabled = module.EnabledProperty()
 	props.Visibility = []string{"//visibility:override", "//visibility:private"}
 	// sources are generated from the droiddoc
 	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
@@ -1838,6 +1842,7 @@
 func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
 	props := struct {
 		Name                             *string
+		Enabled                          proptools.Configurable[bool]
 		Visibility                       []string
 		Srcs                             []string
 		Installable                      *bool
@@ -1879,6 +1884,7 @@
 	// * libs (static_libs/libs)
 
 	props.Name = proptools.StringPtr(name)
+	props.Enabled = module.EnabledProperty()
 	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility)
 	props.Srcs = append(props.Srcs, module.properties.Srcs...)
 	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
@@ -2004,6 +2010,7 @@
 func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
 		Name              *string
+		Enabled           proptools.Configurable[bool]
 		Visibility        []string
 		Api_contributions []string
 		Libs              proptools.Configurable[[]string]
@@ -2016,6 +2023,7 @@
 	}{}
 
 	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
+	props.Enabled = module.EnabledProperty()
 	props.Visibility = []string{"//visibility:override", "//visibility:private"}
 
 	apiContributions := []string{}
@@ -2066,6 +2074,7 @@
 func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
 	props := libraryProperties{}
 
+	props.Enabled = module.EnabledProperty()
 	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
 	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
@@ -2158,6 +2167,7 @@
 	}
 	props := struct {
 		Name                      *string
+		Enabled                   proptools.Configurable[bool]
 		Lib_name                  *string
 		Apex_available            []string
 		On_bootclasspath_since    *string
@@ -2168,6 +2178,7 @@
 		Uses_libs_dependencies    []string
 	}{
 		Name:                      proptools.StringPtr(module.xmlPermissionsModuleName()),
+		Enabled:                   module.EnabledProperty(),
 		Lib_name:                  proptools.StringPtr(module.BaseModuleName()),
 		Apex_available:            module.ApexProperties.Apex_available,
 		On_bootclasspath_since:    module.commonSdkLibraryProperties.On_bootclasspath_since,
@@ -2263,11 +2274,6 @@
 // runtime libs and xml file. If requested, the stubs and docs are created twice
 // once for public API level and once for system API level
 func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookContext) {
-	// If the module has been disabled then don't create any child modules.
-	if !module.Enabled(mctx) {
-		return
-	}
-
 	if len(module.properties.Srcs) == 0 {
 		mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
 		return
diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp
index 754e7bf..a930502 100644
--- a/linkerconfig/proto/Android.bp
+++ b/linkerconfig/proto/Android.bp
@@ -15,6 +15,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    visibility: ["//system/linkerconfig"],
 }
 
 python_library_host {
diff --git a/phony/Android.bp b/phony/Android.bp
index db5efc9..2e250c6 100644
--- a/phony/Android.bp
+++ b/phony/Android.bp
@@ -13,4 +13,5 @@
         "phony.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/response/Android.bp b/response/Android.bp
index e19981f..2f319fe 100644
--- a/response/Android.bp
+++ b/response/Android.bp
@@ -13,4 +13,8 @@
     testSrcs: [
         "response_test.go",
     ],
+    visibility: [
+        "//build/make/tools/compliance",
+        "//build/soong:__subpackages__",
+    ],
 }
diff --git a/rust/Android.bp b/rust/Android.bp
index 53c9462..781f325 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -61,4 +61,5 @@
         "test_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/rust/clippy.go b/rust/clippy.go
index 6f0ed7f..426fd73 100644
--- a/rust/clippy.go
+++ b/rust/clippy.go
@@ -38,11 +38,14 @@
 }
 
 func (c *clippy) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
-	enabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints)
+	dirEnabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints)
 	if err != nil {
 		ctx.PropertyErrorf("clippy_lints", err.Error())
 	}
-	flags.Clippy = enabled
+
+	envDisable := ctx.Config().IsEnvTrue("SOONG_DISABLE_CLIPPY")
+
+	flags.Clippy = dirEnabled && !envDisable
 	flags.ClippyFlags = append(flags.ClippyFlags, lints)
 	return flags, deps
 }
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 79ea7a1..25f7580 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -24,4 +24,8 @@
         "x86_64_device.go",
         "arm64_linux_host.go",
     ],
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//prebuilts/rust/soong",
+    ],
 }
diff --git a/rust/config/global.go b/rust/config/global.go
index 990a643..68a74c2 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/rust/config")
 
-	RustDefaultVersion = "1.80.1"
+	RustDefaultVersion = "1.81.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/coverage.go b/rust/coverage.go
index 91a7806..381fcf1 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -87,10 +87,6 @@
 }
 
 func (cov *coverage) begin(ctx BaseModuleContext) {
-	if ctx.Host() {
-		// Host coverage not yet supported.
-	} else {
-		// Update useSdk and sdkVersion args if Rust modules become SDK aware.
-		cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "")
-	}
+	// Update useSdk and sdkVersion args if Rust modules become SDK aware.
+	cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "")
 }
diff --git a/scripts/check_prebuilt_presigned_apk.py b/scripts/check_prebuilt_presigned_apk.py
index abab2e1..db64f90 100755
--- a/scripts/check_prebuilt_presigned_apk.py
+++ b/scripts/check_prebuilt_presigned_apk.py
@@ -36,7 +36,7 @@
                 if fail:
                     sys.exit(args.apk + ': Contains compressed JNI libraries')
                 return True
-            # It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
+            # It's ok for non-privileged apps to have compressed dex files
             if args.privileged and args.uncompress_priv_app_dex:
                 if info.filename.endswith('.dex') and info.compress_type != zipfile.ZIP_STORED:
                     if fail:
@@ -46,6 +46,10 @@
 
 
 def main():
+    # This script enforces requirements for presigned apps as documented in:
+    # go/gms-uncompressed-jni-slides
+    # https://docs.partner.android.com/gms/building/integrating/jni-libs
+    # https://docs.partner.android.com/gms/policies/domains/mba#jni-lib
     parser = argparse.ArgumentParser()
     parser.add_argument('--aapt2', help = "the path to the aapt2 executable")
     parser.add_argument('--zipalign', help = "the path to the zipalign executable")
diff --git a/scripts/manifest.py b/scripts/manifest.py
index 81f9c61..32603e8 100755
--- a/scripts/manifest.py
+++ b/scripts/manifest.py
@@ -23,9 +23,40 @@
 android_ns = 'http://schemas.android.com/apk/res/android'
 
 
+def get_or_create_applications(doc, manifest):
+  """Get all <application> tags from the manifest, or create one if none exist.
+  Multiple <application> tags may exist when manifest feature flagging is used.
+  """
+  applications = get_children_with_tag(manifest, 'application')
+  if len(applications) == 0:
+    application = doc.createElement('application')
+    indent = get_indent(manifest.firstChild, 1)
+    first = manifest.firstChild
+    manifest.insertBefore(doc.createTextNode(indent), first)
+    manifest.insertBefore(application, first)
+    applications.append(application)
+  return applications
+
+
+def get_or_create_uses_sdks(doc, manifest):
+  """Get all <uses-sdk> tags from the manifest, or create one if none exist.
+  Multiple <uses-sdk> tags may exist when manifest feature flagging is used.
+  """
+  uses_sdks = get_children_with_tag(manifest, 'uses-sdk')
+  if len(uses_sdks) == 0:
+    uses_sdk = doc.createElement('uses-sdk')
+    indent = get_indent(manifest.firstChild, 1)
+    manifest.insertBefore(uses_sdk, manifest.firstChild)
+
+    # Insert an indent before uses-sdk to line it up with the indentation of the
+    # other children of the <manifest> tag.
+    manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
+    uses_sdks.append(uses_sdk)
+  return uses_sdks
+
 def get_children_with_tag(parent, tag_name):
   children = []
-  for child in  parent.childNodes:
+  for child in parent.childNodes:
     if child.nodeType == minidom.Node.ELEMENT_NODE and \
        child.tagName == tag_name:
       children.append(child)
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index b101259..1e32d1d 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -25,10 +25,7 @@
 import sys
 from xml.dom import minidom
 
-from manifest import android_ns
-from manifest import get_children_with_tag
-from manifest import parse_manifest
-from manifest import write_xml
+from manifest import *
 
 
 class ManifestMismatchError(Exception):
@@ -122,7 +119,7 @@
     # handles module names specified in Android.bp properties. However not all
     # <uses-library> entries in the manifest correspond to real modules: some of
     # the optional libraries may be missing at build time. Therefor this script
-    # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the
+    # accepts raw module names as spelled in Android.bp/Android.mk and trims the
     # optional namespace part manually.
     required = trim_namespace_parts(required)
     optional = trim_namespace_parts(optional)
@@ -205,15 +202,9 @@
     """Extract <uses-library> tags from the manifest."""
 
     manifest = parse_manifest(xml)
-    elems = get_children_with_tag(manifest, 'application')
-    if len(elems) > 1:
-        raise RuntimeError('found multiple <application> tags')
-    if not elems:
-        return [], [], []
-
-    application = elems[0]
-
-    libs = get_children_with_tag(application, 'uses-library')
+    libs = [child
+            for application in get_or_create_applications(xml, manifest)
+            for child in get_children_with_tag(application, 'uses-library')]
 
     required = [uses_library_name(x) for x in libs if uses_library_required(x)]
     optional = [
@@ -266,7 +257,7 @@
     manifest: manifest (either parsed XML or aapt dump of APK)
     is_apk:   if the manifest comes from an APK or an XML file
     """
-    if is_apk: #pylint: disable=no-else-return
+    if is_apk:  #pylint: disable=no-else-return
         return extract_target_sdk_version_apk(manifest)
     else:
         return extract_target_sdk_version_xml(manifest)
@@ -376,7 +367,7 @@
 
             # Create a status file that is empty on success, or contains an
             # error message on failure. When exceptions are suppressed,
-            # dexpreopt command command will check file size to determine if
+            # dexpreopt command will check file size to determine if
             # the check has failed.
             if args.enforce_uses_libraries_status:
                 with open(args.enforce_uses_libraries_status, 'w') as f:
@@ -386,7 +377,7 @@
         if args.extract_target_sdk_version:
             try:
                 print(extract_target_sdk_version(manifest, is_apk))
-            except: #pylint: disable=bare-except
+            except:  #pylint: disable=bare-except
                 # Failed; don't crash, return "any" SDK version. This will
                 # result in dexpreopt not adding any compatibility libraries.
                 print(10000)
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 8003b3e..abe0d8b 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -44,8 +44,8 @@
 class EnforceUsesLibrariesTest(unittest.TestCase):
     """Unit tests for add_extract_native_libs function."""
 
-    def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[],
-                 missing_optional_uses_libraries=[]): #pylint: disable=dangerous-default-value
+    def run_test(self, xml, apk, uses_libraries=(), optional_uses_libraries=(),
+                 missing_optional_uses_libraries=()):  #pylint: disable=dangerous-default-value
         doc = minidom.parseString(xml)
         try:
             relax = False
@@ -114,14 +114,14 @@
         self.assertFalse(matches)
 
     def test_missing_uses_library(self):
-        xml = self.xml_tmpl % ('')
-        apk = self.apk_tmpl % ('')
+        xml = self.xml_tmpl % ''
+        apk = self.apk_tmpl % ''
         matches = self.run_test(xml, apk, uses_libraries=['foo'])
         self.assertFalse(matches)
 
     def test_missing_optional_uses_library(self):
-        xml = self.xml_tmpl % ('')
-        apk = self.apk_tmpl % ('')
+        xml = self.xml_tmpl % ''
+        apk = self.apk_tmpl % ''
         matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
         self.assertFalse(matches)
 
@@ -234,6 +234,32 @@
             optional_uses_libraries=['//x/y/z:bar'])
         self.assertTrue(matches)
 
+    def test_multiple_applications(self):
+        xml = """<?xml version="1.0" encoding="utf-8"?>
+            <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+                <application android:featureFlag="foo">
+                    <uses-library android:name="foo" />
+                    <uses-library android:name="bar" android:required="false" />
+                </application>
+                <application android:featureFlag="!foo">
+                    <uses-library android:name="foo" />
+                    <uses-library android:name="qux" android:required="false" />
+                </application>
+            </manifest>
+        """
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo'),
+            uses_library_apk('bar', required_apk(False)),
+            uses_library_apk('foo'),
+            uses_library_apk('qux', required_apk(False))
+        ]))
+        matches = self.run_test(
+            xml,
+            apk,
+            uses_libraries=['//x/y/z:foo'],
+            optional_uses_libraries=['//x/y/z:bar', '//x/y/z:qux'])
+        self.assertTrue(matches)
+
 
 class ExtractTargetSdkVersionTest(unittest.TestCase):
 
@@ -256,12 +282,12 @@
         "targetSdkVersion:'%s'\n"
         "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")
 
-    def test_targert_sdk_version_28(self):
+    def test_target_sdk_version_28(self):
         xml = self.xml_tmpl % '28'
         apk = self.apk_tmpl % '28'
         self.run_test(xml, apk, '28')
 
-    def test_targert_sdk_version_29(self):
+    def test_target_sdk_version_29(self):
         xml = self.xml_tmpl % '29'
         apk = self.apk_tmpl % '29'
         self.run_test(xml, apk, '29')
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 58079aa..9847ad5 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -23,15 +23,7 @@
 from xml.dom import minidom
 
 
-from manifest import android_ns
-from manifest import compare_version_gt
-from manifest import ensure_manifest_android_ns
-from manifest import find_child_with_attribute
-from manifest import get_children_with_tag
-from manifest import get_indent
-from manifest import parse_manifest
-from manifest import write_xml
-
+from manifest import *
 
 def parse_args():
   """Parse commandline arguments."""
@@ -48,9 +40,9 @@
   parser.add_argument('--library', dest='library', action='store_true',
                       help='manifest is for a static library')
   parser.add_argument('--uses-library', dest='uses_libraries', action='append',
-                      help='specify additional <uses-library> tag to add. android:requred is set to true')
+                      help='specify additional <uses-library> tag to add. android:required is set to true')
   parser.add_argument('--optional-uses-library', dest='optional_uses_libraries', action='append',
-                      help='specify additional <uses-library> tag to add. android:requred is set to false')
+                      help='specify additional <uses-library> tag to add. android:required is set to false')
   parser.add_argument('--uses-non-sdk-api', dest='uses_non_sdk_api', action='store_true',
                       help='manifest is for a package built against the platform')
   parser.add_argument('--logging-parent', dest='logging_parent', default='',
@@ -91,47 +83,33 @@
 
   manifest = parse_manifest(doc)
 
-  # Get or insert the uses-sdk element
-  uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
-  if len(uses_sdk) > 1:
-    raise RuntimeError('found multiple uses-sdk elements')
-  elif len(uses_sdk) == 1:
-    element = uses_sdk[0]
-  else:
-    element = doc.createElement('uses-sdk')
-    indent = get_indent(manifest.firstChild, 1)
-    manifest.insertBefore(element, manifest.firstChild)
-
-    # Insert an indent before uses-sdk to line it up with the indentation of the
-    # other children of the <manifest> tag.
-    manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
-
-  # Get or insert the minSdkVersion attribute.  If it is already present, make
-  # sure it as least the requested value.
-  min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
-  if min_attr is None:
-    min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
-    min_attr.value = min_sdk_version
-    element.setAttributeNode(min_attr)
-  else:
-    if compare_version_gt(min_sdk_version, min_attr.value):
+  for uses_sdk in get_or_create_uses_sdks(doc, manifest):
+    # Get or insert the minSdkVersion attribute.  If it is already present, make
+    # sure it as least the requested value.
+    min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion')
+    if min_attr is None:
+      min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
       min_attr.value = min_sdk_version
-
-  # Insert the targetSdkVersion attribute if it is missing.  If it is already
-  # present leave it as is.
-  target_attr = element.getAttributeNodeNS(android_ns, 'targetSdkVersion')
-  if target_attr is None:
-    target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion')
-    if library:
-      # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but
-      # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it
-      # is empty.  Set it to something low so that it will be overriden by the
-      # main manifest, but high enough that it doesn't cause implicit
-      # permissions grants.
-      target_attr.value = '16'
+      uses_sdk.setAttributeNode(min_attr)
     else:
-      target_attr.value = target_sdk_version
-    element.setAttributeNode(target_attr)
+      if compare_version_gt(min_sdk_version, min_attr.value):
+        min_attr.value = min_sdk_version
+
+    # Insert the targetSdkVersion attribute if it is missing.  If it is already
+    # present leave it as is.
+    target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion')
+    if target_attr is None:
+      target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion')
+      if library:
+        # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but
+        # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it
+        # is empty.  Set it to something low so that it will be overridden by the
+        # main manifest, but high enough that it doesn't cause implicit
+        # permissions grants.
+        target_attr.value = '16'
+      else:
+        target_attr.value = target_sdk_version
+      uses_sdk.setAttributeNode(target_attr)
 
 
 def add_logging_parent(doc, logging_parent_value):
@@ -147,37 +125,27 @@
   manifest = parse_manifest(doc)
 
   logging_parent_key = 'android.content.pm.LOGGING_PARENT'
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    indent = get_indent(application.firstChild, 2)
 
-  indent = get_indent(application.firstChild, 2)
-
-  last = application.lastChild
-  if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
-    last = None
-
-  if not find_child_with_attribute(application, 'meta-data', android_ns,
-                                   'name', logging_parent_key):
-    ul = doc.createElement('meta-data')
-    ul.setAttributeNS(android_ns, 'android:name', logging_parent_key)
-    ul.setAttributeNS(android_ns, 'android:value', logging_parent_value)
-    application.insertBefore(doc.createTextNode(indent), last)
-    application.insertBefore(ul, last)
     last = application.lastChild
+    if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+      last = None
 
-  # align the closing tag with the opening tag if it's not
-  # indented
-  if last and last.nodeType != minidom.Node.TEXT_NODE:
-    indent = get_indent(application.previousSibling, 1)
-    application.appendChild(doc.createTextNode(indent))
+    if not find_child_with_attribute(application, 'meta-data', android_ns,
+                                     'name', logging_parent_key):
+      ul = doc.createElement('meta-data')
+      ul.setAttributeNS(android_ns, 'android:name', logging_parent_key)
+      ul.setAttributeNS(android_ns, 'android:value', logging_parent_value)
+      application.insertBefore(doc.createTextNode(indent), last)
+      application.insertBefore(ul, last)
+      last = application.lastChild
+
+    # align the closing tag with the opening tag if it's not
+    # indented
+    if last and last.nodeType != minidom.Node.TEXT_NODE:
+      indent = get_indent(application.previousSibling, 1)
+      application.appendChild(doc.createTextNode(indent))
 
 
 def add_uses_libraries(doc, new_uses_libraries, required):
@@ -192,42 +160,32 @@
   """
 
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    indent = get_indent(application.firstChild, 2)
 
-  indent = get_indent(application.firstChild, 2)
+    last = application.lastChild
+    if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+      last = None
 
-  last = application.lastChild
-  if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
-    last = None
+    for name in new_uses_libraries:
+      if find_child_with_attribute(application, 'uses-library', android_ns,
+                                   'name', name) is not None:
+        # If the uses-library tag of the same 'name' attribute value exists,
+        # respect it.
+        continue
 
-  for name in new_uses_libraries:
-    if find_child_with_attribute(application, 'uses-library', android_ns,
-                                 'name', name) is not None:
-      # If the uses-library tag of the same 'name' attribute value exists,
-      # respect it.
-      continue
+      ul = doc.createElement('uses-library')
+      ul.setAttributeNS(android_ns, 'android:name', name)
+      ul.setAttributeNS(android_ns, 'android:required', str(required).lower())
 
-    ul = doc.createElement('uses-library')
-    ul.setAttributeNS(android_ns, 'android:name', name)
-    ul.setAttributeNS(android_ns, 'android:required', str(required).lower())
+      application.insertBefore(doc.createTextNode(indent), last)
+      application.insertBefore(ul, last)
 
-    application.insertBefore(doc.createTextNode(indent), last)
-    application.insertBefore(ul, last)
-
-  # align the closing tag with the opening tag if it's not
-  # indented
-  if application.lastChild.nodeType != minidom.Node.TEXT_NODE:
-    indent = get_indent(application.previousSibling, 1)
-    application.appendChild(doc.createTextNode(indent))
+    # align the closing tag with the opening tag if it's not
+    # indented
+    if application.lastChild.nodeType != minidom.Node.TEXT_NODE:
+      indent = get_indent(application.previousSibling, 1)
+      application.appendChild(doc.createTextNode(indent))
 
 
 def add_uses_non_sdk_api(doc):
@@ -240,111 +198,63 @@
   """
 
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
-
-  attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi')
-  if attr is None:
-    attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi')
-    attr.value = 'true'
-    application.setAttributeNode(attr)
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi')
+    if attr is None:
+      attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi')
+      attr.value = 'true'
+      application.setAttributeNode(attr)
 
 
 def add_use_embedded_dex(doc):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
-
-  attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex')
-  if attr is None:
-    attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex')
-    attr.value = 'true'
-    application.setAttributeNode(attr)
-  elif attr.value != 'true':
-    raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex')
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex')
+    if attr is None:
+      attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex')
+      attr.value = 'true'
+      application.setAttributeNode(attr)
+    elif attr.value != 'true':
+      raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex')
 
 
 def add_extract_native_libs(doc, extract_native_libs):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
-
-  value = str(extract_native_libs).lower()
-  attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs')
-  if attr is None:
-    attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs')
-    attr.value = value
-    application.setAttributeNode(attr)
-  elif attr.value != value:
-    raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' %
-                       (attr.value, value))
+  for application in get_or_create_applications(doc, manifest):
+    value = str(extract_native_libs).lower()
+    attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs')
+    if attr is None:
+      attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs')
+      attr.value = value
+      application.setAttributeNode(attr)
+    elif attr.value != value:
+      raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' %
+                         (attr.value, value))
 
 
 def set_has_code_to_false(doc):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'hasCode')
+    if attr is not None:
+      # Do nothing if the application already has a hasCode attribute.
+      continue
+    attr = doc.createAttributeNS(android_ns, 'android:hasCode')
+    attr.value = 'false'
+    application.setAttributeNode(attr)
 
-  attr = application.getAttributeNodeNS(android_ns, 'hasCode')
-  if attr is not None:
-    # Do nothing if the application already has a hasCode attribute.
-    return
-  attr = doc.createAttributeNS(android_ns, 'android:hasCode')
-  attr.value = 'false'
-  application.setAttributeNode(attr)
 
 def set_test_only_flag_to_true(doc):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'testOnly')
+    if attr is not None:
+      # Do nothing If the application already has a testOnly attribute.
+      continue
+    attr = doc.createAttributeNS(android_ns, 'android:testOnly')
+    attr.value = 'true'
+    application.setAttributeNode(attr)
 
-  attr = application.getAttributeNodeNS(android_ns, 'testOnly')
-  if attr is not None:
-    # Do nothing If the application already has a testOnly attribute.
-    return
-  attr = doc.createAttributeNS(android_ns, 'android:testOnly')
-  attr.value = 'true'
-  application.setAttributeNode(attr)
 
 def set_max_sdk_version(doc, max_sdk_version):
   """Replace the maxSdkVersion attribute value for permission and
@@ -364,6 +274,7 @@
       if max_attr and max_attr.value == 'current':
         max_attr.value = max_sdk_version
 
+
 def override_placeholder_version(doc, new_version):
   """Replace the versionCode attribute value if it\'s currently
   set to the placeholder version of 0.
@@ -374,9 +285,10 @@
   """
   manifest = parse_manifest(doc)
   version = manifest.getAttribute("android:versionCode")
-  if (version == '0'):
+  if version == '0':
     manifest.setAttribute("android:versionCode", new_version)
 
+
 def main():
   """Program entry point."""
   try:
@@ -427,5 +339,6 @@
     print('error: ' + str(err), file=sys.stderr)
     sys.exit(-1)
 
+
 if __name__ == '__main__':
   main()
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 0a62b10..e4d8dc3 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -20,12 +20,13 @@
 import sys
 import unittest
 from xml.dom import minidom
-import xml.etree.ElementTree as ET
+import xml.etree.ElementTree as ElementTree
 
 import manifest_fixer
 
 sys.dont_write_bytecode = True
 
+
 class CompareVersionGtTest(unittest.TestCase):
   """Unit tests for compare_version_gt function."""
 
@@ -69,25 +70,24 @@
       '%s'
       '</manifest>\n')
 
-  # pylint: disable=redefined-builtin
-  def uses_sdk(self, min=None, target=None, extra=''):
+  def uses_sdk(self, min_sdk=None, target_sdk=None, extra=''):
     attrs = ''
-    if min:
-      attrs += ' android:minSdkVersion="%s"' % (min)
-    if target:
-      attrs += ' android:targetSdkVersion="%s"' % (target)
+    if min_sdk:
+      attrs += ' android:minSdkVersion="%s"' % min_sdk
+    if target_sdk:
+      attrs += ' android:targetSdkVersion="%s"' % target_sdk
     if extra:
       attrs += ' ' + extra
-    return '    <uses-sdk%s/>\n' % (attrs)
+    return '    <uses-sdk%s/>\n' % attrs
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def test_no_uses_sdk(self):
     """Tests inserting a uses-sdk element into a manifest."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
@@ -95,7 +95,7 @@
     """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
 
     manifest_input = self.manifest_tmpl % '    <uses-sdk extra="foo"/>\n'
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28',
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28',
                                                   extra='extra="foo"')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
@@ -103,64 +103,64 @@
   def test_raise_min(self):
     """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
   def test_raise(self):
     """Tests raising a minSdkVersion attribute."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
   def test_no_raise_min(self):
     """Tests a minSdkVersion that doesn't need raising."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='28')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False)
     self.assert_xml_equal(output, expected)
 
   def test_raise_codename(self):
     """Tests raising a minSdkVersion attribute to a codename."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
-    expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='28')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='P', target_sdk='P')
     output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False)
     self.assert_xml_equal(output, expected)
 
   def test_no_raise_codename(self):
     """Tests a minSdkVersion codename that doesn't need raising."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='P')
-    expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='P')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='P', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
   def test_target(self):
     """Tests an existing targetSdkVersion is preserved."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='26', target_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
   def test_no_target(self):
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='29')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
   def test_target_no_min(self):
     """"Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(target_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
@@ -168,23 +168,23 @@
     """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='29')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
   def test_library_no_target(self):
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='16')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assert_xml_equal(output, expected)
 
   def test_library_target_no_min(self):
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(target_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assert_xml_equal(output, expected)
 
@@ -192,7 +192,7 @@
     """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='16')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assert_xml_equal(output, expected)
 
@@ -228,12 +228,24 @@
 
     self.assert_xml_equal(output, expected)
 
+  def test_multiple_uses_sdks(self):
+    """Tests a manifest that contains multiple uses_sdks elements."""
+
+    manifest_input = self.manifest_tmpl % (
+        '    <uses-sdk android:featureFlag="foo" android:minSdkVersion="21" />\n'
+        '    <uses-sdk android:featureFlag="!foo" android:minSdkVersion="22" />\n')
+    expected = self.manifest_tmpl % (
+      '    <uses-sdk android:featureFlag="foo" android:minSdkVersion="28" android:targetSdkVersion="28" />\n'
+      '    <uses-sdk android:featureFlag="!foo" android:minSdkVersion="28" android:targetSdkVersion="28" />\n')
+
+    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+    self.assert_xml_equal(output, expected)
 
 class AddLoggingParentTest(unittest.TestCase):
   """Unit tests for add_logging_parent function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def add_logging_parent_test(self, input_manifest, logging_parent=None):
     doc = minidom.parseString(input_manifest)
@@ -253,8 +265,8 @@
     attrs = ''
     if logging_parent:
       meta_text = ('<meta-data android:name="android.content.pm.LOGGING_PARENT" '
-                   'android:value="%s"/>\n') % (logging_parent)
-      attrs += '    <application>\n        %s    </application>\n' % (meta_text)
+                   'android:value="%s"/>\n') % logging_parent
+      attrs += '    <application>\n        %s    </application>\n' % meta_text
 
     return attrs
 
@@ -277,7 +289,7 @@
   """Unit tests for add_uses_libraries function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, new_uses_libraries):
     doc = minidom.parseString(input_manifest)
@@ -289,18 +301,16 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application>\n'
       '%s'
-      '    </application>\n'
       '</manifest>\n')
 
   def uses_libraries(self, name_required_pairs):
-    ret = ''
+    ret = '    <application>\n'
     for name, required in name_required_pairs:
       ret += (
           '        <uses-library android:name="%s" android:required="%s"/>\n'
       ) % (name, required)
-
+    ret += '    </application>\n'
     return ret
 
   def test_empty(self):
@@ -361,12 +371,23 @@
     output = self.run_test(manifest_input, ['foo', 'bar'])
     self.assert_xml_equal(output, expected)
 
+  def test_multiple_application(self):
+    """When there are multiple applications, the libs are added to each."""
+    manifest_input = self.manifest_tmpl % (
+            self.uses_libraries([('foo', 'false')]) +
+            self.uses_libraries([('bar', 'false')]))
+    expected = self.manifest_tmpl % (
+            self.uses_libraries([('foo', 'false'), ('bar', 'true')]) +
+            self.uses_libraries([('bar', 'false'), ('foo', 'true')]))
+    output = self.run_test(manifest_input, ['foo', 'bar'])
+    self.assert_xml_equal(output, expected)
+
 
 class AddUsesNonSdkApiTest(unittest.TestCase):
   """Unit tests for add_uses_libraries function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -378,11 +399,11 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application%s/>\n'
+      '    %s\n'
       '</manifest>\n')
 
   def uses_non_sdk_api(self, value):
-    return ' android:usesNonSdkApi="true"' if value else ''
+    return '<application %s/>' % ('android:usesNonSdkApi="true"' if value else '')
 
   def test_set_true(self):
     """Empty new_uses_libraries must not touch the manifest."""
@@ -398,12 +419,19 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, expected)
 
+  def test_multiple_applications(self):
+    """new_uses_libraries must be added to all applications."""
+    manifest_input = self.manifest_tmpl % (self.uses_non_sdk_api(True) +  self.uses_non_sdk_api(False))
+    expected = self.manifest_tmpl % (self.uses_non_sdk_api(True) +  self.uses_non_sdk_api(True))
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class UseEmbeddedDexTest(unittest.TestCase):
   """Unit tests for add_use_embedded_dex function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -415,14 +443,14 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application%s/>\n'
+      '    %s\n'
       '</manifest>\n')
 
   def use_embedded_dex(self, value):
-    return ' android:useEmbeddedDex="%s"' % value
+    return '<application android:useEmbeddedDex="%s" />' % value
 
   def test_manifest_with_undeclared_preference(self):
-    manifest_input = self.manifest_tmpl % ''
+    manifest_input = self.manifest_tmpl % '<application/>'
     expected = self.manifest_tmpl % self.use_embedded_dex('true')
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, expected)
@@ -437,12 +465,24 @@
     manifest_input = self.manifest_tmpl % self.use_embedded_dex('false')
     self.assertRaises(RuntimeError, self.run_test, manifest_input)
 
+  def test_multiple_applications(self):
+    manifest_input = self.manifest_tmpl % (
+        self.use_embedded_dex('true') +
+        '<application/>'
+    )
+    expected = self.manifest_tmpl % (
+        self.use_embedded_dex('true') +
+        self.use_embedded_dex('true')
+    )
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class AddExtractNativeLibsTest(unittest.TestCase):
   """Unit tests for add_extract_native_libs function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, value):
     doc = minidom.parseString(input_manifest)
@@ -454,20 +494,20 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application%s/>\n'
+      '    %s\n'
       '</manifest>\n')
 
   def extract_native_libs(self, value):
-    return ' android:extractNativeLibs="%s"' % value
+    return '<application android:extractNativeLibs="%s" />' % value
 
   def test_set_true(self):
-    manifest_input = self.manifest_tmpl % ''
+    manifest_input = self.manifest_tmpl % '<application/>'
     expected = self.manifest_tmpl % self.extract_native_libs('true')
     output = self.run_test(manifest_input, True)
     self.assert_xml_equal(output, expected)
 
   def test_set_false(self):
-    manifest_input = self.manifest_tmpl % ''
+    manifest_input = self.manifest_tmpl % '<application/>'
     expected = self.manifest_tmpl % self.extract_native_libs('false')
     output = self.run_test(manifest_input, False)
     self.assert_xml_equal(output, expected)
@@ -482,12 +522,18 @@
     manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
     self.assertRaises(RuntimeError, self.run_test, manifest_input, False)
 
+  def test_multiple_applications(self):
+    manifest_input = self.manifest_tmpl % (self.extract_native_libs('true') + '<application/>')
+    expected = self.manifest_tmpl % (self.extract_native_libs('true') + self.extract_native_libs('true'))
+    output = self.run_test(manifest_input, True)
+    self.assert_xml_equal(output, expected)
+
 
 class AddNoCodeApplicationTest(unittest.TestCase):
   """Unit tests for set_has_code_to_false function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -515,7 +561,7 @@
     self.assert_xml_equal(output, expected)
 
   def test_has_application_has_code_false(self):
-    """ Do nothing if there's already an application elemeent. """
+    """ Do nothing if there's already an application element. """
     manifest_input = self.manifest_tmpl % '    <application android:hasCode="false"/>\n'
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
@@ -527,12 +573,25 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
+  def test_multiple_applications(self):
+    """ Apply to all applications  """
+    manifest_input = self.manifest_tmpl % (
+        '    <application android:hasCode="true" />\n' +
+        '    <application android:hasCode="false" />\n' +
+        '    <application/>\n')
+    expected = self.manifest_tmpl % (
+        '    <application android:hasCode="true" />\n' +
+        '    <application android:hasCode="false" />\n' +
+        '    <application android:hasCode="false" />\n')
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class AddTestOnlyApplicationTest(unittest.TestCase):
   """Unit tests for set_test_only_flag_to_true function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -571,12 +630,26 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
+  def test_multiple_applications(self):
+    manifest_input = self.manifest_tmpl % (
+        '    <application android:testOnly="true" />\n' +
+        '    <application android:testOnly="false" />\n' +
+        '    <application/>\n'
+    )
+    expected = self.manifest_tmpl % (
+        '    <application android:testOnly="true" />\n' +
+        '    <application android:testOnly="false" />\n' +
+        '    <application android:testOnly="true" />\n'
+    )
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class SetMaxSdkVersionTest(unittest.TestCase):
   """Unit tests for set_max_sdk_version function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, max_sdk_version):
     doc = minidom.parseString(input_manifest)
@@ -591,15 +664,15 @@
       '%s'
       '</manifest>\n')
 
-  def permission(self, max=None):
-    if max is None:
+  def permission(self, max_sdk=None):
+    if max_sdk is None:
       return '   <permission/>'
-    return '    <permission android:maxSdkVersion="%s"/>\n' % max
+    return '    <permission android:maxSdkVersion="%s"/>\n' % max_sdk
 
-  def uses_permission(self, max=None):
-    if max is None:
+  def uses_permission(self, max_sdk=None):
+    if max_sdk is None:
       return '   <uses-permission/>'
-    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max
+    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max_sdk
 
   def test_permission_no_max_sdk_version(self):
     """Tests if permission has no maxSdkVersion attribute"""
@@ -643,11 +716,12 @@
     output = self.run_test(manifest_input, '9000')
     self.assert_xml_equal(output, expected)
 
+
 class OverrideDefaultVersionTest(unittest.TestCase):
   """Unit tests for override_default_version function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, version):
     doc = minidom.parseString(input_manifest)
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index a00a5e4..22cba3b 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -21,4 +21,6 @@
         "sysprop_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/testing/code_metadata_internal_proto/Android.bp b/testing/code_metadata_internal_proto/Android.bp
index a534cc2..396e44f 100644
--- a/testing/code_metadata_internal_proto/Android.bp
+++ b/testing/code_metadata_internal_proto/Android.bp
@@ -20,10 +20,14 @@
     name: "soong-testing-code_metadata_internal_proto",
     pkgPath: "android/soong/testing/code_metadata_internal_proto",
     deps: [
-            "golang-protobuf-reflect-protoreflect",
-            "golang-protobuf-runtime-protoimpl",
-        ],
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "code_metadata_internal.pb.go",
     ],
+    visibility: [
+        "//build/make/tools/metadata",
+        "//build/soong:__subpackages__",
+    ],
 }
diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp
index f07efff..ae41d4a 100644
--- a/testing/code_metadata_proto/Android.bp
+++ b/testing/code_metadata_proto/Android.bp
@@ -26,6 +26,7 @@
     srcs: [
         "code_metadata.pb.go",
     ],
+    visibility: ["//build/make/tools/metadata"],
 }
 
 python_library_host {
@@ -40,4 +41,5 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: ["//tools/asuite/team_build_scripts"],
 }
diff --git a/testing/test_spec_proto/Android.bp b/testing/test_spec_proto/Android.bp
index d5ad70b..1070d1a 100644
--- a/testing/test_spec_proto/Android.bp
+++ b/testing/test_spec_proto/Android.bp
@@ -26,6 +26,11 @@
     srcs: [
         "test_spec.pb.go",
     ],
+    visibility: [
+        "//build/make/tools/metadata",
+        "//build/soong:__subpackages__",
+        "//vendor:__subpackages__",
+    ],
 }
 
 python_library_host {
@@ -40,4 +45,5 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: ["//tools/asuite/team_build_scripts"],
 }
diff --git a/tests/build_action_caching_test.sh b/tests/build_action_caching_test.sh
new file mode 100755
index 0000000..7524d82
--- /dev/null
+++ b/tests/build_action_caching_test.sh
@@ -0,0 +1,120 @@
+#!/bin/bash -u
+
+set -o pipefail
+
+# Test that the mk and ninja files generated by Soong don't change if some
+# incremental modules are restored from cache.
+
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+echo ${OUTPUT_DIR}
+
+function cleanup {
+  rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+function run_soong_build {
+  USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode --incremental-build-actions nothing
+}
+
+function run_soong_clean {
+  build/soong/soong_ui.bash --make-mode clean
+}
+
+function assert_files_equal {
+  if [ $# -ne 2 ]; then
+    echo "Usage: assert_files_equal file1 file2"
+    exit 1
+  fi
+
+  if ! cmp -s "$1" "$2"; then
+    echo "Files are different: $1 $2"
+    exit 1
+  fi
+}
+
+function compare_mtimes() {
+  if [ $# -ne 2 ]; then
+    echo "Usage: compare_mtimes file1 file2"
+    exit 1
+  fi
+
+  file1_mtime=$(stat -c '%Y' $1)
+  file2_mtime=$(stat -c '%Y' $2)
+
+  if [ "$file1_mtime" -eq "$file2_mtime" ]; then
+      return 1
+  else
+      return 0
+  fi
+}
+
+function test_build_action_restoring() {
+  run_soong_clean
+  cat > ${OUTPUT_DIR}/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["my_little_binary_host.py"],
+}
+EOF
+  touch ${OUTPUT_DIR}/my_little_binary_host.py
+  run_soong_build
+  mkdir -p "${OUTPUT_DIR}/before"
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm.*.ninja ${OUTPUT_DIR}/before
+  # add a comment to the bp file, this should force a new analysis but no module
+  # should be really impacted, so all the incremental modules should be skipped.
+  cat >> ${OUTPUT_DIR}/Android.bp <<'EOF'
+// new comments
+EOF
+  run_soong_build
+  mkdir -p "${OUTPUT_DIR}/after"
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm.*.ninja ${OUTPUT_DIR}/after
+
+  compare_files
+  echo "Tests passed"
+}
+
+function compare_files() {
+  count=0
+  for file_before in ${OUTPUT_DIR}/before/*.ninja; do
+    file_after="${OUTPUT_DIR}/after/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    compare_mtimes $file_before $file_after
+    if [ $? -ne 0 ]; then
+      echo "Files have identical mtime: $file_before $file_after"
+      exit 1
+    fi
+    ((count++))
+  done
+  echo "Compared $count ninja files"
+
+  count=0
+  for file_before in ${OUTPUT_DIR}/before/*.mk; do
+    file_after="${OUTPUT_DIR}/after/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    compare_mtimes $file_before $file_after
+    # mk files shouldn't be regenerated
+    if [ $? -ne 1 ]; then
+      echo "Files have different mtimes: $file_before $file_after"
+      exit 1
+    fi
+    ((count++))
+  done
+  echo "Compared $count mk files"
+
+  count=0
+  for file_before in ${OUTPUT_DIR}/before/build_aosp_arm_ninja_incremental/*.ninja; do
+    file_after="${OUTPUT_DIR}/after/build_aosp_arm_ninja_incremental/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    compare_mtimes $file_before $file_after
+    # ninja files of skipped modules shouldn't be regenerated
+    if [ $? -ne 1 ]; then
+      echo "Files have different mtimes: $file_before $file_after"
+      exit 1
+    fi
+    ((count++))
+  done
+  echo "Compared $count incremental ninja files"
+}
+
+test_build_action_restoring
diff --git a/ui/build/config.go b/ui/build/config.go
index f02222e..851a22a 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -54,6 +54,16 @@
 	rbeRandPrefix = rand.Intn(1000)
 }
 
+// Which builder are we using?
+type ninjaCommandType = int
+
+const (
+	_ = iota
+	NINJA_NINJA
+	NINJA_N2
+	NINJA_SISO
+)
+
 type Config struct{ *configImpl }
 
 type configImpl struct {
@@ -123,9 +133,8 @@
 	// could consider merging them.
 	moduleDebugFile string
 
-	// Whether to use n2 instead of ninja.  This is controlled with the
-	// environment variable SOONG_USE_N2
-	useN2 bool
+	// Which builder are we using
+	ninjaCommand ninjaCommandType
 }
 
 type NinjaWeightListSource uint
@@ -288,8 +297,16 @@
 		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
 	}
 
-	if os.Getenv("SOONG_USE_N2") == "true" {
-		ret.useN2 = true
+	ret.ninjaCommand = NINJA_NINJA
+	switch os.Getenv("SOONG_NINJA") {
+	case "n2":
+		ret.ninjaCommand = NINJA_N2
+	case "siso":
+		ret.ninjaCommand = NINJA_SISO
+	default:
+		if os.Getenv("SOONG_USE_N2") == "true" {
+			ret.ninjaCommand = NINJA_N2
+		}
 	}
 
 	ret.environ.Unset(
@@ -349,7 +366,8 @@
 		// We read it here already, don't let others share in the fun
 		"GENERATE_SOONG_DEBUG",
 
-		// Use config.useN2 instead.
+		// Use config.ninjaCommand instead.
+		"SOONG_NINJA",
 		"SOONG_USE_N2",
 	)
 
@@ -1375,8 +1393,10 @@
 	// Perform a log directory cleanup only when the log directory
 	// is auto created by the build rather than user-specified.
 	for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
-		if _, ok := c.environ.Get(f); ok {
-			return false
+		if v, ok := c.environ.Get(f); ok {
+			if v != c.rbeTmpDir() {
+				return false
+			}
 		}
 	}
 	return true
@@ -1641,6 +1661,12 @@
 	return strings.ReplaceAll(path, "/linux-x86/", "/linux_musl-x86/")
 }
 
+func (c *configImpl) SisoBin() string {
+	path := c.PrebuiltBuildTool("siso")
+	// Use musl instead of glibc because glibc on the build server is old and has bugs
+	return strings.ReplaceAll(path, "/linux-x86/", "/linux_musl-x86/")
+}
+
 func (c *configImpl) PrebuiltBuildTool(name string) string {
 	if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
 		if sanitize := strings.Fields(v); inList("address", sanitize) {
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 4e3e544..def0783 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -49,14 +49,10 @@
 	nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 	defer nr.Close()
 
-	executable := config.NinjaBin()
-	args := []string{
-		"-d", "keepdepfile",
-		"-d", "keeprsp",
-		"-d", "stats",
-		"--frontend_file", fifo,
-	}
-	if config.useN2 {
+	var executable string
+	var args []string
+	switch config.ninjaCommand {
+	case NINJA_N2:
 		executable = config.N2Bin()
 		args = []string{
 			"-d", "trace",
@@ -66,8 +62,31 @@
 			//"-d", "stats",
 			"--frontend-file", fifo,
 		}
+	case NINJA_SISO:
+		executable = config.SisoBin()
+		args = []string{
+			"ninja",
+			"--log_dir", config.SoongOutDir(),
+			// TODO: implement these features, or remove them.
+			//"-d", "trace",
+			//"-d", "keepdepfile",
+			//"-d", "keeprsp",
+			//"-d", "stats",
+			//"--frontend-file", fifo,
+		}
+	default:
+		// NINJA_NINJA is the default.
+		executable = config.NinjaBin()
+		args = []string{
+			"-d", "keepdepfile",
+			"-d", "keeprsp",
+			"-d", "stats",
+			"--frontend_file", fifo,
+			"-o", "usesphonyoutputs=yes",
+			"-w", "dupbuild=err",
+			"-w", "missingdepfile=err",
+		}
 	}
-
 	args = append(args, config.NinjaArgs()...)
 
 	var parallel int
@@ -83,17 +102,10 @@
 
 	args = append(args, "-f", config.CombinedNinjaFile())
 
-	if !config.useN2 {
-		args = append(args,
-			"-o", "usesphonyoutputs=yes",
-			"-w", "dupbuild=err",
-			"-w", "missingdepfile=err")
-	}
-
 	if !config.BuildBrokenMissingOutputs() {
 		// Missing outputs will be treated as errors.
 		// BUILD_BROKEN_MISSING_OUTPUTS can be used to bypass this check.
-		if !config.useN2 {
+		if config.ninjaCommand != NINJA_N2 {
 			args = append(args,
 				"-w", "missingoutfile=err",
 			)
@@ -110,21 +122,18 @@
 		cmd.Environment.AppendFromKati(config.KatiEnvFile())
 	}
 
-	switch config.NinjaWeightListSource() {
-	case NINJA_LOG:
-		if !config.useN2 {
+	// TODO(b/346806126): implement this for the other ninjaCommand values.
+	if config.ninjaCommand == NINJA_NINJA {
+		switch config.NinjaWeightListSource() {
+		case NINJA_LOG:
 			cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
-		}
-	case EVENLY_DISTRIBUTED:
-		// pass empty weight list means ninja considers every tasks's weight as 1(default value).
-		if !config.useN2 {
+		case EVENLY_DISTRIBUTED:
+			// pass empty weight list means ninja considers every tasks's weight as 1(default value).
 			cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
-		}
-	case EXTERNAL_FILE:
-		fallthrough
-	case HINT_FROM_SOONG:
-		// The weight list is already copied/generated.
-		if !config.useN2 {
+		case EXTERNAL_FILE:
+			fallthrough
+		case HINT_FROM_SOONG:
+			// The weight list is already copied/generated.
 			ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName)
 			cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath)
 		}
@@ -227,6 +236,8 @@
 			// We don't want this build broken flag to cause reanalysis, so allow it through to the
 			// actions.
 			"BUILD_BROKEN_INCORRECT_PARTITION_IMAGES",
+			// Do not do reanalysis just because we changed ninja commands.
+			"SOONG_NINJA",
 			"SOONG_USE_N2",
 			"RUST_BACKTRACE",
 			"RUST_LOG",
@@ -235,8 +246,11 @@
 
 	cmd.Environment.Set("DIST_DIR", config.DistDir())
 	cmd.Environment.Set("SHELL", "/bin/bash")
-	if config.useN2 {
+	switch config.ninjaCommand {
+	case NINJA_N2:
 		cmd.Environment.Set("RUST_BACKTRACE", "1")
+	default:
+		// Only set RUST_BACKTRACE for n2.
 	}
 
 	// Print the environment variables that Ninja is operating in.
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 97bc997..eb51022 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -588,19 +588,11 @@
 		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 		defer nr.Close()
 
-		ninjaArgs := []string{
-			"-d", "keepdepfile",
-			"-d", "stats",
-			"-o", "usesphonyoutputs=yes",
-			"-o", "preremoveoutputs=yes",
-			"-w", "dupbuild=err",
-			"-w", "outputdir=err",
-			"-w", "missingoutfile=err",
-			"-j", strconv.Itoa(config.Parallel()),
-			"--frontend_file", fifo,
-			"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
-		}
-		if config.useN2 {
+		var ninjaCmd string
+		var ninjaArgs []string
+		switch config.ninjaCommand {
+		case NINJA_N2:
+			ninjaCmd = config.N2Bin()
 			ninjaArgs = []string{
 				// TODO: implement these features, or remove them.
 				//"-d", "keepdepfile",
@@ -615,6 +607,39 @@
 				"--frontend-file", fifo,
 				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
 			}
+		case NINJA_SISO:
+			ninjaCmd = config.SisoBin()
+			ninjaArgs = []string{
+				"ninja",
+				// TODO: implement these features, or remove them.
+				//"-d", "keepdepfile",
+				//"-d", "stats",
+				//"-o", "usesphonyoutputs=yes",
+				//"-o", "preremoveoutputs=yes",
+				//"-w", "dupbuild=err",
+				//"-w", "outputdir=err",
+				//"-w", "missingoutfile=err",
+				"-v",
+				"-j", strconv.Itoa(config.Parallel()),
+				//"--frontend-file", fifo,
+				"--log_dir", config.SoongOutDir(),
+				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+			}
+		default:
+			// NINJA_NINJA is the default.
+			ninjaCmd = config.NinjaBin()
+			ninjaArgs = []string{
+				"-d", "keepdepfile",
+				"-d", "stats",
+				"-o", "usesphonyoutputs=yes",
+				"-o", "preremoveoutputs=yes",
+				"-w", "dupbuild=err",
+				"-w", "outputdir=err",
+				"-w", "missingoutfile=err",
+				"-j", strconv.Itoa(config.Parallel()),
+				"--frontend_file", fifo,
+				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+			}
 		}
 
 		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
@@ -623,10 +648,6 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
-		ninjaCmd := config.NinjaBin()
-		if config.useN2 {
-			ninjaCmd = config.N2Bin()
-		}
 
 		cmd := Command(ctx, config, "soong bootstrap",
 			ninjaCmd, ninjaArgs...)
diff --git a/zip/cmd/Android.bp b/zip/cmd/Android.bp
index 43bf232..16c3f69 100644
--- a/zip/cmd/Android.bp
+++ b/zip/cmd/Android.bp
@@ -24,4 +24,6 @@
     srcs: [
         "main.go",
     ],
+    // Used by genrules
+    visibility: ["//visibility:public"],
 }