Merge "Revert^2 "Use -target-feature for MTE"" into main
diff --git a/Android.bp b/Android.bp
index 432c7fc..d71bcec 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,9 @@
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_visibility: [
+        "//build/soong:__subpackages__",
+    ],
+    default_team: "trendy_team_build",
 }
 
 subdirs = [
@@ -23,6 +27,8 @@
     srcs: [
         "doc.go",
     ],
+    // Used by plugins, though probably shouldn't be.
+    visibility: ["//visibility:public"],
 }
 
 //
@@ -40,6 +46,7 @@
             enabled: true,
         },
     },
+    defaults_visibility: ["//visibility:public"],
 }
 
 //
@@ -51,6 +58,7 @@
     vendor: true,
     recovery_available: true,
     min_sdk_version: "apex_inherit",
+    visibility: ["//visibility:public"],
 }
 
 cc_genrule {
@@ -75,6 +83,7 @@
     cmd: "$(location) -s $(out) $(in)",
     srcs: [":linker"],
     out: ["linker.s"],
+    visibility: ["//bionic/libc"],
 }
 
 cc_genrule {
@@ -99,11 +108,18 @@
     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"],
+}
+
+art_boot_images {
+    name: "art_boot_images",
+    visibility: ["//art:__subpackages__"],
 }
 
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@@ -123,11 +139,14 @@
 // container for apex_contributions selected using build flags
 all_apex_contributions {
     name: "all_apex_contributions",
+    visibility: ["//visibility:public"],
 }
 
 product_config {
     name: "product_config",
-    visibility: ["//device/google/cuttlefish/system_image"],
+    visibility: [
+        "//build/make/target/product/generic",
+    ],
 }
 
 build_prop {
@@ -136,7 +155,7 @@
     product_config: ":product_config",
     // Currently, only microdroid and cf system image can refer to system-build.prop
     visibility: [
-        "//device/google/cuttlefish/system_image",
+        "//build/make/target/product/generic",
         "//packages/modules/Virtualization/build/microdroid",
     ],
 }
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/build_flags/Android.bp b/aconfig/build_flags/Android.bp
index b3c7339..139aeac 100644
--- a/aconfig/build_flags/Android.bp
+++ b/aconfig/build_flags/Android.bp
@@ -13,10 +13,11 @@
         "soong-android",
     ],
     srcs: [
-        "all_build_flag_declarations.go",
         "build_flags.go",
+        "build_flags_singleton.go",
         "declarations.go",
         "init.go",
+        "release_configs.go",
     ],
     testSrcs: [
     ],
diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go
deleted file mode 100644
index 5f02912..0000000
--- a/aconfig/build_flags/all_build_flag_declarations.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2023 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package build_flags
-
-import (
-	"android/soong/android"
-)
-
-// A singleton module that collects all of the build flags declared in the
-// tree into a single combined file for export to the external flag setting
-// server (inside Google it's Gantry).
-//
-// Note that this is ALL build_declarations modules present in the tree, not just
-// ones that are relevant to the product currently being built, so that that infra
-// doesn't need to pull from multiple builds and merge them.
-func AllBuildFlagDeclarationsFactory() android.Singleton {
-	return &allBuildFlagDeclarationsSingleton{}
-}
-
-type allBuildFlagDeclarationsSingleton struct {
-	intermediateBinaryProtoPath android.OutputPath
-	intermediateTextProtoPath   android.OutputPath
-}
-
-func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// Find all of the build_flag_declarations modules
-	var intermediateFiles android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
-		if !ok {
-			return
-		}
-		intermediateFiles = append(intermediateFiles, decl.IntermediateCacheOutputPath)
-	})
-
-	// Generate build action for build_flag (binary proto output)
-	this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.pb")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        allDeclarationsRule,
-		Inputs:      intermediateFiles,
-		Output:      this.intermediateBinaryProtoPath,
-		Description: "all_build_flag_declarations",
-		Args: map[string]string{
-			"intermediates": android.JoinPathsWithPrefix(intermediateFiles, "--intermediate "),
-		},
-	})
-	ctx.Phony("all_build_flag_declarations", this.intermediateBinaryProtoPath)
-
-	// Generate build action for build_flag (text proto output)
-	this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.textproto")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        allDeclarationsRuleTextProto,
-		Input:       this.intermediateBinaryProtoPath,
-		Output:      this.intermediateTextProtoPath,
-		Description: "all_build_flag_declarations_textproto",
-	})
-	ctx.Phony("all_build_flag_declarations_textproto", this.intermediateTextProtoPath)
-}
-
-func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("droid", this.intermediateBinaryProtoPath)
-	for _, goal := range []string{"docs", "droid", "sdk"} {
-		ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "build_flags/all_flags.pb")
-		ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "build_flags/all_flags.textproto")
-	}
-}
diff --git a/aconfig/build_flags/build_flags_singleton.go b/aconfig/build_flags/build_flags_singleton.go
new file mode 100644
index 0000000..3b40755
--- /dev/null
+++ b/aconfig/build_flags/build_flags_singleton.go
@@ -0,0 +1,123 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"android/soong/android"
+)
+
+// A singleton module that collects all of the build flags declared in the
+// tree into a single combined file for export to the external flag setting
+// server (inside Google it's Gantry).
+//
+// Note that this is ALL build_declarations modules present in the tree, not just
+// ones that are relevant to the product currently being built, so that that infra
+// doesn't need to pull from multiple builds and merge them.
+func AllBuildFlagDeclarationsFactory() android.Singleton {
+	return &allBuildFlagDeclarationsSingleton{}
+}
+
+type allBuildFlagDeclarationsSingleton struct {
+	flagsBinaryProtoPath   android.OutputPath
+	flagsTextProtoPath     android.OutputPath
+	configsBinaryProtoPath android.OutputPath
+	configsTextProtoPath   android.OutputPath
+}
+
+func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// Find all of the build_flag_declarations modules
+	var flagsFiles android.Paths
+	// Find all of the release_config_contribution modules
+	var contributionDirs android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
+		if ok {
+			flagsFiles = append(flagsFiles, decl.IntermediateCacheOutputPath)
+		}
+
+		contrib, ok := android.OtherModuleProvider(ctx, module, ReleaseConfigContributionsProviderKey)
+		if ok {
+			contributionDirs = append(contributionDirs, contrib.ContributionDir)
+		}
+	})
+
+	// Generate build action for build_flag (binary proto output)
+	this.flagsBinaryProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRule,
+		Inputs:      flagsFiles,
+		Output:      this.flagsBinaryProtoPath,
+		Description: "all_build_flag_declarations",
+		Args: map[string]string{
+			"intermediates": android.JoinPathsWithPrefix(flagsFiles, "--intermediate "),
+		},
+	})
+	ctx.Phony("all_build_flag_declarations", this.flagsBinaryProtoPath)
+
+	// Generate build action for build_flag (text proto output)
+	this.flagsTextProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRuleTextProto,
+		Input:       this.flagsBinaryProtoPath,
+		Output:      this.flagsTextProtoPath,
+		Description: "all_build_flag_declarations_textproto",
+	})
+	ctx.Phony("all_build_flag_declarations_textproto", this.flagsTextProtoPath)
+
+	// Generate build action for release_configs (binary proto output)
+	this.configsBinaryProtoPath = android.PathForIntermediates(ctx, "all_release_config_contributions.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allReleaseConfigContributionsRule,
+		Inputs:      contributionDirs,
+		Output:      this.configsBinaryProtoPath,
+		Description: "all_release_config_contributions",
+		Args: map[string]string{
+			"dirs":   android.JoinPathsWithPrefix(contributionDirs, "--dir "),
+			"format": "pb",
+		},
+	})
+	ctx.Phony("all_release_config_contributions", this.configsBinaryProtoPath)
+
+	this.configsTextProtoPath = android.PathForIntermediates(ctx, "all_release_config_contributions.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allReleaseConfigContributionsRule,
+		Inputs:      contributionDirs,
+		Output:      this.configsTextProtoPath,
+		Description: "all_release_config_contributions_textproto",
+		Args: map[string]string{
+			"dirs":   android.JoinPathsWithPrefix(contributionDirs, "--dir "),
+			"format": "textproto",
+		},
+	})
+	ctx.Phony("all_release_config_contributions_textproto", this.configsTextProtoPath)
+
+	// Add a simple target for ci/build_metadata to use.
+	ctx.Phony("release_config_metadata",
+		this.flagsBinaryProtoPath,
+		this.flagsTextProtoPath,
+		this.configsBinaryProtoPath,
+		this.configsTextProtoPath,
+	)
+}
+
+func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("droid", this.flagsBinaryProtoPath)
+	for _, goal := range []string{"docs", "droid", "sdk", "release_config_metadata"} {
+		ctx.DistForGoalWithFilename(goal, this.flagsBinaryProtoPath, "build_flags/all_flags.pb")
+		ctx.DistForGoalWithFilename(goal, this.flagsTextProtoPath, "build_flags/all_flags.textproto")
+		ctx.DistForGoalWithFilename(goal, this.configsBinaryProtoPath, "build_flags/all_release_config_contributions.pb")
+		ctx.DistForGoalWithFilename(goal, this.configsTextProtoPath, "build_flags/all_release_config_contributions.textproto")
+	}
+}
diff --git a/aconfig/build_flags/declarations.go b/aconfig/build_flags/declarations.go
index e927db2..4a54269 100644
--- a/aconfig/build_flags/declarations.go
+++ b/aconfig/build_flags/declarations.go
@@ -35,7 +35,7 @@
 
 	// Properties for "aconfig_declarations"
 	properties struct {
-		// aconfig files, relative to this Android.bp file
+		// build flag declaration files, relative to this Android.bp file
 		Srcs []string `android:"path"`
 	}
 }
diff --git a/aconfig/build_flags/init.go b/aconfig/build_flags/init.go
index dc1369c..a7575e8 100644
--- a/aconfig/build_flags/init.go
+++ b/aconfig/build_flags/init.go
@@ -65,15 +65,32 @@
 				"${buildFlagDeclarations}",
 			},
 		})
+
+	allReleaseConfigContributionsRule = pctx.AndroidStaticRule("all-release-config-contributions-dump",
+		blueprint.RuleParams{
+			Command: `${releaseConfigContributions} ${dirs} --format ${format} --output ${out}`,
+			CommandDeps: []string{
+				"${releaseConfigContributions}",
+			},
+		}, "dirs", "format")
+	allReleaseConfigContributionsRuleText = pctx.AndroidStaticRule("all-release-config-contributions-dumptext",
+		blueprint.RuleParams{
+			Command: `${releaseConfigContributions} ${dirs} --format ${format} --output ${out}`,
+			CommandDeps: []string{
+				"${releaseConfigContributions}",
+			},
+		}, "dirs", "format")
 )
 
 func init() {
 	RegisterBuildComponents(android.InitRegistrationContext)
 	pctx.Import("android/soong/android")
 	pctx.HostBinToolVariable("buildFlagDeclarations", "build-flag-declarations")
+	pctx.HostBinToolVariable("releaseConfigContributions", "release-config-contributions")
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("build_flag_declarations", DeclarationsFactory)
+	ctx.RegisterModuleType("release_config_contributions", ReleaseConfigContributionsFactory)
 	ctx.RegisterParallelSingletonType("all_build_flag_declarations", AllBuildFlagDeclarationsFactory)
 }
diff --git a/aconfig/build_flags/release_configs.go b/aconfig/build_flags/release_configs.go
new file mode 100644
index 0000000..3fa8a7c
--- /dev/null
+++ b/aconfig/build_flags/release_configs.go
@@ -0,0 +1,78 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"path/filepath"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+type ReleaseConfigContributionsProviderData struct {
+	ContributionDir android.SourcePath
+}
+
+var ReleaseConfigContributionsProviderKey = blueprint.NewProvider[ReleaseConfigContributionsProviderData]()
+
+// Soong uses `release_config_contributions` modules to produce the
+// `build_flags/all_release_config_contributions.*` artifacts, listing *all* of
+// the directories in the source tree that contribute to each release config,
+// whether or not they are actually used for the lunch product.
+//
+// This artifact helps flagging automation determine in which directory a flag
+// should be placed by default.
+type ReleaseConfigContributionsModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// Properties for "release_config_contributions"
+	properties struct {
+		// The `release_configs/*.textproto` files provided by this
+		// directory, relative to this Android.bp file
+		Srcs []string `android:"path"`
+	}
+}
+
+func ReleaseConfigContributionsFactory() android.Module {
+	module := &ReleaseConfigContributionsModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+
+	return module
+}
+
+func (module *ReleaseConfigContributionsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	srcs := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+	if len(srcs) == 0 {
+		return
+	}
+	contributionDir := filepath.Dir(filepath.Dir(srcs[0].String()))
+	for _, file := range srcs {
+		if filepath.Dir(filepath.Dir(file.String())) != contributionDir {
+			ctx.ModuleErrorf("Cannot include %s with %s contributions", file, contributionDir)
+		}
+		if filepath.Base(filepath.Dir(file.String())) != "release_configs" || file.Ext() != ".textproto" {
+			ctx.ModuleErrorf("Invalid contribution file %s", file)
+		}
+	}
+	android.SetProvider(ctx, ReleaseConfigContributionsProviderKey, ReleaseConfigContributionsProviderData{
+		ContributionDir: android.PathForSource(ctx, contributionDir),
+	})
+
+}
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index b5cf687..8c4bfe6 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -63,7 +63,7 @@
 	callbacks := &CcAconfigLibraryCallbacks{
 		properties: &CcAconfigLibraryProperties{},
 	}
-	return cc.GeneratedCcLibraryModuleFactory("cc_aconfig_library", callbacks)
+	return cc.GeneratedCcLibraryModuleFactory(callbacks)
 }
 
 func (this *CcAconfigLibraryCallbacks) GeneratorInit(ctx cc.BaseModuleContext) {
diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go
index 98d288f..ed0b3ed 100644
--- a/aconfig/codegen/init.go
+++ b/aconfig/codegen/init.go
@@ -32,6 +32,7 @@
 				`    --mode ${mode}` +
 				`    --cache ${in}` +
 				`    --out ${out}.tmp` +
+				`    --allow-instrumentation ${debug}` +
 				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
 				` && rm -rf ${out}.tmp`,
 			CommandDeps: []string{
@@ -39,7 +40,7 @@
 				"$soong_zip",
 			},
 			Restat: true,
-		}, "mode")
+		}, "mode", "debug")
 
 	// For cc_aconfig_library: Generate C++ library
 	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 673ac2a..ebca413 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -20,6 +20,7 @@
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+	"strconv"
 )
 
 type declarationsTagType struct {
@@ -71,6 +72,7 @@
 		module.AddSharedLibrary("aconfig-annotations-lib")
 		// TODO(b/303773055): Remove the annotation after access issue is resolved.
 		module.AddSharedLibrary("unsupportedappusage")
+		module.AddSharedLibrary("aconfig_storage_reader_java")
 	}
 }
 
@@ -102,7 +104,8 @@
 		Output:      srcJarPath,
 		Description: "aconfig.srcjar",
 		Args: map[string]string{
-			"mode": mode,
+			"mode":  mode,
+			"debug": strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
 		},
 	})
 
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..eb8c64d 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -71,6 +71,7 @@
         "module.go",
         "module_context.go",
         "module_info_json.go",
+        "module_proxy.go",
         "mutator.go",
         "namespace.go",
         "neverallow.go",
@@ -88,6 +89,7 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "product_config.go",
+        "product_config_to_bp.go",
         "proto.go",
         "provider.go",
         "raw_files.go",
@@ -105,7 +107,6 @@
         "test_asserts.go",
         "test_suites.go",
         "testing.go",
-        "updatable_modules.go",
         "util.go",
         "variable.go",
         "vintf_fragment.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 6bfbf37..b902f8b 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -107,7 +107,7 @@
 	mergedAconfigFiles := make(map[string]Paths)
 	mergedModeInfos := make(map[string]ModeInfo)
 
-	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
+	ctx.VisitDirectDeps(func(module Module) {
 		if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 {
 			maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos)
 		}
@@ -187,6 +187,20 @@
 	}
 }
 
+func aconfigUpdateAndroidMkInfos(ctx fillInEntriesContext, mod Module, infos *AndroidMkProviderInfo) {
+	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
+	if !ok || len(info.AconfigFiles) == 0 {
+		return
+	}
+	// All of the files in the module potentially depend on the aconfig flag values.
+	infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+	if len(infos.ExtraInfo) > 0 {
+		for _, ei := range (*infos).ExtraInfo {
+			ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+		}
+	}
+}
+
 func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths {
 	inputs = SortedUniquePaths(inputs)
 	if len(inputs) == 1 {
@@ -219,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 fc628cb..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"
 )
@@ -502,6 +501,7 @@
 	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 	ModuleType(module blueprint.Module) string
 	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
+	HasMutatorFinished(mutatorName string) bool
 }
 
 func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -806,15 +806,19 @@
 
 	// Additional cases here require review for correct license propagation to make.
 	var err error
-	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:
-		// Not exported to make so no make variables to set.
+
+	if info, ok := ctx.otherModuleProvider(mod, AndroidMkInfoProvider); ok {
+		androidMkEntriesInfos := info.(*AndroidMkProviderInfo)
+		err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, androidMkEntriesInfos)
+	} else {
+		switch x := mod.(type) {
+		case AndroidMkDataProvider:
+			err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
+		case AndroidMkEntriesProvider:
+			err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
+		default:
+			// Not exported to make so no make variables to set.
+		}
 	}
 
 	if err != nil {
@@ -824,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{
@@ -983,11 +970,11 @@
 	return nil
 }
 
-func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool {
+func ShouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module Module) bool {
 	return shouldSkipAndroidMkProcessing(ctx, module.base())
 }
 
-func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module *ModuleBase) bool {
+func shouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module *ModuleBase) bool {
 	if !module.commonProperties.NamespaceExportedToMake {
 		// TODO(jeffrygaston) do we want to validate that there are no modules being
 		// exported to Kati that depend on this module?
@@ -1063,3 +1050,564 @@
 	}
 	fmt.Fprintln(w)
 }
+
+type AndroidMkProviderInfo struct {
+	PrimaryInfo AndroidMkInfo
+	ExtraInfo   []AndroidMkInfo
+}
+
+type AndroidMkInfo struct {
+	// Android.mk class string, e.g. EXECUTABLES, JAVA_LIBRARIES, ETC
+	Class string
+	// Optional suffix to append to the module name. Useful when a module wants to return multiple
+	// AndroidMkEntries objects. For example, when a java_library returns an additional entry for
+	// its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a
+	// different name than the parent's.
+	SubName string
+	// If set, this value overrides the base module name. SubName is still appended.
+	OverrideName string
+	// Dist files to output
+	DistFiles TaggedDistFiles
+	// The output file for Kati to process and/or install. If absent, the module is skipped.
+	OutputFile OptionalPath
+	// If true, the module is skipped and does not appear on the final Android-<product name>.mk
+	// file. Useful when a module needs to be skipped conditionally.
+	Disabled bool
+	// The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk
+	// If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
+	Include string
+	// Required modules that need to be built and included in the final build output when building
+	// this module.
+	Required []string
+	// Required host modules that need to be built and included in the final build output when
+	// building this module.
+	Host_required []string
+	// Required device modules that need to be built and included in the final build output when
+	// building this module.
+	Target_required []string
+
+	HeaderStrings []string
+	FooterStrings []string
+
+	// A map that holds the up-to-date Make variable values. Can be accessed from tests.
+	EntryMap map[string][]string
+	// A list of EntryMap keys in insertion order. This serves a few purposes:
+	// 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this,
+	// the outputted Android-*.mk file may change even though there have been no content changes.
+	// 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR),
+	// without worrying about the variables being mixed up in the actual mk file.
+	// 3. Makes troubleshooting and spotting errors easier.
+	EntryOrder []string
+}
+
+// TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
+var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
+
+func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+	mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error {
+	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+		return nil
+	}
+
+	// Deep copy the provider info since we need to modify the info later
+	info := deepCopyAndroidMkProviderInfo(providerInfo)
+
+	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info)
+
+	// Any new or special cases here need review to verify correct propagation of license information.
+	info.PrimaryInfo.fillInEntries(ctx, mod)
+	info.PrimaryInfo.write(w)
+	if len(info.ExtraInfo) > 0 {
+		for _, ei := range info.ExtraInfo {
+			ei.fillInEntries(ctx, mod)
+			ei.write(w)
+		}
+	}
+
+	if !info.PrimaryInfo.disabled() {
+		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		}
+	}
+
+	return nil
+}
+
+// Utility funcs to manipulate Android.mk variable entries.
+
+// SetString sets a Make variable with the given name to the given value.
+func (a *AndroidMkInfo) SetString(name, value string) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = []string{value}
+}
+
+// SetPath sets a Make variable with the given name to the given path string.
+func (a *AndroidMkInfo) SetPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = []string{path.String()}
+}
+
+// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
+// It is a no-op if the given path is invalid.
+func (a *AndroidMkInfo) SetOptionalPath(name string, path OptionalPath) {
+	if path.Valid() {
+		a.SetPath(name, path.Path())
+	}
+}
+
+// AddPath appends the given path string to a Make variable with the given name.
+func (a *AndroidMkInfo) AddPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], path.String())
+}
+
+// AddOptionalPath appends the given path string to a Make variable with the given name if it is
+// valid. It is a no-op if the given path is invalid.
+func (a *AndroidMkInfo) AddOptionalPath(name string, path OptionalPath) {
+	if path.Valid() {
+		a.AddPath(name, path.Path())
+	}
+}
+
+// SetPaths sets a Make variable with the given name to a slice of the given path strings.
+func (a *AndroidMkInfo) SetPaths(name string, paths Paths) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = paths.Strings()
+}
+
+// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
+// only if there are a non-zero amount of paths.
+func (a *AndroidMkInfo) SetOptionalPaths(name string, paths Paths) {
+	if len(paths) > 0 {
+		a.SetPaths(name, paths)
+	}
+}
+
+// AddPaths appends the given path strings to a Make variable with the given name.
+func (a *AndroidMkInfo) AddPaths(name string, paths Paths) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
+}
+
+// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
+// It is a no-op if the given flag is false.
+func (a *AndroidMkInfo) SetBoolIfTrue(name string, flag bool) {
+	if flag {
+		if _, ok := a.EntryMap[name]; !ok {
+			a.EntryOrder = append(a.EntryOrder, name)
+		}
+		a.EntryMap[name] = []string{"true"}
+	}
+}
+
+// SetBool sets a Make variable with the given name to if the given bool flag value.
+func (a *AndroidMkInfo) SetBool(name string, flag bool) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	if flag {
+		a.EntryMap[name] = []string{"true"}
+	} else {
+		a.EntryMap[name] = []string{"false"}
+	}
+}
+
+// AddStrings appends the given strings to a Make variable with the given name.
+func (a *AndroidMkInfo) AddStrings(name string, value ...string) {
+	if len(value) == 0 {
+		return
+	}
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], value...)
+}
+
+// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
+// for partial MTS and MCTS test suites.
+func (a *AndroidMkInfo) AddCompatibilityTestSuites(suites ...string) {
+	// M(C)TS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
+	// To reduce repetition, if we find a partial M(C)TS test suite without an full M(C)TS test suite,
+	// we add the full test suite to our list.
+	if PrefixInList(suites, "mts-") && !InList("mts", suites) {
+		suites = append(suites, "mts")
+	}
+	if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
+		suites = append(suites, "mcts")
+	}
+	a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
+}
+
+func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+	helperInfo := AndroidMkInfo{
+		EntryMap: make(map[string][]string),
+	}
+
+	amod := mod.(Module)
+	base := amod.base()
+	name := base.BaseModuleName()
+	if a.OverrideName != "" {
+		name = a.OverrideName
+	}
+
+	if a.Include == "" {
+		a.Include = "$(BUILD_PREBUILT)"
+	}
+	a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
+	a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
+	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
+
+	for _, distString := range a.GetDistForGoals(ctx, mod) {
+		a.HeaderStrings = append(a.HeaderStrings, distString)
+	}
+
+	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod)))
+
+	// Collect make variable assignment entries.
+	helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
+	helperInfo.SetString("LOCAL_MODULE", name+a.SubName)
+	helperInfo.SetString("LOCAL_MODULE_CLASS", a.Class)
+	helperInfo.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
+	helperInfo.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
+	helperInfo.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
+	helperInfo.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
+	helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
+
+	// If the install rule was generated by Soong tell Make about it.
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	if len(info.KatiInstalls) > 0 {
+		// Assume the primary install file is last since it probably needs to depend on any other
+		// installed files.  If that is not the case we can add a method to specify the primary
+		// installed file.
+		helperInfo.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
+		helperInfo.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
+		helperInfo.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
+	} else {
+		// Soong may not have generated the install rule also when `no_full_install: true`.
+		// Mark this module as uninstallable in order to prevent Make from creating an
+		// install rule there.
+		helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
+	}
+
+	if len(info.TestData) > 0 {
+		helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
+	}
+
+	if am, ok := mod.(ApexModule); ok {
+		helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
+	}
+
+	archStr := base.Arch().ArchType.String()
+	host := false
+	switch base.Os().Class {
+	case Host:
+		if base.Target().HostCross {
+			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
+			if base.Arch().ArchType != Common {
+				helperInfo.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+			}
+		} else {
+			// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
+			if base.Arch().ArchType != Common {
+				helperInfo.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
+			}
+		}
+		host = true
+	case Device:
+		// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
+		if base.Arch().ArchType != Common {
+			if base.Target().NativeBridge {
+				hostArchStr := base.Target().NativeBridgeHostArchName
+				if hostArchStr != "" {
+					helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
+				}
+			} else {
+				helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", archStr)
+			}
+		}
+
+		if !base.InVendorRamdisk() {
+			helperInfo.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
+		}
+		if len(info.VintfFragmentsPaths) > 0 {
+			helperInfo.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
+		}
+		helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
+		if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
+			helperInfo.SetString("LOCAL_VENDOR_MODULE", "true")
+		}
+		helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
+		helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
+		helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
+		if base.commonProperties.Owner != nil {
+			helperInfo.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
+		}
+	}
+
+	if host {
+		makeOs := base.Os().String()
+		if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
+			makeOs = "linux"
+		}
+		helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs)
+		helperInfo.SetString("LOCAL_IS_HOST_MODULE", "true")
+	}
+
+	prefix := ""
+	if base.ArchSpecific() {
+		switch base.Os().Class {
+		case Host:
+			if base.Target().HostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
+		case Device:
+			prefix = "TARGET_"
+
+		}
+
+		if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType {
+			prefix = "2ND_" + prefix
+		}
+	}
+
+	if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
+		helperInfo.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
+	}
+
+	if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		helperInfo.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
+	}
+
+	a.mergeEntries(&helperInfo)
+
+	// Write to footer.
+	a.FooterStrings = append([]string{"include " + a.Include}, a.FooterStrings...)
+}
+
+// This method merges the entries to helperInfo, then replaces a's EntryMap and
+// EntryOrder with helperInfo's
+func (a *AndroidMkInfo) mergeEntries(helperInfo *AndroidMkInfo) {
+	for _, extraEntry := range a.EntryOrder {
+		if v, ok := helperInfo.EntryMap[extraEntry]; ok {
+			v = append(v, a.EntryMap[extraEntry]...)
+		} else {
+			helperInfo.EntryMap[extraEntry] = a.EntryMap[extraEntry]
+			helperInfo.EntryOrder = append(helperInfo.EntryOrder, extraEntry)
+		}
+	}
+	a.EntryOrder = helperInfo.EntryOrder
+	a.EntryMap = helperInfo.EntryMap
+}
+
+func (a *AndroidMkInfo) disabled() bool {
+	return a.Disabled || !a.OutputFile.Valid()
+}
+
+// write  flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
+// given Writer object.
+func (a *AndroidMkInfo) write(w io.Writer) {
+	if a.disabled() {
+		return
+	}
+
+	combinedHeaderString := strings.Join(a.HeaderStrings, "\n")
+	combinedFooterString := strings.Join(a.FooterStrings, "\n")
+	w.Write([]byte(combinedHeaderString))
+	for _, name := range a.EntryOrder {
+		AndroidMkEmitAssignList(w, name, a.EntryMap[name])
+	}
+	w.Write([]byte(combinedFooterString))
+}
+
+// Compute the list of Make strings to declare phony goals and dist-for-goals
+// calls from the module's dist and dists properties.
+func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod blueprint.Module) []string {
+	distContributions := a.getDistContributions(ctx, mod)
+	if distContributions == nil {
+		return nil
+	}
+
+	return generateDistContributionsForMake(distContributions)
+}
+
+// Compute the contributions that the module makes to the dist.
+func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod blueprint.Module) *distContributions {
+	amod := mod.(Module).base()
+	name := amod.BaseModuleName()
+
+	// Collate the set of associated tag/paths available for copying to the dist.
+	// Start with an empty (nil) set.
+	var availableTaggedDists TaggedDistFiles
+
+	// Then merge in any that are provided explicitly by the module.
+	if a.DistFiles != nil {
+		// Merge the DistFiles into the set.
+		availableTaggedDists = availableTaggedDists.merge(a.DistFiles)
+	}
+
+	// If no paths have been provided for the DefaultDistTag and the output file is
+	// valid then add that as the default dist path.
+	if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() {
+		availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
+	}
+
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	// If the distFiles created by GenerateTaggedDistFiles contains paths for the
+	// DefaultDistTag then that takes priority so delete any existing paths.
+	if _, ok := info.DistFiles[DefaultDistTag]; ok {
+		delete(availableTaggedDists, DefaultDistTag)
+	}
+
+	// Finally, merge the distFiles created by GenerateTaggedDistFiles.
+	availableTaggedDists = availableTaggedDists.merge(info.DistFiles)
+
+	if len(availableTaggedDists) == 0 {
+		// Nothing dist-able for this module.
+		return nil
+	}
+
+	// Collate the contributions this module makes to the dist.
+	distContributions := &distContributions{}
+
+	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+		distContributions.licenseMetadataFile = info.LicenseMetadataFile
+	}
+
+	// Iterate over this module's dist structs, merged from the dist and dists properties.
+	for _, dist := range amod.Dists() {
+		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
+		goals := strings.Join(dist.Targets, " ")
+
+		// Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
+		var tag string
+		if dist.Tag == nil {
+			// If the dist struct does not specify a tag, use the default output files tag.
+			tag = DefaultDistTag
+		} else {
+			tag = *dist.Tag
+		}
+
+		// Get the paths of the output files to be dist'd, represented by the tag.
+		// Can be an empty list.
+		tagPaths := availableTaggedDists[tag]
+		if len(tagPaths) == 0 {
+			// Nothing to dist for this tag, continue to the next dist.
+			continue
+		}
+
+		if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
+			errorMessage := "%s: Cannot apply dest/suffix for more than one dist " +
+				"file for %q goals tag %q in module %s. The list of dist files, " +
+				"which should have a single element, is:\n%s"
+			panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths))
+		}
+
+		copiesForGoals := distContributions.getCopiesForGoals(goals)
+
+		// Iterate over each path adding a copy instruction to copiesForGoals
+		for _, path := range tagPaths {
+			// It's possible that the Path is nil from errant modules. Be defensive here.
+			if path == nil {
+				tagName := "default" // for error message readability
+				if dist.Tag != nil {
+					tagName = *dist.Tag
+				}
+				panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
+			}
+
+			dest := filepath.Base(path.String())
+
+			if dist.Dest != nil {
+				var err error
+				if dest, err = validateSafePath(*dist.Dest); err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			ext := filepath.Ext(dest)
+			suffix := ""
+			if dist.Suffix != nil {
+				suffix = *dist.Suffix
+			}
+
+			productString := ""
+			if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
+				productString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
+			}
+
+			if suffix != "" || productString != "" {
+				dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
+			}
+
+			if dist.Dir != nil {
+				var err error
+				if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			copiesForGoals.addCopyInstruction(path, dest)
+		}
+	}
+
+	return distContributions
+}
+
+func deepCopyAndroidMkProviderInfo(providerInfo *AndroidMkProviderInfo) AndroidMkProviderInfo {
+	info := AndroidMkProviderInfo{
+		PrimaryInfo: deepCopyAndroidMkInfo(&providerInfo.PrimaryInfo),
+	}
+	if len(providerInfo.ExtraInfo) > 0 {
+		for _, i := range providerInfo.ExtraInfo {
+			info.ExtraInfo = append(info.ExtraInfo, deepCopyAndroidMkInfo(&i))
+		}
+	}
+	return info
+}
+
+func deepCopyAndroidMkInfo(mkinfo *AndroidMkInfo) AndroidMkInfo {
+	info := AndroidMkInfo{
+		Class:        mkinfo.Class,
+		SubName:      mkinfo.SubName,
+		OverrideName: mkinfo.OverrideName,
+		// There is no modification on DistFiles or OutputFile, so no need to
+		// make their deep copy.
+		DistFiles:       mkinfo.DistFiles,
+		OutputFile:      mkinfo.OutputFile,
+		Disabled:        mkinfo.Disabled,
+		Include:         mkinfo.Include,
+		Required:        deepCopyStringSlice(mkinfo.Required),
+		Host_required:   deepCopyStringSlice(mkinfo.Host_required),
+		Target_required: deepCopyStringSlice(mkinfo.Target_required),
+		HeaderStrings:   deepCopyStringSlice(mkinfo.HeaderStrings),
+		FooterStrings:   deepCopyStringSlice(mkinfo.FooterStrings),
+		EntryOrder:      deepCopyStringSlice(mkinfo.EntryOrder),
+	}
+	info.EntryMap = make(map[string][]string)
+	for k, v := range mkinfo.EntryMap {
+		info.EntryMap[k] = deepCopyStringSlice(v)
+	}
+
+	return info
+}
+
+func deepCopyStringSlice(original []string) []string {
+	result := make([]string, len(original))
+	copy(result, original)
+	return result
+}
diff --git a/android/apex.go b/android/apex.go
index 114fe29..79ab13c 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -989,8 +989,8 @@
 // Function called while walking an APEX's payload dependencies.
 //
 // Return true if the `to` module should be visited, false otherwise.
-type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
-type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback)
+type PayloadDepsCallback func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
+type WalkPayloadDepsFunc func(ctx BaseModuleContext, do PayloadDepsCallback)
 
 // ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
 type ModuleWithMinSdkVersionCheck interface {
@@ -1017,7 +1017,7 @@
 		return
 	}
 
-	walk(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
+	walk(ctx, func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
 		if externalDep {
 			// external deps are outside the payload boundary, which is "stable"
 			// interface. We don't have to check min_sdk_version for external
diff --git a/android/arch.go b/android/arch.go
index 6d896e5..1ec971d 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"
 )
 
@@ -139,6 +138,16 @@
 	return a.Name
 }
 
+func (a ArchType) Bitness() string {
+	if a.Multilib == "lib32" {
+		return "32"
+	}
+	if a.Multilib == "lib64" {
+		return "64"
+	}
+	panic("Bitness is not defined for the common variant")
+}
+
 const COMMON_VARIANT = "common"
 
 var (
@@ -397,45 +406,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 +428,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 +545,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 +576,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 +628,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 +648,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 +656,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 +664,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 +783,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_list.go b/android/arch_list.go
index 9501c87..389f194 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -29,6 +29,7 @@
 		"armv9-2a",
 	},
 	X86: {
+		"alderlake",
 		"amberlake",
 		"atom",
 		"broadwell",
@@ -53,6 +54,7 @@
 		"x86_64",
 	},
 	X86_64: {
+		"alderlake",
 		"amberlake",
 		"broadwell",
 		"goldmont",
@@ -110,9 +112,6 @@
 }
 
 var archFeatures = map[ArchType][]string{
-	Arm: {
-		"neon",
-	},
 	Arm64: {
 		"dotprod",
 	},
@@ -142,17 +141,6 @@
 }
 
 var androidArchFeatureMap = map[ArchType]map[string][]string{
-	Arm: {
-		"armv7-a-neon": {
-			"neon",
-		},
-		"armv8-a": {
-			"neon",
-		},
-		"armv8-2a": {
-			"neon",
-		},
-	},
 	Arm64: {
 		"armv8-2a-dotprod": {
 			"dotprod",
@@ -165,6 +153,16 @@
 		},
 	},
 	X86: {
+		"alderlake": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"avx",
+			"avx2",
+			"aes_ni",
+			"popcnt",
+		},
 		"amberlake": {
 			"ssse3",
 			"sse4",
@@ -341,6 +339,16 @@
 			"sse4_2",
 			"popcnt",
 		},
+		"alderlake": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"avx",
+			"avx2",
+			"aes_ni",
+			"popcnt",
+		},
 		"amberlake": {
 			"ssse3",
 			"sse4",
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/base_module_context.go b/android/base_module_context.go
index 5506000..670537f 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -33,6 +33,8 @@
 
 	blueprintBaseModuleContext() blueprint.BaseModuleContext
 
+	EqualModules(m1, m2 Module) bool
+
 	// OtherModuleName returns the name of another Module.  See BaseModuleContext.ModuleName for more information.
 	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleName(m blueprint.Module) string
@@ -113,31 +115,30 @@
 	// the first DependencyTag.
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 
-	// VisitDirectDepsBlueprint calls visit for each direct dependency.  If there are multiple
+	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
+	// direct dependencies on the same module visit will be called multiple times on that module
+	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
+	// dependencies are disabled.
+	//
+	// The Module passed to the visit function should not be retained outside of the visit
+	// function, it may be invalidated by future mutators.
+	VisitDirectDeps(visit func(Module))
+
+	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
 	// direct dependencies on the same module visit will be called multiple times on that module
 	// and OtherModuleDependencyTag will return a different tag for each.
 	//
 	// The Module passed to the visit function should not be retained outside of the visit
 	// function, it may be invalidated by future mutators.
-	VisitDirectDepsBlueprint(visit func(blueprint.Module))
+	VisitDirectDepsAllowDisabled(visit func(Module))
 
-	// VisitDirectDepsIgnoreBlueprint calls visit for each direct dependency.  If there are multiple
-	// direct dependencies on the same module visit will be called multiple times on that module
-	// and OtherModuleDependencyTag will return a different tag for each.  It silently ignores any
-	// dependencies that are not an android.Module.
+	// VisitDirectDepsProxyAllowDisabled calls visit for each direct dependency.  If there are
+	// multiple direct dependencies on the same module visit will be called multiple times on
+	// that module and OtherModuleDependencyTag will return a different tag for each.
 	//
-	// The Module passed to the visit function should not be retained outside of the visit
-	// function, it may be invalidated by future mutators.
-	VisitDirectDepsIgnoreBlueprint(visit func(Module))
-
-	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
-	// direct dependencies on the same module visit will be called multiple times on that module
-	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
-	// dependencies are not an android.Module.
-	//
-	// The Module passed to the visit function should not be retained outside of the visit
-	// function, it may be invalidated by future mutators.
-	VisitDirectDeps(visit func(Module))
+	// The Module passed to the visit function should not be retained outside of the visit function, it may be
+	// invalidated by future mutators.
+	VisitDirectDepsProxyAllowDisabled(visit func(proxy Module))
 
 	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
 
@@ -164,16 +165,15 @@
 	// invalidated by future mutators.
 	WalkDeps(visit func(child, parent Module) bool)
 
-	// WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency
-	// tree in top down order.  visit may be called multiple times for the same (child, parent)
-	// pair if there are multiple direct dependencies between the child and parent with different
-	// tags.  OtherModuleDependencyTag will return the tag for the currently visited
-	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down
-	// to child.
+	// WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order.  visit may
+	// be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the
+	// child and parent with different tags.  OtherModuleDependencyTag will return the tag for the currently visited
+	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down to child.  It skips
+	// any dependencies that are not an android.Module.
 	//
 	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
 	// invalidated by future mutators.
-	WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
+	WalkDepsProxy(visit func(child, parent Module) bool)
 
 	// GetWalkPath is supposed to be called in visit function passed in WalkDeps()
 	// and returns a top-down dependency path from a start module to current child module.
@@ -234,15 +234,26 @@
 
 }
 
+func getWrappedModule(module blueprint.Module) blueprint.Module {
+	if mp, isProxy := module.(ModuleProxy); isProxy {
+		return mp.module
+	}
+	return module
+}
+
+func (b *baseModuleContext) EqualModules(m1, m2 Module) bool {
+	return b.bp.EqualModules(getWrappedModule(m1), getWrappedModule(m2))
+}
+
 func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
-	return b.bp.OtherModuleName(m)
+	return b.bp.OtherModuleName(getWrappedModule(m))
 }
 func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) }
 func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
 	b.bp.OtherModuleErrorf(m, fmt, args...)
 }
 func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
-	return b.bp.OtherModuleDependencyTag(m)
+	return b.bp.OtherModuleDependencyTag(getWrappedModule(m))
 }
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
 func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
@@ -311,7 +322,7 @@
 	return true
 }
 
-func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module {
+func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module {
 	aModule, _ := module.(Module)
 
 	if !strict {
@@ -319,10 +330,7 @@
 	}
 
 	if aModule == nil {
-		if !ignoreBlueprint {
-			b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
-		}
-		return nil
+		panic(fmt.Errorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag))
 	}
 
 	if !aModule.Enabled(b) {
@@ -345,15 +353,8 @@
 
 func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep {
 	var deps []dep
-	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
-		if aModule, _ := module.(Module); aModule != nil {
-			if aModule.base().BaseModuleName() == name {
-				returnedTag := b.bp.OtherModuleDependencyTag(aModule)
-				if tag == nil || returnedTag == tag {
-					deps = append(deps, dep{aModule, returnedTag})
-				}
-			}
-		} else if b.bp.OtherModuleName(module) == name {
+	b.VisitDirectDeps(func(module Module) {
+		if module.base().BaseModuleName() == name {
 			returnedTag := b.bp.OtherModuleDependencyTag(module)
 			if tag == nil || returnedTag == tag {
 				deps = append(deps, dep{module, returnedTag})
@@ -396,11 +397,9 @@
 
 func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
 	var deps []Module
-	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
-		if aModule, _ := module.(Module); aModule != nil {
-			if b.bp.OtherModuleDependencyTag(aModule) == tag {
-				deps = append(deps, aModule)
-			}
+	b.VisitDirectDeps(func(module Module) {
+		if b.bp.OtherModuleDependencyTag(module) == tag {
+			deps = append(deps, module)
 		}
 	})
 	return deps
@@ -413,30 +412,32 @@
 	return b.getDirectDepFirstTag(name)
 }
 
-func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
-	b.bp.VisitDirectDeps(visit)
-}
-
 func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
-	b.visitDirectDeps(visit, false)
-}
-
-func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) {
-	b.visitDirectDeps(visit, true)
-}
-
-func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); aModule != nil {
+		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
 	})
 }
 
+func (b *baseModuleContext) VisitDirectDepsAllowDisabled(visit func(Module)) {
+	b.bp.VisitDirectDeps(func(module blueprint.Module) {
+		visit(module.(Module))
+	})
+}
+
+func (b *baseModuleContext) VisitDirectDepsProxyAllowDisabled(visit func(proxy Module)) {
+	b.bp.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
+		visit(ModuleProxy{
+			module: module,
+		})
+	})
+}
+
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if b.bp.OtherModuleDependencyTag(module) == tag {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 				visit(aModule)
 			}
 		}
@@ -447,7 +448,7 @@
 	b.bp.VisitDirectDepsIf(
 		// pred
 		func(module blueprint.Module) bool {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 				return pred(aModule)
 			} else {
 				return false
@@ -461,7 +462,7 @@
 
 func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
 	b.bp.VisitDepsDepthFirst(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
 	})
@@ -471,7 +472,7 @@
 	b.bp.VisitDepsDepthFirstIf(
 		// pred
 		func(module blueprint.Module) bool {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 				return pred(aModule)
 			} else {
 				return false
@@ -483,10 +484,6 @@
 		})
 }
 
-func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
-	b.bp.WalkDeps(visit)
-}
-
 func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
 	b.walkPath = []Module{b.Module()}
 	b.tagPath = []blueprint.DependencyTag{}
@@ -508,6 +505,23 @@
 	})
 }
 
+func (b *baseModuleContext) WalkDepsProxy(visit func(Module, Module) bool) {
+	b.walkPath = []Module{ModuleProxy{blueprint.CreateModuleProxy(b.Module())}}
+	b.tagPath = []blueprint.DependencyTag{}
+	b.bp.WalkDepsProxy(func(child, parent blueprint.ModuleProxy) bool {
+		childAndroidModule := ModuleProxy{child}
+		parentAndroidModule := ModuleProxy{parent}
+		// record walkPath before visit
+		for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
+			b.walkPath = b.walkPath[0 : len(b.walkPath)-1]
+			b.tagPath = b.tagPath[0 : len(b.tagPath)-1]
+		}
+		b.walkPath = append(b.walkPath, childAndroidModule)
+		b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule))
+		return visit(childAndroidModule, parentAndroidModule)
+	})
+}
+
 func (b *baseModuleContext) GetWalkPath() []Module {
 	return b.walkPath
 }
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 38f1382..d28831e 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -17,7 +17,6 @@
 import (
 	"bytes"
 	"encoding/csv"
-	"encoding/gob"
 	"fmt"
 	"slices"
 	"strconv"
@@ -126,32 +125,32 @@
 	properties map[string]string
 }
 
+type complianceMetadataInfoGob struct {
+	Properties map[string]string
+}
+
 func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
 	return &ComplianceMetadataInfo{
 		properties: map[string]string{},
 	}
 }
 
-func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := encoder.Encode(c.properties)
-	if err != nil {
-		return nil, err
+func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
+	return &complianceMetadataInfoGob{
+		Properties: m.properties,
 	}
+}
 
-	return w.Bytes(), nil
+func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
+	m.properties = data.Properties
+}
+
+func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[complianceMetadataInfoGob](c)
 }
 
 func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := decoder.Decode(&c.properties)
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[complianceMetadataInfoGob](data, c)
 }
 
 func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
diff --git a/android/config.go b/android/config.go
index b682c2e..06d71c0 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 {
@@ -245,6 +250,13 @@
 		Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource)
 }
 
+func (c Config) ReleaseDefaultUpdatableModuleVersion() string {
+	if val, exists := c.GetBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION"); exists {
+		return val
+	}
+	panic("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION is missing from build flags.")
+}
+
 func (c Config) ReleaseDisableVerifyOverlaps() bool {
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_DISABLE_VERIFY_OVERLAPS_CHECK")
 }
@@ -275,6 +287,10 @@
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_READ_FROM_NEW_STORAGE")
 }
 
+func (c Config) ReleaseCreateAconfigStorageFile() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_CREATE_ACONFIG_STORAGE_FILE")
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -1247,7 +1263,7 @@
 }
 
 func (c *config) LibartImgHostBaseAddress() string {
-	return "0x60000000"
+	return "0x70000000"
 }
 
 func (c *config) LibartImgDeviceBaseAddress() string {
@@ -1273,6 +1289,7 @@
 	}
 	return false
 }
+
 func (c *config) EnforceRROExcludedOverlay(path string) bool {
 	excluded := c.productVariables.EnforceRROExcludedOverlays
 	if len(excluded) > 0 {
@@ -1281,6 +1298,11 @@
 	return false
 }
 
+func (c *config) EnforceRROGlobally() bool {
+	enforceList := c.productVariables.EnforceRROTargets
+	return InList("*", enforceList)
+}
+
 func (c *config) ExportedNamespaces() []string {
 	return append([]string(nil), c.productVariables.NamespacesToExport...)
 }
@@ -1663,6 +1685,17 @@
 	return Bool(c.productVariables.TrimmedApex)
 }
 
+func (c *config) UseSoongSystemImage() bool {
+	return Bool(c.productVariables.UseSoongSystemImage)
+}
+
+func (c *config) SoongDefinedSystemImage() string {
+	if c.UseSoongSystemImage() {
+		return String(c.productVariables.ProductSoongDefinedSystemImage)
+	}
+	return ""
+}
+
 func (c *config) EnforceSystemCertificate() bool {
 	return Bool(c.productVariables.EnforceSystemCertificate)
 }
@@ -1675,14 +1708,6 @@
 	return Bool(c.productVariables.EnforceProductPartitionInterface)
 }
 
-func (c *config) EnforceInterPartitionJavaSdkLibrary() bool {
-	return Bool(c.productVariables.EnforceInterPartitionJavaSdkLibrary)
-}
-
-func (c *config) InterPartitionJavaLibraryAllowList() []string {
-	return c.productVariables.InterPartitionJavaLibraryAllowList
-}
-
 func (c *config) ProductHiddenAPIStubs() []string {
 	return c.productVariables.ProductHiddenAPIStubs
 }
@@ -1954,6 +1979,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")
 }
@@ -2061,6 +2090,10 @@
 	return PathsForSource(ctx, c.productVariables.OdmPropFiles)
 }
 
+func (c *config) ExtraAllowedDepsTxt() string {
+	return String(c.productVariables.ExtraAllowedDepsTxt)
+}
+
 func (c *config) EnableUffdGc() string {
 	return String(c.productVariables.EnableUffdGc)
 }
@@ -2080,3 +2113,10 @@
 func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string {
 	return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs
 }
+
+// Returns true if RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION is set to true.
+// If true, dexpreopt files of apex system server jars will be installed in the same partition as the parent apex.
+// If false, all these files will be installed in /system partition.
+func (c Config) InstallApexSystemServerDexpreoptSamePartition() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION")
+}
diff --git a/android/container.go b/android/container.go
index c048d6c..2a3777b 100644
--- a/android/container.go
+++ b/android/container.go
@@ -479,7 +479,7 @@
 func checkContainerViolations(ctx ModuleContext) {
 	if _, ok := ctx.Module().(InstallableModule); ok {
 		containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
-		ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) {
+		ctx.VisitDirectDeps(func(dep Module) {
 			if !dep.Enabled(ctx) {
 				return
 			}
diff --git a/android/deapexer.go b/android/deapexer.go
index dcae3e4..4049d2b 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -109,10 +108,6 @@
 	return i.exportedModuleNames
 }
 
-// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
-// on a `deapexer` module to retrieve its `DeapexerInfo`.
-var DeapexerProvider = blueprint.NewProvider[DeapexerInfo]()
-
 // NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
 // for use with a prebuilt_apex module.
 //
@@ -169,45 +164,6 @@
 	RequiresFilesFromPrebuiltApex()
 }
 
-// FindDeapexerProviderForModule searches through the direct dependencies of the current context
-// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
-// deapexer module isn't found then it returns it an error
-// clients should check the value of error and call ctx.ModuleErrof if a non nil error is received
-func FindDeapexerProviderForModule(ctx ModuleContext) (*DeapexerInfo, error) {
-	var di *DeapexerInfo
-	var err error
-	ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
-		if err != nil {
-			// An err has been found. Do not visit further.
-			return
-		}
-		c, ok := OtherModuleProvider(ctx, m, DeapexerProvider)
-		if !ok {
-			ctx.ModuleErrorf("Expected all deps with DeapexerTag to have a DeapexerProvider, but module %q did not", m.Name())
-			return
-		}
-		p := &c
-		if di != nil {
-			// If two DeapexerInfo providers have been found then check if they are
-			// equivalent. If they are then use the selected one, otherwise fail.
-			if selected := equivalentDeapexerInfoProviders(di, p); selected != nil {
-				di = selected
-				return
-			}
-			err = fmt.Errorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s", di.ApexModuleName(), p.ApexModuleName())
-		}
-		di = p
-	})
-	if err != nil {
-		return nil, err
-	}
-	if di != nil {
-		return di, nil
-	}
-	ai, _ := ModuleProvider(ctx, ApexInfoProvider)
-	return nil, fmt.Errorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
-}
-
 // removeCompressedApexSuffix removes the _compressed suffix from the name if present.
 func removeCompressedApexSuffix(name string) string {
 	return strings.TrimSuffix(name, "_compressed")
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/depset_generic.go b/android/depset_generic.go
index 690987a..d04f88b 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -15,10 +15,9 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
+
+	"github.com/google/blueprint"
 )
 
 // DepSet is designed to be conceptually compatible with Bazel's depsets:
@@ -68,28 +67,38 @@
 	transitive []*DepSet[T]
 }
 
-func (d *DepSet[T]) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(d.preorder), encoder.Encode(d.reverse),
-		encoder.Encode(d.order), encoder.Encode(d.direct), encoder.Encode(d.transitive))
-	if err != nil {
-		return nil, err
-	}
+type depSetGob[T depSettableType] struct {
+	Preorder   bool
+	Reverse    bool
+	Order      DepSetOrder
+	Direct     []T
+	Transitive []*DepSet[T]
+}
 
-	return w.Bytes(), nil
+func (d *DepSet[T]) ToGob() *depSetGob[T] {
+	return &depSetGob[T]{
+		Preorder:   d.preorder,
+		Reverse:    d.reverse,
+		Order:      d.order,
+		Direct:     d.direct,
+		Transitive: d.transitive,
+	}
+}
+
+func (d *DepSet[T]) FromGob(data *depSetGob[T]) {
+	d.preorder = data.Preorder
+	d.reverse = data.Reverse
+	d.order = data.Order
+	d.direct = data.Direct
+	d.transitive = data.Transitive
+}
+
+func (d *DepSet[T]) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[depSetGob[T]](d)
 }
 
 func (d *DepSet[T]) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&d.preorder), decoder.Decode(&d.reverse),
-		decoder.Decode(&d.order), decoder.Decode(&d.direct), decoder.Decode(&d.transitive))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[depSetGob[T]](data, d)
 }
 
 // NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
diff --git a/android/early_module_context.go b/android/early_module_context.go
index 23f4c90..11de771 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -93,6 +93,10 @@
 	// Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the
 	// default SimpleNameInterface if Context.SetNameInterface was not called.
 	Namespace() *Namespace
+
+	// HasMutatorFinished returns true if the given mutator has finished running.
+	// It will panic if given an invalid mutator name.
+	HasMutatorFinished(mutatorName string) bool
 }
 
 // Deprecated: use EarlyModuleContext instead
@@ -175,3 +179,7 @@
 func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) {
 	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...)
 }
+
+func (e *earlyModuleContext) HasMutatorFinished(mutatorName string) bool {
+	return e.EarlyModuleContext.HasMutatorFinished(mutatorName)
+}
diff --git a/android/hooks.go b/android/hooks.go
index 2ad3b5f..bd2fa5e 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -95,10 +95,17 @@
 
 type createModuleContext interface {
 	Module() Module
+	HasMutatorFinished(mutatorName string) bool
 	createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module
 }
 
 func createModule(ctx createModuleContext, factory ModuleFactory, ext string, props ...interface{}) Module {
+	if ctx.HasMutatorFinished("defaults") {
+		// Creating modules late is oftentimes problematic, because they don't have earlier
+		// mutators run on them. Prevent making modules after the defaults mutator has run.
+		panic("Cannot create a module after the defaults mutator has finished")
+	}
+
 	inherited := []interface{}{&ctx.Module().base().commonProperties}
 
 	var typeName string
diff --git a/android/init.go b/android/init.go
index d5b486b..1ace344 100644
--- a/android/init.go
+++ b/android/init.go
@@ -17,6 +17,12 @@
 import "encoding/gob"
 
 func init() {
+	gob.Register(extraFilesZip{})
+	gob.Register(InstallPath{})
+	gob.Register(ModuleGenPath{})
 	gob.Register(ModuleOutPath{})
+	gob.Register(OutputPath{})
+	gob.Register(PhonyPath{})
+	gob.Register(SourcePath{})
 	gob.Register(unstableInfo{})
 }
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 0ac975f..f925638 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -63,11 +63,7 @@
 	var allDepOutputFiles Paths
 	var allDepMetadataDepSets []*DepSet[Path]
 
-	ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
-		dep, _ := bpdep.(Module)
-		if dep == nil {
-			return
-		}
+	ctx.VisitDirectDeps(func(dep Module) {
 		if !dep.Enabled(ctx) {
 			return
 		}
diff --git a/android/module.go b/android/module.go
index 009b0df..44f7583 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"net/url"
 	"path/filepath"
@@ -58,7 +55,7 @@
 
 	base() *ModuleBase
 	Disable()
-	Enabled(ctx ConfigAndErrorContext) bool
+	Enabled(ctx ConfigurableEvaluatorContext) bool
 	Target() Target
 	MultiTargets() []Target
 
@@ -81,6 +78,7 @@
 	InstallInOdm() bool
 	InstallInProduct() bool
 	InstallInVendor() bool
+	InstallInSystemExt() bool
 	InstallForceOS() (*OsType, *ArchType)
 	PartitionTag(DeviceConfig) string
 	HideFromMake()
@@ -109,12 +107,12 @@
 	// Get information about the properties that can contain visibility rules.
 	visibilityProperties() []visibilityProperty
 
-	RequiredModuleNames(ctx ConfigAndErrorContext) []string
+	RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
-	VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string
+	VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string
 
-	ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator
+	ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator
 }
 
 // Qualified id for a module
@@ -381,7 +379,7 @@
 	Native_bridge_supported *bool `android:"arch_variant"`
 
 	// init.rc files to be installed if this module is installed
-	Init_rc []string `android:"arch_variant,path"`
+	Init_rc proptools.Configurable[[]string] `android:"arch_variant,path"`
 
 	// VINTF manifest fragments to be installed if this module is installed
 	Vintf_fragments proptools.Configurable[[]string] `android:"path"`
@@ -443,12 +441,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.
@@ -1063,8 +1055,29 @@
 }{}
 
 func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
+	// Vintf manifests in the recovery partition will be ignored.
+	if !ctx.Device() || ctx.Module().InstallInRecovery() {
+		return
+	}
+
+	deviceConfig := ctx.DeviceConfig()
+
 	mod := ctx.Module()
-	ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+	vintfModules := ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+
+	modPartition := mod.PartitionTag(deviceConfig)
+	for _, vintf := range vintfModules {
+		if vintfModule, ok := vintf.(*vintfFragmentModule); ok {
+			vintfPartition := vintfModule.PartitionTag(deviceConfig)
+			if modPartition != vintfPartition {
+				ctx.ModuleErrorf("Module %q(%q) and Vintf_fragment %q(%q) are installed to different partitions.",
+					mod.Name(), modPartition,
+					vintfModule.Name(), vintfPartition)
+			}
+		} else {
+			ctx.ModuleErrorf("Only vintf_fragment type module should be listed in vintf_fragment_modules : %q", vintf.Name())
+		}
+	}
 }
 
 // AddProperties "registers" the provided props
@@ -1221,7 +1234,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.
@@ -1339,13 +1352,21 @@
 	return partition
 }
 
-func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) bool {
+func (m *ModuleBase) Enabled(ctx ConfigurableEvaluatorContext) bool {
 	if m.commonProperties.ForcedDisabled {
 		return false
 	}
 	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
 }
@@ -1483,6 +1504,10 @@
 	return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Soc_specific) || Bool(m.commonProperties.Proprietary)
 }
 
+func (m *ModuleBase) InstallInSystemExt() bool {
+	return Bool(m.commonProperties.System_ext_specific)
+}
+
 func (m *ModuleBase) InstallInRoot() bool {
 	return false
 }
@@ -1536,7 +1561,7 @@
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
 
-func (m *ModuleBase) RequiredModuleNames(ctx ConfigAndErrorContext) []string {
+func (m *ModuleBase) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
 	return m.base().commonProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
@@ -1548,7 +1573,7 @@
 	return m.base().commonProperties.Target_required
 }
 
-func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string {
+func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string {
 	return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
@@ -1770,6 +1795,26 @@
 
 var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]()
 
+type CommonPropertiesProviderData struct {
+	Enabled bool
+	// Whether the module has been replaced by a prebuilt
+	ReplacedByPrebuilt bool
+}
+
+var CommonPropertiesProviderKey = blueprint.NewProvider[CommonPropertiesProviderData]()
+
+type PrebuiltModuleProviderData struct {
+	// Empty for now
+}
+
+var PrebuiltModuleProviderKey = blueprint.NewProvider[PrebuiltModuleProviderData]()
+
+type HostToolProviderData struct {
+	HostToolPath OptionalPath
+}
+
+var HostToolProviderKey = blueprint.NewProvider[HostToolProviderData]()
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
@@ -1838,10 +1883,8 @@
 
 	if m.Enabled(ctx) {
 		// ensure all direct android.Module deps are enabled
-		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
-			if m, ok := bm.(Module); ok {
-				ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps, false)
-			}
+		ctx.VisitDirectDeps(func(m Module) {
+			ctx.validateAndroidModule(m, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps)
 		})
 
 		if m.Device() {
@@ -1853,7 +1896,7 @@
 			// so only a single rule is created for each init.rc or vintf fragment file.
 
 			if !m.InVendorRamdisk() {
-				ctx.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
+				ctx.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc.GetOrDefault(ctx, nil))
 				rcDir := PathForModuleInstall(ctx, "etc", "init")
 				for _, src := range ctx.initRcPaths {
 					installedInitRc := rcDir.Join(ctx, src.Base())
@@ -2017,6 +2060,23 @@
 		})
 	}
 	buildComplianceMetadataProvider(ctx, m)
+
+	commonData := CommonPropertiesProviderData{
+		ReplacedByPrebuilt: m.commonProperties.ReplacedByPrebuilt,
+	}
+	if m.commonProperties.ForcedDisabled {
+		commonData.Enabled = false
+	} else {
+		commonData.Enabled = m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
+	}
+	SetProvider(ctx, CommonPropertiesProviderKey, commonData)
+	if p, ok := m.module.(PrebuiltInterface); ok && p.Prebuilt() != nil {
+		SetProvider(ctx, PrebuiltModuleProviderKey, PrebuiltModuleProviderData{})
+	}
+	if h, ok := m.module.(HostToolProvider); ok {
+		SetProvider(ctx, HostToolProviderKey, HostToolProviderData{
+			HostToolPath: h.HostToolPath()})
+	}
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
@@ -2096,36 +2156,47 @@
 	orderOnlyDeps Paths
 	executable    bool
 	extraFiles    *extraFilesZip
-
-	absFrom string
+	absFrom       string
 }
 
-func (p *katiInstall) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.from), encoder.Encode(p.to),
-		encoder.Encode(p.implicitDeps), encoder.Encode(p.orderOnlyDeps),
-		encoder.Encode(p.executable), encoder.Encode(p.extraFiles),
-		encoder.Encode(p.absFrom))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
+type katiInstallGob struct {
+	From          Path
+	To            InstallPath
+	ImplicitDeps  Paths
+	OrderOnlyDeps Paths
+	Executable    bool
+	ExtraFiles    *extraFilesZip
+	AbsFrom       string
 }
 
-func (p *katiInstall) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.from), decoder.Decode(&p.to),
-		decoder.Decode(&p.implicitDeps), decoder.Decode(&p.orderOnlyDeps),
-		decoder.Decode(&p.executable), decoder.Decode(&p.extraFiles),
-		decoder.Decode(&p.absFrom))
-	if err != nil {
-		return err
+func (k *katiInstall) ToGob() *katiInstallGob {
+	return &katiInstallGob{
+		From:          k.from,
+		To:            k.to,
+		ImplicitDeps:  k.implicitDeps,
+		OrderOnlyDeps: k.orderOnlyDeps,
+		Executable:    k.executable,
+		ExtraFiles:    k.extraFiles,
+		AbsFrom:       k.absFrom,
 	}
+}
 
-	return nil
+func (k *katiInstall) FromGob(data *katiInstallGob) {
+	k.from = data.From
+	k.to = data.To
+	k.implicitDeps = data.ImplicitDeps
+	k.orderOnlyDeps = data.OrderOnlyDeps
+	k.executable = data.Executable
+	k.extraFiles = data.ExtraFiles
+	k.absFrom = data.AbsFrom
+}
+
+func (k *katiInstall) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[katiInstallGob](k)
+}
+
+func (k *katiInstall) GobDecode(data []byte) error {
+	return blueprint.CustomGobDecode[katiInstallGob](data, k)
 }
 
 type extraFilesZip struct {
@@ -2133,26 +2204,29 @@
 	dir InstallPath
 }
 
-func (p *extraFilesZip) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.zip), encoder.Encode(p.dir))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
+type extraFilesZipGob struct {
+	Zip Path
+	Dir InstallPath
 }
 
-func (p *extraFilesZip) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.zip), decoder.Decode(&p.dir))
-	if err != nil {
-		return err
+func (e *extraFilesZip) ToGob() *extraFilesZipGob {
+	return &extraFilesZipGob{
+		Zip: e.zip,
+		Dir: e.dir,
 	}
+}
 
-	return nil
+func (e *extraFilesZip) FromGob(data *extraFilesZipGob) {
+	e.zip = data.Zip
+	e.dir = data.Dir
+}
+
+func (e *extraFilesZip) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[extraFilesZipGob](e)
+}
+
+func (e *extraFilesZip) GobDecode(data []byte) error {
+	return blueprint.CustomGobDecode[extraFilesZipGob](data, e)
 }
 
 type katiInstalls []katiInstall
@@ -2204,17 +2278,23 @@
 	return proptools.Bool(m.commonProperties.Native_bridge_supported)
 }
 
-type ConfigAndErrorContext interface {
+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 {
-	ctx ConfigAndErrorContext
+	ctx ConfigurableEvaluatorContext
 	m   Module
 }
 
-func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator {
+func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator {
 	return configurationEvalutor{
 		ctx: ctx,
 		m:   m.module,
@@ -2228,6 +2308,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 {
@@ -2255,6 +2341,8 @@
 		}
 		variable := condition.Arg(0)
 		switch variable {
+		case "build_from_text_stub":
+			return proptools.ConfigurableValueBool(ctx.Config().BuildFromTextStub())
 		case "debuggable":
 			return proptools.ConfigurableValueBool(ctx.Config().Debuggable())
 		case "use_debug_art":
diff --git a/android/module_context.go b/android/module_context.go
index 3889e40..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)
@@ -194,7 +196,7 @@
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 
-	RequiredModuleNames(ctx ConfigAndErrorContext) []string
+	RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
 
@@ -779,7 +781,7 @@
 	m.uncheckedModule = true
 }
 
-func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
+func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
 
@@ -855,7 +857,7 @@
 	return OptionalPath{}
 }
 
-func (m *moduleContext) RequiredModuleNames(ctx ConfigAndErrorContext) []string {
+func (m *moduleContext) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
 	return m.module.RequiredModuleNames(ctx)
 }
 
diff --git a/android/module_proxy.go b/android/module_proxy.go
new file mode 100644
index 0000000..bc5090e
--- /dev/null
+++ b/android/module_proxy.go
@@ -0,0 +1,203 @@
+package android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+type ModuleProxy struct {
+	module blueprint.ModuleProxy
+}
+
+func (m ModuleProxy) Name() string {
+	return m.module.Name()
+}
+
+func (m ModuleProxy) GenerateBuildActions(context blueprint.ModuleContext) {
+	m.module.GenerateBuildActions(context)
+}
+
+func (m ModuleProxy) GenerateAndroidBuildActions(context ModuleContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ComponentDepsMutator(ctx BottomUpMutatorContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) DepsMutator(context BottomUpMutatorContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) base() *ModuleBase {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Disable() {
+
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Enabled(ctx ConfigurableEvaluatorContext) bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Target() Target {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) MultiTargets() []Target {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ImageVariation() blueprint.Variation {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Owner() string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInData() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInTestcases() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSanitizerDir() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendorRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInDebugRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRecovery() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRoot() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInOdm() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInProduct() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendor() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSystemExt() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallForceOS() (*OsType, *ArchType) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) PartitionTag(d DeviceConfig) string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) HideFromMake() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsHideFromMake() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsSkipInstall() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) MakeUninstallable() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ReplacedByPrebuilt() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsReplacedByPrebuilt() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ExportedToMake() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) EffectiveLicenseKinds() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) EffectiveLicenseFiles() Paths {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) AddProperties(props ...interface{}) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) GetProperties() []interface{} {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) BuildParamsForTests() []BuildParams {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VariablesForTests() map[string]string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) String() string {
+	return m.module.Name()
+}
+
+func (m ModuleProxy) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) visibilityProperties() []visibilityProperty {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) HostRequiredModuleNames() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) TargetRequiredModuleNames() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator {
+	panic("method is not implemented on ModuleProxy")
+}
diff --git a/android/module_test.go b/android/module_test.go
index d64e3a5..d76d9b3 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -1080,3 +1080,29 @@
 		})
 	}
 }
+
+func TestVintfFragmentModulesChecksPartition(t *testing.T) {
+	bp := `
+	vintf_fragment {
+		name: "vintfModA",
+		src: "test_vintf_file",
+		vendor: true,
+	}
+	deps {
+		name: "modA",
+		vintf_fragment_modules: [
+			"vintfModA",
+		]
+	}
+	`
+
+	testPreparer := GroupFixturePreparers(
+		PrepareForTestWithAndroidBuildComponents,
+		prepareForModuleTests,
+	)
+
+	testPreparer.
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(
+			"Module .+ and Vintf_fragment .+ are installed to different partitions.")).
+		RunTestWithBp(t, bp)
+}
diff --git a/android/mutator.go b/android/mutator.go
index 38fb857..8265458 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -26,17 +26,17 @@
 //   run Pre-deps mutators
 //   run depsMutator
 //   run PostDeps mutators
-//   run FinalDeps mutators (CreateVariations disallowed in this phase)
+//   run FinalDeps mutators (TransitionMutators disallowed in this phase)
 //   continue on to GenerateAndroidBuildActions
 
 // collateGloballyRegisteredMutators constructs the list of mutators that have been registered
 // with the InitRegistrationContext and will be used at runtime.
 func collateGloballyRegisteredMutators() sortableComponents {
-	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
+	return collateRegisteredMutators(preArch, preDeps, postDeps, postApex, finalDeps)
 }
 
 // collateRegisteredMutators constructs a single list of mutators from the separate lists.
-func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) sortableComponents {
+func collateRegisteredMutators(preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc) sortableComponents {
 	mctx := &registerMutatorsContext{}
 
 	register := func(funcs []RegisterMutatorFunc) {
@@ -53,6 +53,8 @@
 
 	register(postDeps)
 
+	register(postApex)
+
 	mctx.finalPhase = true
 	register(finalDeps)
 
@@ -148,9 +150,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{
@@ -166,6 +168,8 @@
 	RegisterOverridePostDepsMutators,
 }
 
+var postApex = []RegisterMutatorFunc{}
+
 var finalDeps = []RegisterMutatorFunc{}
 
 func PreArchMutators(f RegisterMutatorFunc) {
@@ -180,6 +184,10 @@
 	postDeps = append(postDeps, f)
 }
 
+func PostApexMutators(f RegisterMutatorFunc) {
+	postApex = append(postApex, f)
+}
+
 func FinalDepsMutators(f RegisterMutatorFunc) {
 	finalDeps = append(finalDeps, f)
 }
@@ -193,16 +201,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 {
@@ -231,36 +239,6 @@
 	// module's dependency list.
 	AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
 
-	// CreateVariations splits  a module into multiple variants, one for each name in the variationNames
-	// parameter.  It returns a list of new modules in the same order as the variationNames
-	// list.
-	//
-	// If any of the dependencies of the module being operated on were already split
-	// by calling CreateVariations with the same name, the dependency will automatically
-	// be updated to point the matching variant.
-	//
-	// If a module is split, and then a module depending on the first module is not split
-	// when the Mutator is later called on it, the dependency of the depending module will
-	// automatically be updated to point to the first variant.
-	CreateVariations(...string) []Module
-
-	// CreateLocationVariations splits a module into multiple variants, one for each name in the variantNames
-	// parameter.  It returns a list of new modules in the same order as the variantNames
-	// list.
-	//
-	// Local variations do not affect automatic dependency resolution - dependencies added
-	// to the split module via deps or DynamicDependerModule must exactly match a variant
-	// that contains all the non-local variations.
-	CreateLocalVariations(...string) []Module
-
-	// SetDependencyVariation sets all dangling dependencies on the current module to point to the variation
-	// with given name. This function ignores the default variation set by SetDefaultDependencyVariation.
-	SetDependencyVariation(string)
-
-	// SetDefaultDependencyVariation sets the default variation when a dangling reference is detected
-	// during the subsequent calls on Create*Variations* functions. To reset, set it to nil.
-	SetDefaultDependencyVariation(*string)
-
 	// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
 	// argument to select which variant of the dependency to use.  It returns a slice of modules for
 	// each dependency (some entries may be nil).  A variant of the dependency must exist that matches
@@ -272,6 +250,16 @@
 	// be ordered correctly for all future mutator passes.
 	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
 
+	// AddReverseVariationDependencies adds a dependency from the named module to the current
+	// module. The given variations will be added to the current module's varations, and then the
+	// result will be used to find the correct variation of the depending module, which must exist.
+	//
+	// Does not affect the ordering of the current mutator pass, but will be ordered
+	// correctly for all future mutator passes.  All reverse dependencies for a destination module are
+	// collected until the end of the mutator pass, sorted by name, and then appended to the destination
+	// module's dependency list.
+	AddReverseVariationDependency([]blueprint.Variation, blueprint.DependencyTag, string)
+
 	// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
 	// variations argument to select which variant of the dependency to use.  It returns a slice of
 	// modules for each dependency (some entries may be nil).  A variant of the dependency must
@@ -287,12 +275,6 @@
 	// be ordered correctly for all future mutator passes.
 	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
 
-	// AddInterVariantDependency adds a dependency between two variants of the same module.  Variants are always
-	// ordered in the same orderas they were listed in CreateVariations, and AddInterVariantDependency does not change
-	// that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps,
-	// WalkDeps, etc.
-	AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
-
 	// ReplaceDependencies finds all the variants of the module with the specified name, then
 	// replaces all dependencies onto those variants with the current variant of this module.
 	// Replacements don't take effect until after the mutator pass is finished.
@@ -303,30 +285,6 @@
 	// as long as the supplied predicate returns true.
 	// Replacements don't take effect until after the mutator pass is finished.
 	ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
-
-	// AliasVariation takes a variationName that was passed to CreateVariations for this module,
-	// and creates an alias from the current variant (before the mutator has run) to the new
-	// variant.  The alias will be valid until the next time a mutator calls CreateVariations or
-	// CreateLocalVariations on this module without also calling AliasVariation.  The alias can
-	// be used to add dependencies on the newly created variant using the variant map from
-	// before CreateVariations was run.
-	AliasVariation(variationName string)
-
-	// CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this
-	// module, and creates an alias from a new fromVariationName variant the toVariationName
-	// variant.  The alias will be valid until the next time a mutator calls CreateVariations or
-	// CreateLocalVariations on this module without also calling AliasVariation.  The alias can
-	// be used to add dependencies on the toVariationName variant using the fromVariationName
-	// variant.
-	CreateAliasVariation(fromVariationName, toVariationName string)
-
-	// SetVariationProvider sets the value for a provider for the given newly created variant of
-	// the current module, i.e. one of the Modules returned by CreateVariations..  It panics if
-	// not called during the appropriate mutator or GenerateBuildActions pass for the provider,
-	// if the value is not of the appropriate type, or if the module is not a newly created
-	// variant of the current module.  The value should not be modified after being passed to
-	// SetVariationProvider.
-	SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{})
 }
 
 // An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
@@ -516,6 +474,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 +700,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")
@@ -753,48 +722,11 @@
 	b.bp.AddReverseDependency(module, tag, name)
 }
 
-func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module {
-	if b.finalPhase {
-		panic("CreateVariations not allowed in FinalDepsMutators")
+func (b *bottomUpMutatorContext) AddReverseVariationDependency(variations []blueprint.Variation, tag blueprint.DependencyTag, name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
 	}
-
-	modules := b.bp.CreateVariations(variations...)
-
-	aModules := make([]Module, len(modules))
-	for i := range variations {
-		aModules[i] = modules[i].(Module)
-		base := aModules[i].base()
-		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
-		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
-	}
-
-	return aModules
-}
-
-func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module {
-	if b.finalPhase {
-		panic("CreateLocalVariations not allowed in FinalDepsMutators")
-	}
-
-	modules := b.bp.CreateLocalVariations(variations...)
-
-	aModules := make([]Module, len(modules))
-	for i := range variations {
-		aModules[i] = modules[i].(Module)
-		base := aModules[i].base()
-		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
-		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
-	}
-
-	return aModules
-}
-
-func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) {
-	b.bp.SetDependencyVariation(variation)
-}
-
-func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string) {
-	b.bp.SetDefaultDependencyVariation(variation)
+	b.bp.AddReverseVariationDependency(variations, tag, name)
 }
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
@@ -814,10 +746,6 @@
 	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
 
-func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module) {
-	b.bp.AddInterVariantDependency(tag, from, to)
-}
-
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
@@ -831,15 +759,3 @@
 	}
 	b.bp.ReplaceDependenciesIf(name, predicate)
 }
-
-func (b *bottomUpMutatorContext) AliasVariation(variationName string) {
-	b.bp.AliasVariation(variationName)
-}
-
-func (b *bottomUpMutatorContext) CreateAliasVariation(fromVariationName, toVariationName string) {
-	b.bp.CreateAliasVariation(fromVariationName, toVariationName)
-}
-
-func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{}) {
-	b.bp.SetVariationProvider(module, provider, value)
-}
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 21eebd2..5d4074a 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"}
+					},
 				})
 			})
 
@@ -242,28 +287,21 @@
 	AssertDeepEquals(t, "final", finalWant, finalGot)
 }
 
-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")
-		}
-	}
-
+func TestTransitionMutatorInFinalDeps(t *testing.T) {
 	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/neverallow.go b/android/neverallow.go
index b89d150..e135f57 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -58,7 +58,6 @@
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
 	AddNeverAllowRules(createCcStubsRule())
-	AddNeverAllowRules(createJavaExcludeStaticLibsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
 	AddNeverAllowRules(createLimitNdkExportRule()...)
 }
@@ -253,14 +252,6 @@
 	}
 }
 
-func createJavaExcludeStaticLibsRule() Rule {
-	return NeverAllow().
-		NotIn("build/soong", "libcore", "frameworks/base/api").
-		ModuleType("java_library").
-		WithMatcher("exclude_static_libs", isSetMatcherInstance).
-		Because("exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api")
-}
-
 func createProhibitHeaderOnlyRule() Rule {
 	return NeverAllow().
 		Without("name", "framework-minus-apex-headers").
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index b2620ef..192c924 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -344,23 +344,6 @@
 			`module "outside_allowed_list": violates neverallow`,
 		},
 	},
-	// Test for the rule restricting use of exclude_static_libs
-	{
-		name: `"exclude_static_libs" outside allowed directory`,
-		fs: map[string][]byte{
-			"a/b/Android.bp": []byte(`
-				java_library {
-					name: "baz",
-					exclude_static_libs: [
-						"bar",
-					],
-				}
-			`),
-		},
-		expectedErrors: []string{
-			`exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`,
-		},
-	},
 	// Test for only allowing headers_only for framework-minus-apex-headers
 	{
 		name: `"headers_only" outside framework-minus-apex-headers modules`,
diff --git a/android/packaging.go b/android/packaging.go
index 0909936..3c64d56 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"path/filepath"
 	"sort"
@@ -67,34 +64,53 @@
 	owner string
 }
 
-func (p *PackagingSpec) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.relPathInPackage), encoder.Encode(p.srcPath),
-		encoder.Encode(p.symlinkTarget), encoder.Encode(p.executable),
-		encoder.Encode(p.effectiveLicenseFiles), encoder.Encode(p.partition),
-		encoder.Encode(p.skipInstall), encoder.Encode(p.aconfigPaths),
-		encoder.Encode(p.archType))
-	if err != nil {
-		return nil, err
-	}
+type packagingSpecGob struct {
+	RelPathInPackage string
+	SrcPath          Path
+	SymlinkTarget    string
+	Executable       bool
+	Partition        string
+	SkipInstall      bool
+	AconfigPaths     *Paths
+	ArchType         ArchType
+	Overrides        *[]string
+	Owner            string
+}
 
-	return w.Bytes(), nil
+func (p *PackagingSpec) ToGob() *packagingSpecGob {
+	return &packagingSpecGob{
+		RelPathInPackage: p.relPathInPackage,
+		SrcPath:          p.srcPath,
+		SymlinkTarget:    p.symlinkTarget,
+		Executable:       p.executable,
+		Partition:        p.partition,
+		SkipInstall:      p.skipInstall,
+		AconfigPaths:     p.aconfigPaths,
+		ArchType:         p.archType,
+		Overrides:        p.overrides,
+		Owner:            p.owner,
+	}
+}
+
+func (p *PackagingSpec) FromGob(data *packagingSpecGob) {
+	p.relPathInPackage = data.RelPathInPackage
+	p.srcPath = data.SrcPath
+	p.symlinkTarget = data.SymlinkTarget
+	p.executable = data.Executable
+	p.partition = data.Partition
+	p.skipInstall = data.SkipInstall
+	p.aconfigPaths = data.AconfigPaths
+	p.archType = data.ArchType
+	p.overrides = data.Overrides
+	p.owner = data.Owner
+}
+
+func (p *PackagingSpec) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[packagingSpecGob](p)
 }
 
 func (p *PackagingSpec) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.relPathInPackage), decoder.Decode(&p.srcPath),
-		decoder.Decode(&p.symlinkTarget), decoder.Decode(&p.executable),
-		decoder.Decode(&p.effectiveLicenseFiles), decoder.Decode(&p.partition),
-		decoder.Decode(&p.skipInstall), decoder.Decode(&p.aconfigPaths),
-		decoder.Decode(&p.archType))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[packagingSpecGob](data, p)
 }
 
 func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
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 e457959..9c2df65 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -27,7 +24,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -92,8 +88,10 @@
 // the Path methods that rely on module dependencies having been resolved.
 type ModuleWithDepsPathContext interface {
 	EarlyModulePathContext
-	VisitDirectDepsBlueprint(visit func(blueprint.Module))
+	OtherModuleProviderContext
+	VisitDirectDeps(visit func(Module))
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
+	HasMutatorFinished(mutatorName string) bool
 }
 
 // ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by
@@ -341,6 +339,11 @@
 	invalidReason string // Not applicable if path != nil. "" if the reason is unknown.
 }
 
+type optionalPathGob struct {
+	Path          Path
+	InvalidReason string
+}
+
 // OptionalPathForPath returns an OptionalPath containing the path.
 func OptionalPathForPath(path Path) OptionalPath {
 	return OptionalPath{path: path}
@@ -352,6 +355,26 @@
 	return OptionalPath{invalidReason: reason}
 }
 
+func (p *OptionalPath) ToGob() *optionalPathGob {
+	return &optionalPathGob{
+		Path:          p.path,
+		InvalidReason: p.invalidReason,
+	}
+}
+
+func (p *OptionalPath) FromGob(data *optionalPathGob) {
+	p.path = data.Path
+	p.invalidReason = data.InvalidReason
+}
+
+func (p OptionalPath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[optionalPathGob](&p)
+}
+
+func (p *OptionalPath) GobDecode(data []byte) error {
+	return blueprint.CustomGobDecode[optionalPathGob](data, p)
+}
+
 // Valid returns whether there is a valid path
 func (p OptionalPath) Valid() bool {
 	return p.path != nil
@@ -554,13 +577,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.
@@ -572,10 +588,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
@@ -608,7 +620,7 @@
 	// create the tag here as was supplied to create the tag when the dependency was added so that
 	// this finds the matching dependency module.
 	expectedTag := sourceOrOutputDepTag(moduleName, tag)
-	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+	ctx.VisitDirectDeps(func(module Module) {
 		depTag := ctx.OtherModuleDependencyTag(module)
 		if depTag == expectedTag {
 			found = module
@@ -1075,26 +1087,29 @@
 	rel  string
 }
 
-func (p basePath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.path), encoder.Encode(p.rel))
-	if err != nil {
-		return nil, err
-	}
+type basePathGob struct {
+	Path string
+	Rel  string
+}
 
-	return w.Bytes(), nil
+func (p *basePath) ToGob() *basePathGob {
+	return &basePathGob{
+		Path: p.path,
+		Rel:  p.rel,
+	}
+}
+
+func (p *basePath) FromGob(data *basePathGob) {
+	p.path = data.Path
+	p.rel = data.Rel
+}
+
+func (p basePath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[basePathGob](&p)
 }
 
 func (p *basePath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.path), decoder.Decode(&p.rel))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[basePathGob](data, p)
 }
 
 func (p basePath) Ext() string {
@@ -1347,26 +1362,32 @@
 	fullPath string
 }
 
-func (p OutputPath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath))
-	if err != nil {
-		return nil, err
-	}
+type outputPathGob struct {
+	basePath
+	OutDir   string
+	FullPath string
+}
 
-	return w.Bytes(), nil
+func (p *OutputPath) ToGob() *outputPathGob {
+	return &outputPathGob{
+		basePath: p.basePath,
+		OutDir:   p.outDir,
+		FullPath: p.fullPath,
+	}
+}
+
+func (p *OutputPath) FromGob(data *outputPathGob) {
+	p.basePath = data.basePath
+	p.outDir = data.OutDir
+	p.fullPath = data.FullPath
+}
+
+func (p OutputPath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[outputPathGob](&p)
 }
 
 func (p *OutputPath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[outputPathGob](data, p)
 }
 
 func (p OutputPath) withRel(rel string) OutputPath {
@@ -1766,30 +1787,41 @@
 	fullPath string
 }
 
-func (p *InstallPath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir),
-		encoder.Encode(p.partitionDir), encoder.Encode(p.partition),
-		encoder.Encode(p.makePath), encoder.Encode(p.fullPath))
-	if err != nil {
-		return nil, err
-	}
+type installPathGob struct {
+	basePath
+	SoongOutDir  string
+	PartitionDir string
+	Partition    string
+	MakePath     bool
+	FullPath     string
+}
 
-	return w.Bytes(), nil
+func (p *InstallPath) ToGob() *installPathGob {
+	return &installPathGob{
+		basePath:     p.basePath,
+		SoongOutDir:  p.soongOutDir,
+		PartitionDir: p.partitionDir,
+		Partition:    p.partition,
+		MakePath:     p.makePath,
+		FullPath:     p.fullPath,
+	}
+}
+
+func (p *InstallPath) FromGob(data *installPathGob) {
+	p.basePath = data.basePath
+	p.soongOutDir = data.SoongOutDir
+	p.partitionDir = data.PartitionDir
+	p.partition = data.Partition
+	p.makePath = data.MakePath
+	p.fullPath = data.FullPath
+}
+
+func (p InstallPath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[installPathGob](&p)
 }
 
 func (p *InstallPath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir),
-		decoder.Decode(&p.partitionDir), decoder.Decode(&p.partition),
-		decoder.Decode(&p.makePath), decoder.Decode(&p.fullPath))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[installPathGob](data, p)
 }
 
 // Will panic if called from outside a test environment.
diff --git a/android/prebuilt.go b/android/prebuilt.go
index fd5a6ea..4f04d05 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -359,8 +359,8 @@
 //
 // This function is for use on dependencies after PrebuiltPostDepsMutator has
 // run - any dependency that is registered before that will already reference
-// the right module. This function is only safe to call after all mutators that
-// may call CreateVariations, e.g. in GenerateAndroidBuildActions.
+// the right module. This function is only safe to call after all TransitionMutators
+// have run, e.g. in GenerateAndroidBuildActions.
 func PrebuiltGetPreferred(ctx BaseModuleContext, module Module) Module {
 	if !module.IsReplacedByPrebuilt() {
 		return module
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/provider.go b/android/provider.go
index 5ded4cc..81d17a1 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -24,7 +24,7 @@
 // OtherModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext.
 func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
-	value, ok := ctx.otherModuleProvider(module, provider)
+	value, ok := ctx.otherModuleProvider(getWrappedModule(module), provider)
 	if !ok {
 		var k K
 		return k, false
diff --git a/android/register.go b/android/register.go
index eb6a35e..2ce6025 100644
--- a/android/register.go
+++ b/android/register.go
@@ -235,6 +235,7 @@
 
 	PreDepsMutators(f RegisterMutatorFunc)
 	PostDepsMutators(f RegisterMutatorFunc)
+	PostApexMutators(f RegisterMutatorFunc)
 	FinalDepsMutators(f RegisterMutatorFunc)
 }
 
@@ -326,6 +327,10 @@
 	PostDepsMutators(f)
 }
 
+func (ctx *initRegistrationContext) PostApexMutators(f RegisterMutatorFunc) {
+	PostApexMutators(f)
+}
+
 func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
 	FinalDepsMutators(f)
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 18bbcab..56de9cd 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -38,6 +38,9 @@
 const sboxToolsSubDir = "tools"
 const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
 
+const nsjailToolsSubDir = "tools"
+const nsjailOutDir = "out"
+
 // RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
 // graph.
 type RuleBuilder struct {
@@ -59,6 +62,9 @@
 	sboxManifestPath WritablePath
 	missingDeps      []string
 	args             map[string]string
+	nsjail           bool
+	nsjailBasePath   WritablePath
+	nsjailImplicits  Paths
 }
 
 // NewRuleBuilder returns a newly created RuleBuilder.
@@ -165,12 +171,43 @@
 	if len(r.commands) > 0 {
 		panic("Sbox() may not be called after Command()")
 	}
+	if r.nsjail {
+		panic("Sbox() may not be called after Nsjail()")
+	}
 	r.sbox = true
 	r.outDir = outputDir
 	r.sboxManifestPath = manifestPath
 	return r
 }
 
+// Nsjail marks the rule as needing to be wrapped by nsjail. The outputDir should point to the
+// output directory that nsjail will mount to out/. It should not be written to by any other rule.
+// baseDir should point to a location where nsjail will mount to /nsjail_build_sandbox, which will
+// be the working directory of the command.
+func (r *RuleBuilder) Nsjail(outputDir WritablePath, baseDir WritablePath) *RuleBuilder {
+	if len(r.commands) > 0 {
+		panic("Nsjail() may not be called after Command()")
+	}
+	if r.sbox {
+		panic("Nsjail() may not be called after Sbox()")
+	}
+	r.nsjail = true
+	r.outDir = outputDir
+	r.nsjailBasePath = baseDir
+	return r
+}
+
+// NsjailImplicits adds implicit inputs that are not directly mounted. This is useful when
+// the rule mounts directories, as files within those directories can be globbed and
+// tracked as dependencies with NsjailImplicits().
+func (r *RuleBuilder) NsjailImplicits(inputs Paths) *RuleBuilder {
+	if !r.nsjail {
+		panic("NsjailImplicits() must be called after Nsjail()")
+	}
+	r.nsjailImplicits = append(r.nsjailImplicits, inputs...)
+	return r
+}
+
 // SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
 // sandbox.
 func (r *RuleBuilder) SandboxTools() *RuleBuilder {
@@ -514,7 +551,73 @@
 
 	commandString := strings.Join(commands, " && ")
 
-	if r.sbox {
+	if !r.sbox {
+		// If not using sbox the rule will run the command directly, put the hash of the
+		// list of input files in a comment at the end of the command line to ensure ninja
+		// reruns the rule when the list of input files changes.
+		commandString += " # hash of input list: " + hashSrcFiles(inputs)
+	}
+
+	if r.nsjail {
+		var nsjailCmd strings.Builder
+		nsjailPath := r.ctx.Config().PrebuiltBuildTool(r.ctx, "nsjail")
+		nsjailCmd.WriteString("mkdir -p ")
+		nsjailCmd.WriteString(r.nsjailBasePath.String())
+		nsjailCmd.WriteString(" && ")
+		nsjailCmd.WriteString(nsjailPath.String())
+		nsjailCmd.WriteRune(' ')
+		nsjailCmd.WriteString("-B $PWD/")
+		nsjailCmd.WriteString(r.nsjailBasePath.String())
+		nsjailCmd.WriteString(":nsjail_build_sandbox")
+
+		// out is mounted to $(genDir).
+		nsjailCmd.WriteString(" -B $PWD/")
+		nsjailCmd.WriteString(r.outDir.String())
+		nsjailCmd.WriteString(":nsjail_build_sandbox/out")
+
+		for _, input := range inputs {
+			nsjailCmd.WriteString(" -R $PWD/")
+			nsjailCmd.WriteString(input.String())
+			nsjailCmd.WriteString(":nsjail_build_sandbox/")
+			nsjailCmd.WriteString(r.nsjailPathForInputRel(input))
+		}
+		for _, tool := range tools {
+			nsjailCmd.WriteString(" -R $PWD/")
+			nsjailCmd.WriteString(tool.String())
+			nsjailCmd.WriteString(":nsjail_build_sandbox/")
+			nsjailCmd.WriteString(nsjailPathForToolRel(r.ctx, tool))
+		}
+		inputs = append(inputs, tools...)
+		for _, c := range r.commands {
+			for _, tool := range c.packagedTools {
+				nsjailCmd.WriteString(" -R $PWD/")
+				nsjailCmd.WriteString(tool.srcPath.String())
+				nsjailCmd.WriteString(":nsjail_build_sandbox/")
+				nsjailCmd.WriteString(nsjailPathForPackagedToolRel(tool))
+				inputs = append(inputs, tool.srcPath)
+			}
+		}
+
+		// These five directories are necessary to run native host tools like /bin/bash and py3-cmd.
+		nsjailCmd.WriteString(" -R /bin")
+		nsjailCmd.WriteString(" -R /lib")
+		nsjailCmd.WriteString(" -R /lib64")
+		nsjailCmd.WriteString(" -R /dev")
+		nsjailCmd.WriteString(" -R /usr")
+
+		nsjailCmd.WriteString(" -m none:/tmp:tmpfs:size=1073741824") // 1GB, should be enough
+		nsjailCmd.WriteString(" -D nsjail_build_sandbox")
+		nsjailCmd.WriteString(" --disable_rlimits")
+		nsjailCmd.WriteString(" -q")
+		nsjailCmd.WriteString(" -- ")
+		nsjailCmd.WriteString("/bin/bash -c ")
+		nsjailCmd.WriteString(proptools.ShellEscape(commandString))
+
+		commandString = nsjailCmd.String()
+
+		inputs = append(inputs, nsjailPath)
+		inputs = append(inputs, r.nsjailImplicits...)
+	} else if r.sbox {
 		// If running the command inside sbox, write the rule data out to an sbox
 		// manifest.textproto.
 		manifest := sbox_proto.Manifest{}
@@ -734,11 +837,6 @@
 			rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
 			commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
 		}
-	} else {
-		// If not using sbox the rule will run the command directly, put the hash of the
-		// list of input files in a comment at the end of the command line to ensure ninja
-		// reruns the rule when the list of input files changes.
-		commandString += " # hash of input list: " + hashSrcFiles(inputs)
 	}
 
 	// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
@@ -869,6 +967,8 @@
 			rel = filepath.Join(sboxSandboxBaseDir, rel)
 		}
 		return rel
+	} else if c.rule.nsjail {
+		return c.rule.nsjailPathForInputRel(path)
 	}
 	return path.String()
 }
@@ -894,6 +994,10 @@
 		// Errors will be handled in RuleBuilder.Build where we have a context to report them
 		rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
 		return filepath.Join(sboxOutDir, rel)
+	} else if c.rule.nsjail {
+		// Errors will be handled in RuleBuilder.Build where we have a context to report them
+		rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
+		return filepath.Join(nsjailOutDir, rel)
 	}
 	return path.String()
 }
@@ -945,15 +1049,49 @@
 	return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
 }
 
+func nsjailPathForToolRel(ctx BuilderContext, path Path) string {
+	// Errors will be handled in RuleBuilder.Build where we have a context to report them
+	toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
+	relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
+	if isRelOutSoong {
+		// The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+		return filepath.Join(nsjailToolsSubDir, "out", relOutSoong)
+	}
+	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
+	return filepath.Join(nsjailToolsSubDir, "src", path.String())
+}
+
+func (r *RuleBuilder) nsjailPathForInputRel(path Path) string {
+	rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
+	if isRelSboxOut {
+		return filepath.Join(nsjailOutDir, rel)
+	}
+	return path.String()
+}
+
+func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string {
+	ret := make([]string, len(paths))
+	for i, path := range paths {
+		ret[i] = r.nsjailPathForInputRel(path)
+	}
+	return ret
+}
+
+func nsjailPathForPackagedToolRel(spec PackagingSpec) string {
+	return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage)
+}
+
 // PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
 // tool after copying it into the sandbox.  This can be used  on the RuleBuilder command line to
 // reference the tool.
 func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
-	if !c.rule.sboxTools {
-		panic("PathForPackagedTool() requires SandboxTools()")
+	if c.rule.sboxTools {
+		return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+	} else if c.rule.nsjail {
+		return nsjailPathForPackagedToolRel(spec)
+	} else {
+		panic("PathForPackagedTool() requires SandboxTools() or Nsjail()")
 	}
-
-	return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
 }
 
 // PathForTool takes a path to a tool, which may be an output file or a source file, and returns
@@ -962,6 +1100,8 @@
 func (c *RuleBuilderCommand) PathForTool(path Path) string {
 	if c.rule.sbox && c.rule.sboxTools {
 		return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
+	} else if c.rule.nsjail {
+		return nsjailPathForToolRel(c.rule.ctx, path)
 	}
 	return path.String()
 }
@@ -976,6 +1116,12 @@
 			ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
 		}
 		return ret
+	} else if c.rule.nsjail {
+		var ret []string
+		for _, path := range paths {
+			ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path))
+		}
+		return ret
 	}
 	return paths.Strings()
 }
@@ -983,20 +1129,22 @@
 // PackagedTool adds the specified tool path to the command line.  It can only be used with tool
 // sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
 func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
-	if !c.rule.sboxTools {
-		panic("PackagedTool() requires SandboxTools()")
-	}
-
 	c.packagedTools = append(c.packagedTools, spec)
-	c.Text(sboxPathForPackagedToolRel(spec))
+	if c.rule.sboxTools {
+		c.Text(sboxPathForPackagedToolRel(spec))
+	} else if c.rule.nsjail {
+		c.Text(nsjailPathForPackagedToolRel(spec))
+	} else {
+		panic("PackagedTool() requires SandboxTools() or Nsjail()")
+	}
 	return c
 }
 
 // ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
 // line.  It can only be used with tool sandboxing enabled by SandboxTools().
 func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
-	if !c.rule.sboxTools {
-		panic("ImplicitPackagedTool() requires SandboxTools()")
+	if !c.rule.sboxTools && !c.rule.nsjail {
+		panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()")
 	}
 
 	c.packagedTools = append(c.packagedTools, spec)
@@ -1006,8 +1154,8 @@
 // ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
 // line.  It can only be used with tool sandboxing enabled by SandboxTools().
 func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
-	if !c.rule.sboxTools {
-		panic("ImplicitPackagedTools() requires SandboxTools()")
+	if !c.rule.sboxTools && !c.rule.nsjail {
+		panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()")
 	}
 
 	c.packagedTools = append(c.packagedTools, specs...)
diff --git a/android/sdk.go b/android/sdk.go
index d3f04a4..ab9a91c 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -813,8 +813,6 @@
 
 // SdkMemberContext provides access to information common to a specific member.
 type SdkMemberContext interface {
-	ConfigAndErrorContext
-
 	// SdkModuleContext returns the module context of the sdk common os variant which is creating the
 	// snapshot.
 	//
diff --git a/android/singleton.go b/android/singleton.go
index 8542bd9..913bf6a 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -90,6 +90,10 @@
 
 	// OtherModulePropertyErrorf reports an error on the line number of the given property of the given module
 	OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{})
+
+	// HasMutatorFinished returns true if the given mutator has finished running.
+	// It will panic if given an invalid mutator name.
+	HasMutatorFinished(mutatorName string) bool
 }
 
 type singletonAdaptor struct {
@@ -286,3 +290,7 @@
 func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) {
 	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...)
 }
+
+func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool {
+	return s.blueprintSingletonContext().HasMutatorFinished(mutatorName)
+}
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 816707d..7440869 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -197,8 +197,8 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
-	NameResolver                          *NameResolver
+	preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc
+	NameResolver                                    *NameResolver
 
 	// The list of singletons registered for the test.
 	singletons sortableComponents
@@ -229,6 +229,10 @@
 	ctx.postDeps = append(ctx.postDeps, f)
 }
 
+func (ctx *TestContext) PostApexMutators(f RegisterMutatorFunc) {
+	ctx.postApex = append(ctx.postApex, f)
+}
+
 func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
@@ -449,7 +453,7 @@
 func (ctx *TestContext) Register() {
 	globalOrder := globallyRegisteredComponentsOrder()
 
-	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.postApex, ctx.finalDeps)
 	// Ensure that the mutators used in the test are in the same order as they are used at runtime.
 	globalOrder.mutatorOrder.enforceOrdering(mutators)
 	mutators.registerAll(ctx.Context)
@@ -1326,7 +1330,15 @@
 	return ctx.ctx.Config()
 }
 
-func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext {
+func (ctx *panickingConfigAndErrorContext) HasMutatorFinished(mutatorName string) bool {
+	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/updatable_modules.go b/android/updatable_modules.go
deleted file mode 100644
index d2595ed..0000000
--- a/android/updatable_modules.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-// This file contains branch specific constants. They are stored in a separate
-// file to minimise the potential of merge conflicts between branches when
-// the code from the package is changed.
-
-// The default manifest version for all the modules on this branch.
-// This version code will be used only if there is no version field in the
-// module's apex_manifest.json. Release branches have their version injected
-// into apex_manifest.json by the tooling and will not use the version set
-// here. Developers can also set the version field locally in the
-// apex_manifest.json to build a module with a specific version.
-//
-// The value follows the schema from go/mainline-version-codes, and is chosen
-// based on the branch such that the builds from testing and development
-// branches will have a version higher than the prebuilts.
-// Versions per branch:
-// * x-dev           - xx0090000 (where xx is the branch SDK level)
-// * AOSP            - xx9990000
-// * x-mainline-prod - xx9990000
-// * master          - 990090000
-const DefaultUpdatableModuleVersion = "352090000"
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/variable.go b/android/variable.go
index b238c4a..417ba89 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -423,10 +423,10 @@
 
 	TargetFSConfigGen []string `json:",omitempty"`
 
-	EnforceProductPartitionInterface *bool `json:",omitempty"`
+	UseSoongSystemImage            *bool   `json:",omitempty"`
+	ProductSoongDefinedSystemImage *string `json:",omitempty"`
 
-	EnforceInterPartitionJavaSdkLibrary *bool    `json:",omitempty"`
-	InterPartitionJavaLibraryAllowList  []string `json:",omitempty"`
+	EnforceProductPartitionInterface *bool `json:",omitempty"`
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
 
@@ -520,6 +520,12 @@
 	BoardAvbSystemAddHashtreeFooterArgs    []string `json:",omitempty"`
 	DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"`
 	DeviceProductCompatibilityMatrixFile   []string `json:",omitempty"`
+
+	PartitionVarsForSoongMigrationOnlyDoNotUse PartitionVariables
+
+	ExtraAllowedDepsTxt *string `json:",omitempty"`
+
+	AdbKeys *string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
@@ -576,7 +582,8 @@
 
 	BoardAvbEnable bool `json:",omitempty"`
 
-	ProductPackages []string `json:",omitempty"`
+	ProductPackages      []string `json:",omitempty"`
+	ProductPackagesDebug []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
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 17fdfc3..4848513 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,7 +15,6 @@
         "soong-cc",
         "soong-filesystem",
         "soong-java",
-        "soong-multitree",
         "soong-provenance",
         "soong-python",
         "soong-rust",
@@ -43,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 1f4a99b..d3e7eee 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,10 +29,10 @@
 	"android/soong/android"
 	"android/soong/bpf"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
-	"android/soong/multitree"
 	"android/soong/rust"
 	"android/soong/sh"
 )
@@ -50,17 +50,11 @@
 	ctx.RegisterModuleType("override_apex", OverrideApexFactory)
 	ctx.RegisterModuleType("apex_set", apexSetFactory)
 
-	ctx.PreArchMutators(registerPreArchMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
 }
 
-func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("prebuilt_apex_module_creator", prebuiltApexModuleCreatorMutator).Parallel()
-}
-
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
 	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
 }
 
@@ -75,8 +69,6 @@
 	ctx.Transition("apex", &apexTransitionMutator{})
 	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
 	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
-	// Register after apex_info mutator so that it can use ApexVariationName
-	ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
 }
 
 type apexBundleProperties struct {
@@ -216,7 +208,7 @@
 	Native_shared_libs proptools.Configurable[[]string]
 
 	// List of JNI libraries that are embedded inside this APEX.
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 
 	// List of rust dyn libraries that are embedded inside this APEX.
 	Rust_dyn_libs []string
@@ -302,7 +294,7 @@
 // Merge combines another ApexNativeDependencies into this one
 func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
 	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs.GetOrDefault(ctx, nil)...)
-	a.Jni_libs = append(a.Jni_libs, b.Jni_libs...)
+	a.Jni_libs = append(a.Jni_libs, b.Jni_libs.GetOrDefault(ctx, nil)...)
 	a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
 	a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...)
 	a.Tests = append(a.Tests, b.Tests...)
@@ -404,7 +396,7 @@
 
 	// Apex Container package name. Override value for attribute package:name in
 	// AndroidManifest.xml
-	Package_name string
+	Package_name proptools.Configurable[string]
 
 	// A txt file containing list of files that are allowed to be included in this APEX.
 	Allowed_files *string `android:"path"`
@@ -438,7 +430,6 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.OverridableModuleBase
-	multitree.ExportableModuleBase
 
 	// Properties
 	properties            apexBundleProperties
@@ -591,7 +582,7 @@
 	dataPaths                 []android.DataPath // becomes LOCAL_TEST_DATA
 
 	jacocoReportClassesFile android.Path     // only for javalibs and apps
-	lintDepSets             java.LintDepSets // only for javalibs and apps
+	lintInfo                *java.LintInfo   // only for javalibs and apps
 	certificate             java.Certificate // only for apps
 	overriddenPackageName   string           // only for apps
 
@@ -854,7 +845,7 @@
 			deps.Merge(ctx, ApexNativeDependencies{
 				Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil),
 				Tests:              nil,
-				Jni_libs:           nil,
+				Jni_libs:           proptools.NewConfigurable[[]string](nil, nil),
 				Binaries:           a.properties.Binaries,
 			})
 		}
@@ -1120,51 +1111,6 @@
 	if am, ok := mctx.Module().(android.ApexModule); ok {
 		android.ApexInfoMutator(mctx, am)
 	}
-	enforceAppUpdatability(mctx)
-}
-
-// apexStrictUpdatibilityLintMutator propagates strict_updatability_linting to transitive deps of a mainline module
-// This check is enforced for updatable modules
-func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting(mctx) {
-		mctx.WalkDeps(func(child, parent android.Module) bool {
-			// b/208656169 Do not propagate strict updatability linting to libcore/
-			// These libs are available on the classpath during compilation
-			// These libs are transitive deps of the sdk. See java/sdk.go:decodeSdkDep
-			// Only skip libraries defined in libcore root, not subdirectories
-			if mctx.OtherModuleDir(child) == "libcore" {
-				// Do not traverse transitive deps of libcore/ libs
-				return false
-			}
-			if android.InList(child.Name(), skipLintJavalibAllowlist) {
-				return false
-			}
-			if lintable, ok := child.(java.LintDepSetsIntf); ok {
-				lintable.SetStrictUpdatabilityLinting(true)
-			}
-			// visit transitive deps
-			return true
-		})
-	}
-}
-
-// enforceAppUpdatability propagates updatable=true to apps of updatable apexes
-func enforceAppUpdatability(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	if apex, ok := mctx.Module().(*apexBundle); ok && apex.Updatable() {
-		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
-		mctx.VisitDirectDeps(func(module android.Module) {
-			// ignore android_test_app
-			if app, ok := module.(*java.AndroidApp); ok {
-				app.SetUpdatable(true)
-			}
-		})
-	}
 }
 
 // TODO: b/215736885 Whittle the denylist
@@ -1211,20 +1157,9 @@
 		"test_jitzygote_com.android.art",
 		// go/keep-sorted end
 	}
-
-	// TODO: b/215736885 Remove this list
-	skipLintJavalibAllowlist = []string{
-		"conscrypt.module.platform.api.stubs",
-		"conscrypt.module.public.api.stubs",
-		"conscrypt.module.public.api.stubs.system",
-		"conscrypt.module.public.api.stubs.module_lib",
-		"framework-media.stubs",
-		"framework-media.stubs.system",
-		"framework-media.stubs.module_lib",
-	}
 )
 
-func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.TopDownMutatorContext) bool {
+func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.ModuleContext) bool {
 	// The allowlist contains the base apex name, so use that instead of the ApexVariationName
 	return a.Updatable() && !android.InList(mctx.ModuleName(), skipStrictUpdatabilityLintAllowlist)
 }
@@ -1406,8 +1341,6 @@
 	return true
 }
 
-var _ multitree.Exportable = (*apexBundle)(nil)
-
 func (a *apexBundle) Exportable() bool {
 	return true
 }
@@ -1672,7 +1605,6 @@
 	BaseModuleName() string
 	DexJarBuildPath(ctx android.ModuleErrorfContext) java.OptionalDexJarPath
 	JacocoReportClassesFile() android.Path
-	LintDepSets() java.LintDepSets
 	Stem() string
 }
 
@@ -1692,7 +1624,9 @@
 	dirInApex := "javalib"
 	af := newApexFile(ctx, dexImplementationJar, module.BaseModuleName(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
-	af.lintDepSets = module.LintDepSets()
+	if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok {
+		af.lintInfo = lintInfo
+	}
 	af.customStem = module.Stem() + ".jar"
 	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
 	// on the implementation library
@@ -1730,7 +1664,6 @@
 	JacocoReportClassesFile() android.Path
 	Certificate() java.Certificate
 	BaseModuleName() string
-	LintDepSets() java.LintDepSets
 	PrivAppAllowlist() android.OptionalPath
 }
 
@@ -1766,7 +1699,9 @@
 
 	af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
 	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
-	af.lintDepSets = aapp.LintDepSets()
+	if lintInfo, ok := android.OtherModuleProvider(ctx, aapp, java.LintProvider); ok {
+		af.lintInfo = lintInfo
+	}
 	af.certificate = aapp.Certificate()
 
 	if app, ok := aapp.(interface {
@@ -1817,7 +1752,7 @@
 // visited module, the `do` callback is executed. Returning true in the callback continues the visit
 // to the child modules. Returning false makes the visit to continue in the sibling or the parent
 // modules. This is used in check* functions below.
-func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
+func (a *apexBundle) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) {
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		am, ok := child.(android.ApexModule)
 		if !ok || !am.CanHaveApexVariants() {
@@ -1921,6 +1856,7 @@
 	a.CheckMinSdkVersion(ctx)
 	a.checkStaticLinkingToStubLibraries(ctx)
 	a.checkStaticExecutables(ctx)
+	a.enforceAppUpdatability(ctx)
 	if len(a.properties.Tests) > 0 && !a.testApex {
 		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
 		return false
@@ -1984,12 +1920,38 @@
 	})
 }
 
-func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent blueprint.Module) bool {
+// enforcePartitionTagOnApexSystemServerJar checks that the partition tags of an apex system server jar  matches
+// the partition tags of the top-level apex.
+// e.g. if the top-level apex sets system_ext_specific to true, the javalib must set this property to true as well.
+// This check ensures that the dexpreopt artifacts of the apex system server jar is installed in the same partition
+// as the apex.
+func (a *apexBundle) enforcePartitionTagOnApexSystemServerJar(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	ctx.VisitDirectDepsWithTag(sscpfTag, func(child android.Module) {
+		info, ok := android.OtherModuleProvider(ctx, child, java.LibraryNameToPartitionInfoProvider)
+		if !ok {
+			ctx.ModuleErrorf("Could not find partition info of apex system server jars.")
+		}
+		apexPartition := ctx.Module().PartitionTag(ctx.DeviceConfig())
+		for javalib, javalibPartition := range info.LibraryNameToPartition {
+			if !global.AllApexSystemServerJars(ctx).ContainsJar(javalib) {
+				continue // not an apex system server jar
+			}
+			if apexPartition != javalibPartition {
+				ctx.ModuleErrorf(`
+%s is an apex systemserver jar, but its partition does not match the partition of its containing apex. Expected %s, Got %s`,
+					javalib, apexPartition, javalibPartition)
+			}
+		}
+	})
+}
+
+func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent android.Module) bool {
 	depTag := ctx.OtherModuleDependencyTag(child)
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 		return false
 	}
-	if mod, ok := child.(android.Module); ok && !mod.Enabled(ctx) {
+	if !child.Enabled(ctx) {
 		return false
 	}
 	depName := ctx.OtherModuleName(child)
@@ -2333,7 +2295,7 @@
 		checkDuplicate:         a.shouldCheckDuplicate(ctx),
 		unwantedTransitiveDeps: a.properties.Unwanted_transitive_deps,
 	}
-	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
+	ctx.WalkDeps(func(child, parent android.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
 	vctx.normalizeFileInfo(ctx)
 	if a.privateKeyFile == nil {
 		if ctx.Config().AllowMissingDependencies() {
@@ -2406,6 +2368,7 @@
 	a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
 
 	a.setOutputFiles(ctx)
+	a.enforcePartitionTagOnApexSystemServerJar(ctx)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2446,6 +2409,24 @@
 	}
 }
 
+// enforceAppUpdatability propagates updatable=true to apps of updatable apexes
+func (a *apexBundle) enforceAppUpdatability(mctx android.ModuleContext) {
+	if !a.Enabled(mctx) {
+		return
+	}
+	if a.Updatable() {
+		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
+		mctx.VisitDirectDeps(func(module android.Module) {
+			if appInfo, ok := android.OtherModuleProvider(mctx, module, java.AppInfoProvider); ok {
+				// ignore android_test_app
+				if !appInfo.TestHelperApp && !appInfo.Updatable {
+					mctx.ModuleErrorf("app dependency %s must have updatable: true", mctx.OtherModuleName(module))
+				}
+			}
+		})
+	}
+}
+
 // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that
 // the bootclasspath_fragment contributes to the apex.
 func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.Module) []apexFile {
@@ -2540,7 +2521,6 @@
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitOverridableModule(module, &module.overridableProperties.Overrides)
-	multitree.InitExportableModule(module)
 	return module
 }
 
@@ -2664,7 +2644,7 @@
 
 	abInfo, _ := android.ModuleProvider(ctx, android.ApexBundleInfoProvider)
 
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if ccm, ok := to.(*cc.Module); ok {
 			apexName := ctx.ModuleName()
 			fromName := ctx.OtherModuleName(from)
@@ -2735,7 +2715,7 @@
 func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
 	// Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
 	// java's checkLinkType guarantees correct usage for transitive deps
-	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		switch tag {
 		case javaLibTag, androidAppTag:
@@ -2763,6 +2743,12 @@
 		return
 	}
 
+	// Temporarily bypass /product APEXes with a specific prefix.
+	// TODO: b/352818241 - Remove this after APEX availability is enforced for /product APEXes.
+	if a.ProductSpecific() && strings.HasPrefix(a.ApexVariationName(), "com.sdv.") {
+		return
+	}
+
 	// Coverage build adds additional dependencies for the coverage-only runtime libraries.
 	// Requiring them and their transitive depencies with apex_available is not right
 	// because they just add noise.
@@ -2770,7 +2756,7 @@
 		return
 	}
 
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		// As soon as the dependency graph crosses the APEX boundary, don't go further.
 		if externalDep {
 			return false
@@ -2797,7 +2783,7 @@
 			return false
 		}
 
-		if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
+		if to.AvailableFor(apexName) {
 			return true
 		}
 
@@ -2822,7 +2808,7 @@
 
 // checkStaticExecutable ensures that executables in an APEX are not static.
 func (a *apexBundle) checkStaticExecutables(ctx android.ModuleContext) {
-	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+	ctx.VisitDirectDeps(func(module android.Module) {
 		if ctx.OtherModuleDependencyTag(module) != executableTag {
 			return
 		}
@@ -2857,74 +2843,6 @@
 	dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...)
 }
 
-var (
-	apexAvailBaseline        = makeApexAvailableBaseline()
-	inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
-)
-
-func baselineApexAvailable(apex, moduleName string) bool {
-	key := apex
-	moduleName = normalizeModuleName(moduleName)
-
-	if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
-		return true
-	}
-
-	key = android.AvailableToAnyApex
-	if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
-		return true
-	}
-
-	return false
-}
-
-func normalizeModuleName(moduleName string) string {
-	// Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build
-	// system. Trim the prefix for the check since they are confusing
-	moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
-	if strings.HasPrefix(moduleName, "libclang_rt.") {
-		// This module has many arch variants that depend on the product being built.
-		// We don't want to list them all
-		moduleName = "libclang_rt"
-	}
-	if strings.HasPrefix(moduleName, "androidx.") {
-		// TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
-		moduleName = "androidx"
-	}
-	return moduleName
-}
-
-// Transform the map of apex -> modules to module -> apexes.
-func invertApexBaseline(m map[string][]string) map[string][]string {
-	r := make(map[string][]string)
-	for apex, modules := range m {
-		for _, module := range modules {
-			r[module] = append(r[module], apex)
-		}
-	}
-	return r
-}
-
-// Retrieve the baseline of apexes to which the supplied module belongs.
-func BaselineApexAvailable(moduleName string) []string {
-	return inverseApexAvailBaseline[normalizeModuleName(moduleName)]
-}
-
-// This is a map from apex to modules, which overrides the apex_available setting for that
-// particular module to make it available for the apex regardless of its setting.
-// TODO(b/147364041): remove this
-func makeApexAvailableBaseline() map[string][]string {
-	// The "Module separator"s below are employed to minimize merge conflicts.
-	m := make(map[string][]string)
-	//
-	// Module separator
-	//
-	m["com.android.runtime"] = []string{
-		"libz",
-	}
-	return m
-}
-
 func init() {
 	android.AddNeverAllowRules(createBcpPermittedPackagesRules(qBcpPackages())...)
 	android.AddNeverAllowRules(createBcpPermittedPackagesRules(rBcpPackages())...)
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index f405cb2..00dd446 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -18,6 +18,7 @@
 
 import (
 	"encoding/json"
+	"strings"
 
 	"github.com/google/blueprint"
 
@@ -58,9 +59,9 @@
 
 	// Diff two given lists while ignoring comments in the allowed deps file.
 	diffAllowedApexDepsInfoRule = pctx.AndroidStaticRule("diffAllowedApexDepsInfoRule", blueprint.RuleParams{
-		Description: "Diff ${allowed_deps} and ${new_allowed_deps}",
+		Description: "Diff ${allowed_deps_list} and ${new_allowed_deps}",
 		Command: `
-			if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then
+			if grep -v -h '^#' ${allowed_deps_list} | sort -u -f| diff -B -u - ${new_allowed_deps}; then
 			   touch ${out};
 			else
 				echo -e "\n******************************";
@@ -81,10 +82,15 @@
 				exit 1;
 			fi;
 		`,
-	}, "allowed_deps", "new_allowed_deps")
+	}, "allowed_deps_list", "new_allowed_deps")
 )
 
 func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	allowedDepsSources := []android.OptionalPath{android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")}
+	extraAllowedDepsPath := ctx.Config().ExtraAllowedDepsTxt()
+	if extraAllowedDepsPath != "" {
+		allowedDepsSources = append(allowedDepsSources, android.ExistentPathForSource(ctx, extraAllowedDepsPath))
+	}
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
@@ -96,37 +102,42 @@
 			}
 		}
 	})
-
-	allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")
 	newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
 	s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
-
-	if !allowedDepsSource.Valid() {
+	hasOneValidDepsPath := false
+	for _, allowedDepsSource := range allowedDepsSources {
+		if allowedDepsSource.Valid() {
+			hasOneValidDepsPath = true
+			updatableFlatLists = append(updatableFlatLists, allowedDepsSource.Path())
+		}
+	}
+	allowedDepsStrList := make([]string, len(allowedDepsSources))
+	for _, value := range allowedDepsSources {
+		allowedDepsStrList = append(allowedDepsStrList, value.String())
+	}
+	allowedDepsListString := strings.Join(allowedDepsStrList, " ")
+	if !hasOneValidDepsPath {
 		// Unbundled projects may not have packages/modules/common/ checked out; ignore those.
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Touch,
 			Output: s.allowedApexDepsInfoCheckResult,
 		})
 	} else {
-		allowedDeps := allowedDepsSource.Path()
-
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   generateApexDepsInfoFilesRule,
-			Inputs: append(updatableFlatLists, allowedDeps),
+			Inputs: updatableFlatLists,
 			Output: newAllowedDeps,
 		})
-
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   diffAllowedApexDepsInfoRule,
 			Input:  newAllowedDeps,
 			Output: s.allowedApexDepsInfoCheckResult,
 			Args: map[string]string{
-				"allowed_deps":     allowedDeps.String(),
-				"new_allowed_deps": newAllowedDeps.String(),
+				"allowed_deps_list": allowedDepsListString,
+				"new_allowed_deps":  newAllowedDeps.String(),
 			},
 		})
 	}
-
 	ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 685cb37..1d2f3fb 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2185,6 +2185,151 @@
 		flatlist, "yourlib(minSdkVersion:29)")
 }
 
+func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/custom_allowed_deps.txt": nil,
+	}),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
+			},
+		))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
+func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/custom_allowed_deps.txt": nil,
+		"packages/modules/common/build/allowed_deps.txt":        nil,
+	}),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
+			},
+		))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
 func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -3685,7 +3830,7 @@
 }
 
 func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
-	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex")
+	deapexer := ctx.ModuleForTests(moduleName, variant).Description("deapex")
 	outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
 	if deapexer.Output != nil {
 		outputs = append(outputs, deapexer.Output.String())
@@ -4885,236 +5030,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"),
@@ -5137,23 +5052,6 @@
 		}),
 	)
 
-	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
-		t.Helper()
-		s := ctx.ModuleForTests("dex_bootjars", "android_common")
-		foundLibfooJar := false
-		base := stem + ".jar"
-		for _, output := range s.AllOutputs() {
-			if filepath.Base(output) == base {
-				foundLibfooJar = true
-				buildRule := s.Output(output)
-				android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
-			}
-		}
-		if !foundLibfooJar {
-			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs()))
-		}
-	}
-
 	checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
 		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
@@ -5206,12 +5104,14 @@
 			},
 		}
 
-		java_import {
+		java_sdk_library_import {
 			name: "libfoo",
-			jars: ["libfoo.jar"],
+			public: {
+				jars: ["libfoo.jar"],
+			},
 			apex_available: ["myapex"],
+			shared_library: false,
 			permitted_packages: ["foo"],
-			sdk_version: "core_current",
 		}
 
 		java_sdk_library_import {
@@ -5226,8 +5126,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5243,18 +5141,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"],
@@ -5275,13 +5165,17 @@
 			apex_available: ["myapex"],
 		}
 
-		java_import {
+		java_sdk_library_import {
 			name: "libfoo",
-			jars: ["libfoo.jar"],
+			public: {
+				jars: ["libfoo.jar"],
+			},
 			apex_available: ["myapex"],
-			permitted_packages: ["foo"],
+			shared_library: false,
+			permitted_packages: ["libfoo"],
 		}
 
+
 		java_sdk_library_import {
 			name: "libbar",
 			public: {
@@ -5304,8 +5198,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5459,13 +5351,15 @@
 			},
 		}
 
-		java_import {
+		java_sdk_library_import {
 			name: "libfoo",
 			prefer: true,
-			jars: ["libfoo.jar"],
+			public: {
+				jars: ["libfoo.jar"],
+			},
 			apex_available: ["myapex"],
-			permitted_packages: ["foo"],
-			sdk_version: "core_current",
+			shared_library: false,
+			permitted_packages: ["libfoo"],
 		}
 
 		java_library {
@@ -5497,8 +5391,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5597,8 +5489,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5684,11 +5574,11 @@
 			apex_available: ["myapex"],
 			shared_library: false,
 			permitted_packages: ["bar"],
+			prefer: true,
 		}
 
 		java_sdk_library {
 			name: "libbar",
-			enabled: false,
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
@@ -5705,13 +5595,11 @@
 			android.PrepareForTestWithAllowMissingDependencies,
 			android.FixtureMergeMockFs(map[string][]byte{
 				"build/soong/scripts/check_boot_jars/package_allowed_list.txt": nil,
-				"frameworks/base/config/boot-profile.txt":                      nil,
+				"frameworks/base/boot/boot-profile.txt":                        nil,
 			}),
 		)
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer2, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -6270,6 +6158,87 @@
 		system_shared_libs: [],
 		apex_available: ["otherapex"],
 	}`)
+
+	// 'apex_available' check is bypassed for /product apex with a specific prefix.
+	// TODO: b/352818241 - Remove below two cases after APEX availability is enforced for /product APEXes.
+	testApex(t, `
+	apex {
+		name: "com.sdv.myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+		product_specific: true,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "com.any.otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["com.any.otherapex"],
+		product_specific: true,
+	}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts":    nil,
+			"system/sepolicy/apex/com.any.otherapex-file_contexts": nil,
+		}))
+
+	// 'apex_available' check is not bypassed for non-product apex with a specific prefix.
+	testApexError(t, "requires \"libfoo\" that doesn't list the APEX under 'apex_available'.", `
+	apex {
+		name: "com.sdv.myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "com.any.otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["com.any.otherapex"],
+	}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts":    nil,
+			"system/sepolicy/apex/com.any.otherapex-file_contexts": nil,
+		}))
 }
 
 func TestApexAvailable_IndirectDep(t *testing.T) {
@@ -6315,6 +6284,91 @@
 		stl: "none",
 		system_shared_libs: [],
 	}`)
+
+	// 'apex_available' check is bypassed for /product apex with a specific prefix.
+	// TODO: b/352818241 - Remove below two cases after APEX availability is enforced for /product APEXes.
+	testApex(t, `
+		apex {
+			name: "com.sdv.myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+			product_specific: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+			product_specific: true,
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			shared_libs: ["libbaz"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+			product_specific: true,
+		}
+
+		cc_library {
+			name: "libbaz",
+			stl: "none",
+			system_shared_libs: [],
+			product_specific: true,
+		}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts": nil,
+		}))
+
+	// 'apex_available' check is not bypassed for non-product apex with a specific prefix.
+	testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.`, `
+		apex {
+			name: "com.sdv.myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			shared_libs: ["libbaz"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+		}
+
+		cc_library {
+			name: "libbaz",
+			stl: "none",
+			system_shared_libs: [],
+		}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts": nil,
+		}))
 }
 
 func TestApexAvailable_IndirectStaticDep(t *testing.T) {
@@ -6516,14 +6570,14 @@
 	`)
 
 	fooManifestRule := result.ModuleForTests("foo", "android_common_foo").Rule("apexManifestRule")
-	fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion
+	fooExpectedDefaultVersion := testDefaultUpdatableModuleVersion
 	fooActualDefaultVersion := fooManifestRule.Args["default_version"]
 	if fooActualDefaultVersion != fooExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion)
 	}
 
 	barManifestRule := result.ModuleForTests("bar", "android_common_bar").Rule("apexManifestRule")
-	defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion)
+	defaultVersionInt, _ := strconv.Atoi(testDefaultUpdatableModuleVersion)
 	barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3)
 	barActualDefaultVersion := barManifestRule.Args["default_version"]
 	if barActualDefaultVersion != barExpectedDefaultVersion {
@@ -7339,7 +7393,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo"],
+			libs: ["foo.impl"],
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
@@ -7392,7 +7446,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo"],
+			libs: ["foo.stubs"],
 			sdk_version: "none",
 			system_modules: "none",
 		}
@@ -7446,7 +7500,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo"],
+			libs: ["foo.impl"],
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
@@ -8119,9 +8173,9 @@
 	ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
 
 	// Check that the extractor produces the correct output file from the correct input file.
-	extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
+	extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.hwasan.apks"
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
@@ -8146,10 +8200,10 @@
 		}
 	`)
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 
 	// Check that the extractor produces the correct apks file from the input module
-	extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.apks"
+	extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.apks"
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
@@ -8211,189 +8265,6 @@
 	return result.TestContext
 }
 
-func TestDuplicateDeapexersFromPrebuiltApexes(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		prepareForTestWithBootclasspathFragment,
-		dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:libfoo"),
-		PrepareForTestWithApexBuildComponents,
-	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art"))
-
-	bpBase := `
-		apex_set {
-			name: "com.android.art",
-			installable: true,
-			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
-			set: "myapex.apks",
-		}
-
-		apex_set {
-			name: "com.mycompany.android.art",
-			apex_name: "com.android.art",
-			installable: true,
-			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
-			set: "company-myapex.apks",
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "art-bootclasspath-fragment",
-			apex_available: ["com.android.art"],
-			hidden_api: {
-				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
-				metadata: "my-bootclasspath-fragment/metadata.csv",
-				index: "my-bootclasspath-fragment/index.csv",
-				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
-				all_flags: "my-bootclasspath-fragment/all-flags.csv",
-			},
-			%s
-		}
-	`
-
-	t.Run("java_import", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_import {
-				name: "libfoo",
-				jars: ["libfoo.jar"],
-				apex_available: ["com.android.art"],
-			}
-		`)
-	})
-
-	t.Run("java_sdk_library_import", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				shared_library: false,
-				apex_available: ["com.android.art"],
-			}
-		`)
-	})
-
-	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
-			image_name: "art",
-			contents: ["libfoo"],
-		`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				shared_library: false,
-				apex_available: ["com.android.art"],
-			}
-		`)
-	})
-}
-
-func TestDuplicateButEquivalentDeapexersFromPrebuiltApexes(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		PrepareForTestWithApexBuildComponents,
-	)
-
-	errCtx := moduleErrorfTestCtx{}
-
-	bpBase := `
-		apex_set {
-			name: "com.android.myapex",
-			installable: true,
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
-			set: "myapex.apks",
-		}
-
-		apex_set {
-			name: "com.android.myapex_compressed",
-			apex_name: "com.android.myapex",
-			installable: true,
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
-			set: "myapex_compressed.apks",
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "my-bootclasspath-fragment",
-			apex_available: [
-				"com.android.myapex",
-				"com.android.myapex_compressed",
-			],
-			hidden_api: {
-				annotation_flags: "annotation-flags.csv",
-				metadata: "metadata.csv",
-				index: "index.csv",
-				signature_patterns: "signature_patterns.csv",
-			},
-			%s
-		}
-	`
-
-	t.Run("java_import", func(t *testing.T) {
-		result := preparers.RunTestWithBp(t,
-			fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_import {
-				name: "libfoo",
-				jars: ["libfoo.jar"],
-				apex_available: [
-					"com.android.myapex",
-					"com.android.myapex_compressed",
-				],
-			}
-		`)
-
-		module := result.Module("libfoo", "android_common_com.android.myapex")
-		usesLibraryDep := module.(java.UsesLibraryDependency)
-		android.AssertPathRelativeToTopEquals(t, "dex jar path",
-			"out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
-			usesLibraryDep.DexJarBuildPath(errCtx).Path())
-	})
-
-	t.Run("java_sdk_library_import", func(t *testing.T) {
-		result := preparers.RunTestWithBp(t,
-			fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				apex_available: [
-					"com.android.myapex",
-					"com.android.myapex_compressed",
-				],
-				compile_dex: true,
-			}
-		`)
-
-		module := result.Module("libfoo", "android_common_com.android.myapex")
-		usesLibraryDep := module.(java.UsesLibraryDependency)
-		android.AssertPathRelativeToTopEquals(t, "dex jar path",
-			"out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
-			usesLibraryDep.DexJarBuildPath(errCtx).Path())
-	})
-
-	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
-			image_name: "art",
-			contents: ["libfoo"],
-		`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				apex_available: [
-					"com.android.myapex",
-					"com.android.myapex_compressed",
-				],
-				compile_dex: true,
-			}
-		`)
-	})
-}
-
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
@@ -8505,12 +8376,16 @@
 				},
 			}
 
-			java_import {
-				name: "libfoo",
+		java_sdk_library_import {
+			name: "libfoo",
+			prefer: true,
+			public: {
 				jars: ["libfoo.jar"],
-				apex_available: ["myapex"],
-				permitted_packages: ["libfoo"],
-			}
+			},
+			apex_available: ["myapex"],
+			shared_library: false,
+			permitted_packages: ["libfoo"],
+		}
 		`, "", preparer, fragment)
 	})
 }
@@ -8890,7 +8765,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 
 	// Check extract_apks tool parameters.
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -8931,7 +8806,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 
 	// Check extract_apks tool parameters. No native bridge arch expected
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -9618,42 +9493,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 {
@@ -9999,120 +9838,84 @@
 	}
 
 	testCases := []struct {
-		testCaseName              string
-		apexUpdatable             bool
-		javaStrictUpdtabilityLint bool
-		lintFileExists            bool
-		disallowedFlagExpected    bool
+		testCaseName                    string
+		apexUpdatable                   bool
+		javaStrictUpdtabilityLint       bool
+		lintFileExists                  bool
+		disallowedFlagExpectedOnApex    bool
+		disallowedFlagExpectedOnJavalib bool
 	}{
 		{
-			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
-			apexUpdatable:             true,
-			javaStrictUpdtabilityLint: true,
-			lintFileExists:            false,
-			disallowedFlagExpected:    false,
+			testCaseName:                    "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
+			apexUpdatable:                   true,
+			javaStrictUpdtabilityLint:       true,
+			lintFileExists:                  false,
+			disallowedFlagExpectedOnApex:    false,
+			disallowedFlagExpectedOnJavalib: false,
 		},
 		{
-			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
-			apexUpdatable:             false,
-			javaStrictUpdtabilityLint: false,
-			lintFileExists:            true,
-			disallowedFlagExpected:    false,
+			testCaseName:                    "non-updatable apex respects strict_updatability of javalib",
+			apexUpdatable:                   false,
+			javaStrictUpdtabilityLint:       false,
+			lintFileExists:                  true,
+			disallowedFlagExpectedOnApex:    false,
+			disallowedFlagExpectedOnJavalib: false,
 		},
 		{
-			testCaseName:              "non-updatable apex respects strict updatability of javalib",
-			apexUpdatable:             false,
-			javaStrictUpdtabilityLint: true,
-			lintFileExists:            true,
-			disallowedFlagExpected:    true,
+			testCaseName:                    "non-updatable apex respects strict updatability of javalib",
+			apexUpdatable:                   false,
+			javaStrictUpdtabilityLint:       true,
+			lintFileExists:                  true,
+			disallowedFlagExpectedOnApex:    false,
+			disallowedFlagExpectedOnJavalib: true,
 		},
 		{
-			testCaseName:              "updatable apex sets strict updatability of javalib to true",
-			apexUpdatable:             true,
-			javaStrictUpdtabilityLint: false, // will be set to true by mutator
-			lintFileExists:            true,
-			disallowedFlagExpected:    true,
+			testCaseName:                    "updatable apex checks strict updatability of javalib",
+			apexUpdatable:                   true,
+			javaStrictUpdtabilityLint:       false,
+			lintFileExists:                  true,
+			disallowedFlagExpectedOnApex:    true,
+			disallowedFlagExpectedOnJavalib: false,
 		},
 	}
 
 	for _, testCase := range testCases {
-		fixtures := []android.FixturePreparer{}
-		baselineProperty := ""
-		if testCase.lintFileExists {
-			fixtures = append(fixtures, fs.AddToFixture())
-			baselineProperty = "baseline_filename: \"lint-baseline.xml\""
-		}
-		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty)
-
-		result := testApex(t, bp, fixtures...)
-		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
-		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
-
-		if disallowedFlagActual != testCase.disallowedFlagExpected {
-			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-		}
-	}
-}
-
-func TestUpdatabilityLintSkipLibcore(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			java_libs: ["myjavalib"],
-			updatable: true,
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		java_library {
-			name: "myjavalib",
-			srcs: ["MyClass.java"],
-			apex_available: [ "myapex" ],
-			sdk_version: "current",
-			min_sdk_version: "29",
-			lint: {
-				baseline_filename: "lint-baseline.xml",
+		t.Run(testCase.testCaseName, func(t *testing.T) {
+			fixtures := []android.FixturePreparer{}
+			baselineProperty := ""
+			if testCase.lintFileExists {
+				fixtures = append(fixtures, fs.AddToFixture())
+				baselineProperty = "baseline_filename: \"lint-baseline.xml\""
 			}
-		}
-		`
+			bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty)
 
-	testCases := []struct {
-		testCaseName           string
-		moduleDirectory        string
-		disallowedFlagExpected bool
-	}{
-		{
-			testCaseName:           "lintable module defined outside libcore",
-			moduleDirectory:        "",
-			disallowedFlagExpected: true,
-		},
-		{
-			testCaseName:           "lintable module defined in libcore root directory",
-			moduleDirectory:        "libcore/",
-			disallowedFlagExpected: false,
-		},
-		{
-			testCaseName:           "lintable module defined in libcore child directory",
-			moduleDirectory:        "libcore/childdir/",
-			disallowedFlagExpected: true,
-		},
-	}
+			result := testApex(t, bp, fixtures...)
 
-	for _, testCase := range testCases {
-		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
-		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
-		result := testApex(t, "", lintFileCreator, bpFileCreator)
-		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
-		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
-		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
+			checkModule := func(m android.TestingBuildParams, name string, expectStrictUpdatability bool) {
+				if expectStrictUpdatability {
+					if m.Rule == nil {
+						t.Errorf("expected strict updatability check rule on %s", name)
+					} else {
+						android.AssertStringDoesContain(t, fmt.Sprintf("strict updatability check rule for %s", name),
+							m.RuleParams.Command, "--disallowed_issues NewApi")
+						android.AssertStringListContains(t, fmt.Sprintf("strict updatability check baselines for %s", name),
+							m.Inputs.Strings(), "lint-baseline.xml")
+					}
+				} else {
+					if m.Rule != nil {
+						t.Errorf("expected no strict updatability check rule on %s", name)
+					}
+				}
+			}
 
-		if disallowedFlagActual != testCase.disallowedFlagExpected {
-			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-		}
+			myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+			apex := result.ModuleForTests("myapex", "android_common_myapex")
+			apexStrictUpdatabilityCheck := apex.MaybeOutput("lint_strict_updatability_check.stamp")
+			javalibStrictUpdatabilityCheck := myjavalib.MaybeOutput("lint_strict_updatability_check.stamp")
+
+			checkModule(apexStrictUpdatabilityCheck, "myapex", testCase.disallowedFlagExpectedOnApex)
+			checkModule(javalibStrictUpdatabilityCheck, "myjavalib", testCase.disallowedFlagExpectedOnJavalib)
+		})
 	}
 }
 
@@ -10154,11 +9957,12 @@
 	}
 
 	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
-	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
-	}
+	apex := result.ModuleForTests("myapex", "android_common_myapex")
+	apexStrictUpdatabilityCheck := apex.Output("lint_strict_updatability_check.stamp")
+	android.AssertStringDoesContain(t, "strict updatability check rule for myapex",
+		apexStrictUpdatabilityCheck.RuleParams.Command, "--disallowed_issues NewApi")
+	android.AssertStringListContains(t, "strict updatability check baselines for myapex",
+		apexStrictUpdatabilityCheck.Inputs.Strings(), "lint-baseline.xml")
 }
 
 func TestApexLintBcpFragmentSdkLibDeps(t *testing.T) {
@@ -10217,7 +10021,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			updatable: %v,
+			updatable: true,
 			apps: [
 				"myapp",
 			],
@@ -10228,7 +10032,6 @@
 		}
 		android_app {
 			name: "myapp",
-			updatable: %v,
 			apex_available: [
 				"myapex",
 			],
@@ -10236,244 +10039,10 @@
 			min_sdk_version: "30",
 		}
 		`
-	testCases := []struct {
-		name                      string
-		apex_is_updatable_bp      bool
-		app_is_updatable_bp       bool
-		app_is_updatable_expected bool
-	}{
-		{
-			name:                      "Non-updatable apex respects updatable property of non-updatable app",
-			apex_is_updatable_bp:      false,
-			app_is_updatable_bp:       false,
-			app_is_updatable_expected: false,
-		},
-		{
-			name:                      "Non-updatable apex respects updatable property of updatable app",
-			apex_is_updatable_bp:      false,
-			app_is_updatable_bp:       true,
-			app_is_updatable_expected: true,
-		},
-		{
-			name:                      "Updatable apex respects updatable property of updatable app",
-			apex_is_updatable_bp:      true,
-			app_is_updatable_bp:       true,
-			app_is_updatable_expected: true,
-		},
-		{
-			name:                      "Updatable apex sets updatable=true on non-updatable app",
-			apex_is_updatable_bp:      true,
-			app_is_updatable_bp:       false,
-			app_is_updatable_expected: true,
-		},
-	}
-	for _, testCase := range testCases {
-		result := testApex(t, fmt.Sprintf(bp, testCase.apex_is_updatable_bp, testCase.app_is_updatable_bp))
-		myapp := result.ModuleForTests("myapp", "android_common").Module().(*java.AndroidApp)
-		android.AssertBoolEquals(t, testCase.name, testCase.app_is_updatable_expected, myapp.Updatable())
-	}
-}
-
-func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libbaz"],
-			binaries: ["binfoo"],
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		cc_binary {
-			name: "binfoo",
-			shared_libs: ["libbar", "libbaz", "libqux",],
-			apex_available: ["myapex"],
-			min_sdk_version: "29",
-			recovery_available: false,
-		}
-		cc_library {
-			name: "libbar",
-			srcs: ["libbar.cc"],
-			stubs: {
-				symbol_file: "libbar.map.txt",
-				versions: [
-					"29",
-				],
-			},
-		}
-		cc_library {
-			name: "libbaz",
-			srcs: ["libbaz.cc"],
-			apex_available: ["myapex"],
-			min_sdk_version: "29",
-			stubs: {
-				symbol_file: "libbaz.map.txt",
-				versions: [
-					"29",
-				],
-			},
-		}
-		cc_api_library {
-			name: "libbar",
-			src: "libbar_stub.so",
-			min_sdk_version: "29",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "29",
-			src: "libbar_apex_29.so",
-		}
-		cc_api_library {
-			name: "libbaz",
-			src: "libbaz_stub.so",
-			min_sdk_version: "29",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libbaz",
-			variant: "apex",
-			version: "29",
-			src: "libbaz_apex_29.so",
-		}
-		cc_api_library {
-			name: "libqux",
-			src: "libqux_stub.so",
-			min_sdk_version: "29",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libqux",
-			variant: "apex",
-			version: "29",
-			src: "libqux_apex_29.so",
-		}
-		api_imports {
-			name: "api_imports",
-			apex_shared_libs: [
-				"libbar",
-				"libbaz",
-				"libqux",
-			],
-		}
-		`
-	result := testApex(t, bp)
-
-	hasDep := func(m android.Module, wantDep android.Module) bool {
-		t.Helper()
-		var found bool
-		result.VisitDirectDeps(m, func(dep blueprint.Module) {
-			if dep == wantDep {
-				found = true
-			}
-		})
-		return found
-	}
-
-	// Library defines stubs and cc_api_library should be used with cc_api_library
-	binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Module()
-	libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
-	libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
-	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
-
-	binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
-
-	// Library defined in the same APEX should be linked with original definition instead of cc_api_library
-	libbazApexVariant := result.ModuleForTests("libbaz", "android_arm64_armv8-a_shared_apex29").Module()
-	libbazApiImportCoreVariant := result.ModuleForTests("libbaz.apiimport", "android_arm64_armv8-a_shared").Module()
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even from same APEX", true, hasDep(binfooApexVariant, libbazApiImportCoreVariant))
-	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbazApexVariant))
-
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbaz.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbaz.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbaz.apex.29.apiimport.so")
-
-	// cc_api_library defined without original library should be linked with cc_api_library
-	libquxApiImportApexVariant := result.ModuleForTests("libqux.apiimport", "android_arm64_armv8-a_shared").Module()
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even original library definition does not exist", true, hasDep(binfooApexVariant, libquxApiImportApexVariant))
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libqux.apex.29.apiimport.so")
-}
-
-func TestPlatformBinaryBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libbar"],
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		cc_binary {
-			name: "binfoo",
-			shared_libs: ["libbar"],
-			recovery_available: false,
-		}
-		cc_library {
-			name: "libbar",
-			srcs: ["libbar.cc"],
-			apex_available: ["myapex"],
-			min_sdk_version: "29",
-			stubs: {
-				symbol_file: "libbar.map.txt",
-				versions: [
-					"29",
-				],
-			},
-		}
-		cc_api_library {
-			name: "libbar",
-			src: "libbar_stub.so",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "29",
-			src: "libbar_apex_29.so",
-		}
-		api_imports {
-			name: "api_imports",
-			apex_shared_libs: [
-				"libbar",
-			],
-		}
-		`
-
-	result := testApex(t, bp)
-
-	hasDep := func(m android.Module, wantDep android.Module) bool {
-		t.Helper()
-		var found bool
-		result.VisitDirectDeps(m, func(dep blueprint.Module) {
-			if dep == wantDep {
-				found = true
-			}
-		})
-		return found
-	}
-
-	// Library defines stubs and cc_api_library should be used with cc_api_library
-	binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Module()
-	libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
-	libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
-	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
-
-	binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
+	_ = android.GroupFixturePreparers(
+		prepareForApexTest,
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("app dependency myapp must have updatable: true")).
+		RunTestWithBp(t, bp)
 }
 
 func TestTrimmedApex(t *testing.T) {
@@ -10514,21 +10083,6 @@
 			apex_available: ["myapex","mydcla"],
 			min_sdk_version: "29",
 		}
-		cc_api_library {
-			name: "libc",
-			src: "libc.so",
-			min_sdk_version: "29",
-			recovery_available: true,
-			vendor_available: true,
-			product_available: true,
-		}
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libc",
-			],
-			header_libs: [],
-		}
 		`
 	ctx := testApex(t, bp)
 	module := ctx.ModuleForTests("myapex", "android_common_myapex")
@@ -10800,14 +10354,15 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 12 {
-		t.Fatalf("Expected 12 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 14 {
+		t.Fatalf("Expected 14 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val")
+	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info.*/image.apex/etc/flag.info")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10817,6 +10372,7 @@
 	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info")
 }
 
 func TestAconfigFilesJavaAndCcDeps(t *testing.T) {
@@ -10935,14 +10491,15 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 16 {
-		t.Fatalf("Expected 16 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 18 {
+		t.Fatalf("Expected 18 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val")
+	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info .*/image.apex/etc/flag.info")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10953,6 +10510,7 @@
 	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info")
 }
 
 func TestAconfigFilesRustDeps(t *testing.T) {
@@ -11103,14 +10661,15 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 36 {
-		t.Fatalf("Expected 36 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 38 {
+		t.Fatalf("Expected 38 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val")
+	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info .*/image.apex/etc/flag.info")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -11122,6 +10681,7 @@
 	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info")
 }
 
 func VerifyAconfigRule(t *testing.T, mod *android.TestingModule, desc string, inputs []string, output string, container string, file_type string) {
@@ -11450,12 +11010,12 @@
 		{
 			desc:                      "Prebuilt apex prebuilt_com.android.foo is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedApexContributions: "foo.prebuilt.contributions",
-			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo.deapexer/android_common/deapexer/javalib/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
 		},
 		{
 			desc:                      "Prebuilt apex prebuilt_com.android.foo.v2 is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedApexContributions: "foo.prebuilt.v2.contributions",
-			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo.v2.deapexer/android_common/deapexer/javalib/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/com.android.foo.v2/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
 		},
 	}
 
@@ -12147,3 +11707,156 @@
 		)
 	})
 }
+
+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",
+			updatable: true,
+		}
+       `)
+}
+
+// If an apex sets system_ext_specific: true, its systemserverclasspath libraries must set this property as well.
+func TestApexSSCPJarMustBeInSamePartitionAsApex(t *testing.T) {
+	testApexError(t, `foo is an apex systemserver jar, but its partition does not match the partition of its containing apex`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			systemserverclasspath_fragments: [
+				"mysystemserverclasspathfragment",
+			],
+			min_sdk_version: "29",
+			updatable: true,
+			system_ext_specific: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			min_sdk_version: "29",
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+			sdk_version: "current",
+		}
+
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspathfragment",
+			contents: [
+				"foo",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	)
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index df7857f..e44d3f5 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -398,11 +398,20 @@
 
 			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
 			addPrebuilt(true, "foo", "bar"),
+			android.FixtureMergeMockFs(android.MockFS{
+				"apex_contributions/Android.bp": []byte(`
+				apex_contributions {
+					name: "prebuilt_art_contributions",
+					contents: ["prebuilt_com.android.art"],
+					api_domain: "com.android.art",
+				}
+			`)}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
 
 			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
-		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common", []string{
+		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_com.android.art", []string{
 			"etc/boot-image.prof",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
@@ -495,6 +504,7 @@
 		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 		dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
 		java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
 	)
 
 	bp := `
@@ -552,6 +562,12 @@
 			src: "com.mycompany.android.art.apex",
 			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
+	
+		apex_contributions {
+			name: "prebuilt_art_contributions",
+			contents: ["prebuilt_com.android.art"],
+			api_domain: "com.android.art",
+		}
 	`
 
 	t.Run("disabled alternative APEX", func(t *testing.T) {
@@ -561,27 +577,18 @@
 			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
-			`prebuilt_com.android.art.apex.selector`,
-			`prebuilt_com.android.art.deapexer`,
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
 			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_bar`,
-			`prebuilt_com.android.art.deapexer`,
 			`prebuilt_foo`,
 		})
 
 		module := result.ModuleForTests("dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
-
-	t.Run("enabled alternative APEX", func(t *testing.T) {
-		preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")).
-			RunTestWithBp(t, fmt.Sprintf(bp, ""))
-	})
 }
 
 // checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
diff --git a/apex/builder.go b/apex/builder.go
index 437189f..371d7d5 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -96,6 +96,7 @@
 	{"package.map", "create_aconfig_package_map_file", "package_map"},
 	{"flag.map", "create_aconfig_flag_map_file", "flag_map"},
 	{"flag.val", "create_aconfig_flag_val_file", "flag_val"},
+	{"flag.info", "create_aconfig_flag_info_file", "flag_info"},
 }
 
 var (
@@ -359,14 +360,14 @@
 	}
 
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
-	defaultVersion := android.DefaultUpdatableModuleVersion
+	defaultVersion := ctx.Config().ReleaseDefaultUpdatableModuleVersion()
 	if a.properties.Variant_version != nil {
 		defaultVersionInt, err := strconv.Atoi(defaultVersion)
 		if err != nil {
-			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to be an int, but got %s", defaultVersion)
+			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to be an int, but got %s", defaultVersion)
 		}
 		if defaultVersionInt%10 != 0 {
-			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to end in a zero, but got %s", defaultVersion)
+			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero, but got %s", defaultVersion)
 		}
 		variantVersion := []rune(*a.properties.Variant_version)
 		if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
@@ -1073,8 +1074,9 @@
 		}
 		return ""
 	}
-	if a.overridableProperties.Package_name != "" {
-		return a.overridableProperties.Package_name
+	packageNameFromProp := a.overridableProperties.Package_name.GetOrDefault(ctx, "")
+	if packageNameFromProp != "" {
+		return packageNameFromProp
 	}
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
 	if overridden {
@@ -1091,7 +1093,7 @@
 	}
 
 	depInfos := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if from.Name() == to.Name() {
 			// This can happen for cc.reuseObjTag. We are not interested in tracking this.
 			// As soon as the dependency graph crosses the APEX boundary, don't go further.
@@ -1158,10 +1160,23 @@
 func (a *apexBundle) buildLintReports(ctx android.ModuleContext) {
 	depSetsBuilder := java.NewLintDepSetBuilder()
 	for _, fi := range a.filesInfo {
-		depSetsBuilder.Transitive(fi.lintDepSets)
+		if fi.lintInfo != nil {
+			depSetsBuilder.Transitive(fi.lintInfo)
+		}
 	}
 
-	a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
+	depSets := depSetsBuilder.Build()
+	var validations android.Paths
+
+	if a.checkStrictUpdatabilityLinting(ctx) {
+		baselines := depSets.Baseline.ToList()
+		if len(baselines) > 0 {
+			outputFile := java.VerifyStrictUpdatabilityChecks(ctx, baselines)
+			validations = append(validations, outputFile)
+		}
+	}
+
+	a.lintReports = java.BuildModuleLintReportZips(ctx, depSets, validations)
 }
 
 func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
diff --git a/apex/deapexer.go b/apex/deapexer.go
index a673108..3b8233d 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -15,37 +15,9 @@
 package apex
 
 import (
-	"strings"
-
 	"android/soong/android"
 )
 
-// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
-// within a .apex file referenced by `prebuilt_apex` available for use by their associated
-// `java_import` modules.
-//
-// An 'apex' module references `java_library` modules from which .dex files are obtained that are
-// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
-// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
-// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
-// that contains the Java classes.
-//
-// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
-// `java_import` provides the classes jar  (jar containing `.class` files) against which the
-// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
-// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
-// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
-// library specific to the current Android version. This process requires access to implementation
-// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
-// the `.apex` file in the associated `prebuilt_apex`.
-//
-// This is intentionally not registered by name as it is not intended to be used from within an
-// `Android.bp` file.
-
-// DeapexerProperties specifies the properties supported by the deapexer module.
-//
-// As these are never intended to be supplied in a .bp file they use a different naming convention
-// to make it clear that they are different.
 type DeapexerProperties struct {
 	// List of common modules that may need access to files exported by this module.
 	//
@@ -72,46 +44,9 @@
 	Selected_apex *string `android:"path" blueprint:"mutated"`
 }
 
-type Deapexer struct {
-	android.ModuleBase
-
-	properties             DeapexerProperties
-	selectedApexProperties SelectedApexProperties
-
-	inputApex android.Path
-}
-
-// Returns the name of the deapexer module corresponding to an APEX module with the given name.
-func deapexerModuleName(apexModuleName string) string {
-	return apexModuleName + ".deapexer"
-}
-
-// Returns the name of the APEX module corresponding to an deapexer module with
-// the given name. This reverses deapexerModuleName.
-func apexModuleName(deapexerModuleName string) string {
-	return strings.TrimSuffix(deapexerModuleName, ".deapexer")
-}
-
-func privateDeapexerFactory() android.Module {
-	module := &Deapexer{}
-	module.AddProperties(&module.properties, &module.selectedApexProperties)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
-}
-
-func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// Add dependencies from the java modules to which this exports files from the `.apex` file onto
-	// this module so that they can access the `DeapexerInfo` object that this provides.
-	// TODO: b/308174306 - Once all the mainline modules have been flagged, drop this dependency edge
-	for _, lib := range p.properties.CommonModules {
-		dep := prebuiltApexExportedModuleName(ctx, lib)
-		ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
-	}
-}
-
-func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
-
+// deapex creates the build rules to deapex a prebuilt .apex file
+// it returns a pointer to a DeapexerInfo object
+func deapex(ctx android.ModuleContext, apexFile android.Path, deapexerProps DeapexerProperties) *android.DeapexerInfo {
 	// Create and remember the directory into which the .apex file's contents will be unpacked.
 	deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
 
@@ -119,7 +54,7 @@
 
 	// Create mappings from apex relative path to the extracted file's path.
 	exportedPaths := make(android.Paths, 0, len(exports))
-	for _, path := range p.properties.ExportedFiles {
+	for _, path := range deapexerProps.ExportedFiles {
 		// Populate the exports that this makes available.
 		extractedPath := deapexerOutput.Join(ctx, path)
 		exports[path] = extractedPath
@@ -131,9 +66,8 @@
 	// apex relative path to extracted file path available for other modules.
 	if len(exports) > 0 {
 		// Make the information available for other modules.
-		di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports, p.properties.CommonModules)
-		di.AddDexpreoptProfileGuidedExportedModuleNames(p.properties.DexpreoptProfileGuidedModules...)
-		android.SetProvider(ctx, android.DeapexerProvider, di)
+		di := android.NewDeapexerInfo(ctx.ModuleName(), exports, deapexerProps.CommonModules)
+		di.AddDexpreoptProfileGuidedExportedModuleNames(deapexerProps.DexpreoptProfileGuidedModules...)
 
 		// Create a sorted list of the files that this exports.
 		exportedPaths = android.SortedUniquePaths(exportedPaths)
@@ -147,11 +81,13 @@
 			BuiltTool("deapexer").
 			BuiltTool("debugfs").
 			BuiltTool("fsck.erofs").
-			Input(p.inputApex).
+			Input(apexFile).
 			Text(deapexerOutput.String())
 		for _, p := range exportedPaths {
 			command.Output(p.(android.WritablePath))
 		}
-		builder.Build("deapexer", "deapex "+apexModuleName(ctx.ModuleName()))
+		builder.Build("deapexer", "deapex "+ctx.ModuleName())
+		return &di
 	}
+	return nil
 }
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index d8ee4ba..4feade8 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -127,16 +127,29 @@
 			src: "com.android.art-arm.apex",
 			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
+
+		apex_contributions {
+			name: "prebuilt_art_contributions",
+			contents: ["prebuilt_com.android.art"],
+			api_domain: "com.android.art",
+		}
 	`
 
-	result := android.GroupFixturePreparers(
+	fixture := android.GroupFixturePreparers(
 		java.PrepareForTestWithDexpreopt,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
 		java.FixtureConfigureBootJars("com.android.art:core-oj", "platform:foo", "system_ext:bar", "platform:baz"),
 		PrepareForTestWithApexBuildComponents,
 		prepareForTestWithArtApex,
-	).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
+	)
+	if preferPrebuilt {
+		fixture = android.GroupFixturePreparers(
+			fixture,
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
+		)
+	}
+	result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
 
 	dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
 	rule := dexBootJars.Output(ruleFile)
@@ -200,7 +213,7 @@
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
-		"out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+		"out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof",
 		"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
 		"out/soong/dexpreopt/uffd_gc_flag.txt",
 	}
@@ -384,12 +397,12 @@
 		{
 			desc:                         "Prebuilt apex prebuilt_com.android.art is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedArtApexContributions: "art.prebuilt.contributions",
-			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof",
 		},
 		{
 			desc:                         "Prebuilt apex prebuilt_com.android.art.v2 is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedArtApexContributions: "art.prebuilt.v2.contributions",
-			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art.v2.deapexer/android_common/deapexer/etc/boot-image.prof",
+			expectedProfile:              "out/soong/.intermediates/com.android.art.v2/android_common_com.android.art/deapexer/etc/boot-image.prof",
 		},
 	}
 	for _, tc := range testCases {
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 9c2d899..f4da31e 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -409,6 +409,9 @@
 		// The fragments.
 		`com.android.art:art-bootclasspath-fragment`,
 		`myapex:my-bootclasspath-fragment`,
+
+		// Impl lib of sdk_library for transitive srcjar generation
+		`platform:foo.impl`,
 	})
 }
 
@@ -565,6 +568,9 @@
 		// The fragments.
 		"myapex:mybootclasspath-fragment",
 		"myapex:prebuilt_mybootclasspath-fragment",
+
+		// Impl lib of sdk_library for transitive srcjar generation
+		"platform:foo.impl",
 	})
 }
 
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index f1a134e..9cd5688 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
@@ -199,9 +194,8 @@
 }
 
 // If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
-func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
-	// If this apex does not export anything, return
-	if !p.hasExportedDeps() {
+func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext, di *android.DeapexerInfo) {
+	if di == nil {
 		return
 	}
 	// If this prebuilt apex has not been selected, return
@@ -210,10 +204,7 @@
 	}
 	// Use apex_name to determine the api domain of this prebuilt apex
 	apexName := p.ApexVariationName()
-	di, err := android.FindDeapexerProviderForModule(ctx)
-	if err != nil {
-		ctx.ModuleErrorf(err.Error())
-	}
+	// TODO: do not compute twice
 	dc := dexpreopt.GetGlobalConfig(ctx)
 	systemServerJarList := dc.AllApexSystemServerJars(ctx)
 
@@ -262,29 +253,8 @@
 	return entriesList
 }
 
-// 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)
-}
-
-// prebuiltApexModuleCreatorMutator is the mutator responsible for invoking the
-// prebuiltApexModuleCreator's createPrebuiltApexModules method.
-//
-// It is registered as a pre-arch mutator as it must run after the ComponentDepsMutator because it
-// 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) {
-	module := ctx.Module()
-	if creator, ok := module.(prebuiltApexModuleCreator); ok {
-		creator.createPrebuiltApexModules(ctx)
-	}
-}
-
 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 +262,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)
@@ -414,34 +379,6 @@
 	}
 }
 
-// prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex
-// module. It selects the apex to use and makes it available for use by prebuilt_apex and the
-// deapexer.
-type prebuiltApexSelectorModule struct {
-	android.ModuleBase
-
-	apexFileProperties ApexFileProperties
-
-	inputApex android.Path
-}
-
-func privateApexSelectorModuleFactory() android.Module {
-	module := &prebuiltApexSelectorModule{}
-	module.AddProperties(
-		&module.apexFileProperties,
-	)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
-}
-
-func (p *prebuiltApexSelectorModule) Srcs() android.Paths {
-	return android.Paths{p.inputApex}
-}
-
-func (p *prebuiltApexSelectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.inputApex = android.SingleSourcePathFromSupplier(ctx, p.apexFileProperties.prebuiltApexSelector, "src")
-}
-
 type Prebuilt struct {
 	prebuiltCommon
 
@@ -484,11 +421,11 @@
 // to use methods on it that are specific to the current module.
 //
 // See the ApexFileProperties.Src property.
-func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) string {
 	multiTargets := prebuilt.MultiTargets()
 	if len(multiTargets) != 1 {
 		ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
-		return nil
+		return ""
 	}
 	var src string
 	switch multiTargets[0].Arch.ArchType {
@@ -521,7 +458,7 @@
 		// logic from reporting a more general, less useful message.
 	}
 
-	return []string{src}
+	return src
 }
 
 type PrebuiltProperties struct {
@@ -538,35 +475,19 @@
 func PrebuiltFactory() android.Module {
 	module := &Prebuilt{}
 	module.AddProperties(&module.properties)
-	module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
+	module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties
+
+	// init the module as a prebuilt
+	// even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing
+	// InitPrebuiltModule* are not friendly with Sources of Configurable type.
+	// The actual src will be evaluated in GenerateAndroidBuildActions.
+	android.InitPrebuiltModuleWithoutSrcs(module)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 
 	return module
 }
 
-func createApexSelectorModule(ctx android.TopDownMutatorContext, name string, apexFileProperties *ApexFileProperties) {
-	props := struct {
-		Name *string
-	}{
-		Name: proptools.StringPtr(name),
-	}
-
-	ctx.CreateModule(privateApexSelectorModuleFactory,
-		&props,
-		apexFileProperties,
-	)
-}
-
-// 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 listed modules need access to files from within the prebuilt .apex file.
-func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) {
-	// Only create the deapexer module if it is needed.
-	if !p.hasExportedDeps() {
-		return
-	}
-
+func (p *prebuiltCommon) getDeapexerPropertiesIfNeeded(ctx android.ModuleContext) DeapexerProperties {
 	// Compute the deapexer properties from the transitive dependencies of this module.
 	commonModules := []string{}
 	dexpreoptProfileGuidedModules := []string{}
@@ -600,7 +521,7 @@
 	})
 
 	// Create properties for deapexer module.
-	deapexerProperties := &DeapexerProperties{
+	deapexerProperties := DeapexerProperties{
 		// Remove any duplicates from the common modules lists as a module may be included via a direct
 		// dependency as well as transitive ones.
 		CommonModules:                 android.SortedUniqueStrings(commonModules),
@@ -609,22 +530,7 @@
 
 	// Populate the exported files property in a fixed order.
 	deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles)
-
-	props := struct {
-		Name          *string
-		Selected_apex *string
-	}{
-		Name:          proptools.StringPtr(deapexerName),
-		Selected_apex: proptools.StringPtr(apexFileSource),
-	}
-	ctx.CreateModule(privateDeapexerFactory,
-		&props,
-		deapexerProperties,
-	)
-}
-
-func apexSelectorModuleName(baseModuleName string) string {
-	return baseModuleName + ".apex.selector"
+	return deapexerProperties
 }
 
 func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
@@ -666,97 +572,50 @@
 var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
 
 var (
-	exportedJavaLibTag                       = exportedDependencyTag{name: "exported_java_libs"}
 	exportedBootclasspathFragmentTag         = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
 	exportedSystemserverclasspathFragmentTag = exportedDependencyTag{name: "exported_systemserverclasspath_fragments"}
 )
 
-var _ prebuiltApexModuleCreator = (*Prebuilt)(nil)
-
-// createPrebuiltApexModules creates modules necessary to export files from the prebuilt apex to the
-// 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,
-// it does so as follows:
-//
-//  1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
-//     makes them available for use by other modules, at both Soong and ninja levels.
-//
-//  2. It adds a dependency onto those modules and creates an apex specific variant similar to what
-//     an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
-//     dexpreopt, will work the same way from source and prebuilt.
-//
-//  3. The `deapexer` module adds a dependency from the modules that require the exported files onto
-//     itself so that they can retrieve the file paths to those files.
-//
-// It also creates a child module `selector` that is responsible for selecting the appropriate
-// input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons:
-//
-//  1. To dedup the selection logic so it only runs in one module.
-//
-//  2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
-//     `apex_set`.
-//
-//     prebuilt_apex
-//     /      |      \
-//     /         |         \
-//     V            V            V
-//     selector  <---  deapexer  <---  exported java lib
-func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
-	apexSelectorModuleName := apexSelectorModuleName(p.Name())
-	createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
-
-	apexFileSource := ":" + apexSelectorModuleName
-	p.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(p.Name()), apexFileSource)
-
-	// Add a source reference to retrieve the selected apex from the selector module.
-	p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
-}
-
 func (p *Prebuilt) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	p.prebuiltApexContentsDeps(ctx)
 }
 
-func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if p.hasExportedDeps() {
-		// Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module
-		// The deapexer will return a provider which will be used to determine the exported artfifacts from this prebuilt.
-		ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name()))
-	}
-}
-
 var _ ApexInfoMutator = (*Prebuilt)(nil)
 
 func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
 	p.apexInfoMutator(mctx)
 }
 
+// creates the build rules to deapex the prebuilt, and returns a deapexerInfo
+func (p *prebuiltCommon) getDeapexerInfo(ctx android.ModuleContext, apexFile android.Path) *android.DeapexerInfo {
+	if !p.hasExportedDeps() {
+		// nothing to do
+		return nil
+	}
+	deapexerProps := p.getDeapexerPropertiesIfNeeded(ctx)
+	return deapex(ctx, apexFile, deapexerProps)
+}
+
 // Set a provider containing information about the jars and .prof provided by the apex
 // Apexes built from prebuilts retrieve this information by visiting its internal deapexer module
 // Used by dex_bootjars to generate the boot image
-func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) {
-	if !p.hasExportedDeps() {
-		// nothing to do
+func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext, di *android.DeapexerInfo) {
+	if di == nil {
 		return
 	}
-	if di, err := android.FindDeapexerProviderForModule(ctx); err == nil {
-		javaModuleToDexPath := map[string]android.Path{}
-		for _, commonModule := range di.GetExportedModuleNames() {
-			if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
-				javaModuleToDexPath[commonModule] = dex
-			}
+	javaModuleToDexPath := map[string]android.Path{}
+	for _, commonModule := range di.GetExportedModuleNames() {
+		if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
+			javaModuleToDexPath[commonModule] = dex
 		}
-
-		exports := android.ApexExportsInfo{
-			ApexName:                      p.ApexVariationName(),
-			ProfilePathOnHost:             di.PrebuiltExportPath(java.ProfileInstallPathInApex),
-			LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
-		}
-		android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
-	} else {
-		ctx.ModuleErrorf(err.Error())
 	}
+
+	exports := android.ApexExportsInfo{
+		ApexName:                      p.ApexVariationName(),
+		ProfilePathOnHost:             di.PrebuiltExportPath(java.ProfileInstallPathInApex),
+		LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
+	}
+	android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -792,7 +651,7 @@
 
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
-	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
+	p.inputApex = android.PathForModuleSrc(ctx, p.properties.prebuiltApexSelector(ctx, ctx.Module()))
 	p.installDir = android.PathForModuleInstall(ctx, "apex")
 	p.installFilename = p.InstallFilename()
 	if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
@@ -810,11 +669,13 @@
 		return
 	}
 
+	deapexerInfo := p.getDeapexerInfo(ctx, p.inputApex)
+
 	// dexpreopt any system server jars if present
-	p.dexpreoptSystemServerJars(ctx)
+	p.dexpreoptSystemServerJars(ctx, deapexerInfo)
 
 	// provide info used for generating the boot image
-	p.provideApexExportsInfo(ctx)
+	p.provideApexExportsInfo(ctx, deapexerInfo)
 
 	p.providePrebuiltInfo(ctx)
 
@@ -850,26 +711,11 @@
 	extractedApex android.WritablePath
 }
 
-func privateApexExtractorModuleFactory() android.Module {
-	module := &prebuiltApexExtractorModule{}
-	module.AddProperties(
-		&module.properties,
-	)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
-}
-
-func (p *prebuiltApexExtractorModule) Srcs() android.Paths {
-	return android.Paths{p.extractedApex}
-}
-
-func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
-		return p.properties.prebuiltSrcs(ctx)
-	}
+// extract registers the build actions to extract an apex from .apks file
+// returns the path of the extracted apex
+func extract(ctx android.ModuleContext, apexSet android.Path, prerelease *bool) android.Path {
 	defaultAllowPrerelease := ctx.Config().IsEnvTrue("SOONG_ALLOW_PRERELEASE_APEXES")
-	apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
-	p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+	extractedApex := android.PathForModuleOut(ctx, "extracted", apexSet.Base())
 	// Filter out NativeBridge archs (b/260115309)
 	abis := java.SupportedAbis(ctx, true)
 	ctx.Build(pctx,
@@ -877,14 +723,16 @@
 			Rule:        extractMatchingApex,
 			Description: "Extract an apex from an apex set",
 			Inputs:      android.Paths{apexSet},
-			Output:      p.extractedApex,
+			Output:      extractedApex,
 			Args: map[string]string{
 				"abis":              strings.Join(abis, ","),
-				"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(p.properties.Prerelease, defaultAllowPrerelease)),
+				"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(prerelease, defaultAllowPrerelease)),
 				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
 				"skip-sdk-check":    strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
 			},
-		})
+		},
+	)
+	return extractedApex
 }
 
 type ApexSet struct {
@@ -953,48 +801,18 @@
 func apexSetFactory() android.Module {
 	module := &ApexSet{}
 	module.AddProperties(&module.properties)
-	module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
+	module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties
+
+	// init the module as a prebuilt
+	// even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing
+	// InitPrebuiltModule* are not friendly with Sources of Configurable type.
+	// The actual src will be evaluated in GenerateAndroidBuildActions.
+	android.InitPrebuiltModuleWithoutSrcs(module)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 
 	return module
 }
 
-func createApexExtractorModule(ctx android.TopDownMutatorContext, name string, apexExtractorProperties *ApexExtractorProperties) {
-	props := struct {
-		Name *string
-	}{
-		Name: proptools.StringPtr(name),
-	}
-
-	ctx.CreateModule(privateApexExtractorModuleFactory,
-		&props,
-		apexExtractorProperties,
-	)
-}
-
-func apexExtractorModuleName(baseModuleName string) string {
-	return baseModuleName + ".apex.extractor"
-}
-
-var _ prebuiltApexModuleCreator = (*ApexSet)(nil)
-
-// createPrebuiltApexModules creates modules necessary to export files from the apex set to other
-// modules.
-//
-// This effectively does for apex_set what Prebuilt.createPrebuiltApexModules does for a
-// 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) {
-	apexExtractorModuleName := apexExtractorModuleName(a.Name())
-	createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
-
-	apexFileSource := ":" + apexExtractorModuleName
-	a.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(a.Name()), apexFileSource)
-
-	// After passing the arch specific src properties to the creating the apex selector module
-	a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
-}
-
 func (a *ApexSet) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	a.prebuiltApexContentsDeps(ctx)
 }
@@ -1017,7 +835,15 @@
 		ctx.ModuleErrorf("filename should end in %s or %s for apex_set", imageApexSuffix, imageCapexSuffix)
 	}
 
-	inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path()
+	var apexSet android.Path
+	if srcs := a.properties.prebuiltSrcs(ctx); len(srcs) == 1 {
+		apexSet = android.PathForModuleSrc(ctx, srcs[0])
+	} else {
+		ctx.ModuleErrorf("Expected exactly one source apex_set file, found %v\n", srcs)
+	}
+
+	extractedApex := extract(ctx, apexSet, a.properties.Prerelease)
+
 	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
 
 	// Build the output APEX. If compression is not enabled, make sure the output is not compressed even if the input is compressed
@@ -1027,7 +853,7 @@
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   buildRule,
-		Input:  inputApex,
+		Input:  extractedApex,
 		Output: a.outputApex,
 	})
 
@@ -1036,11 +862,13 @@
 		return
 	}
 
+	deapexerInfo := a.getDeapexerInfo(ctx, extractedApex)
+
 	// dexpreopt any system server jars if present
-	a.dexpreoptSystemServerJars(ctx)
+	a.dexpreoptSystemServerJars(ctx, deapexerInfo)
 
 	// provide info used for generating the boot image
-	a.provideApexExportsInfo(ctx)
+	a.provideApexExportsInfo(ctx, deapexerInfo)
 
 	a.providePrebuiltInfo(ctx)
 
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index fd9020b..acb3649 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -277,8 +277,6 @@
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
 		`all_apex_contributions`,
 		`dex2oatd`,
-		`prebuilt_myapex.apex.selector`,
-		`prebuilt_myapex.deapexer`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
@@ -286,10 +284,9 @@
 		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
-		`prebuilt_myapex.deapexer`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
@@ -439,10 +436,9 @@
 		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
-		`prebuilt_myapex.deapexer`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
diff --git a/apex/testing.go b/apex/testing.go
index 3b200f0..63c5b69 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -16,6 +16,8 @@
 
 import "android/soong/android"
 
+const testDefaultUpdatableModuleVersion = "340090000"
+
 var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
 	android.FixtureRegisterWithContext(registerApexBuildComponents),
 	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
@@ -29,4 +31,5 @@
 		// Needed by prebuilt_apex.
 		"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
 	}.AddToFixture(),
+	android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion),
 )
diff --git a/apex/vndk.go b/apex/vndk.go
index 781aa3c..5e630c0 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -54,30 +55,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()
@@ -90,11 +67,37 @@
 		vndkApexName := "com.android.vndk." + vndkVersion
 
 		if mctx.OtherModuleExists(vndkApexName) {
-			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName)
+			// Reverse dependencies must exactly specify the variant they want, starting from the
+			// current module's variant. But unlike cc modules, the vndk apex doesn't have
+			// arch/image/link variations, so we explicitly remove them here.
+			mctx.AddReverseVariationDependency([]blueprint.Variation{
+				{Mutator: "arch", Variation: "common"},
+				{Mutator: "image", Variation: ""},
+				{Mutator: "link", Variation: ""},
+			}, 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/bazel/Android.bp b/bazel/Android.bp
deleted file mode 100644
index f8273a8..0000000
--- a/bazel/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-bazel",
-    pkgPath: "android/soong/bazel",
-    srcs: [
-        "configurability.go",
-        "properties.go",
-        "testing.go",
-    ],
-    testSrcs: [
-        "properties_test.go",
-    ],
-    pluginFor: [
-        "soong_build",
-    ],
-    deps: [
-        "blueprint",
-    ],
-}
diff --git a/bazel/configurability.go b/bazel/configurability.go
deleted file mode 100644
index 2c9a536..0000000
--- a/bazel/configurability.go
+++ /dev/null
@@ -1,393 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bazel
-
-import (
-	"fmt"
-	"math"
-	"sort"
-	"strings"
-)
-
-const (
-	// ArchType names in arch.go
-	archArm     = "arm"
-	archArm64   = "arm64"
-	archRiscv64 = "riscv64"
-	archX86     = "x86"
-	archX86_64  = "x86_64"
-
-	// OsType names in arch.go
-	OsAndroid     = "android"
-	OsDarwin      = "darwin"
-	OsLinux       = "linux_glibc"
-	osLinuxMusl   = "linux_musl"
-	osLinuxBionic = "linux_bionic"
-	OsWindows     = "windows"
-
-	// Targets in arch.go
-	osArchAndroidArm        = "android_arm"
-	OsArchAndroidArm64      = "android_arm64"
-	osArchAndroidRiscv64    = "android_riscv64"
-	osArchAndroidX86        = "android_x86"
-	osArchAndroidX86_64     = "android_x86_64"
-	osArchDarwinArm64       = "darwin_arm64"
-	osArchDarwinX86_64      = "darwin_x86_64"
-	osArchLinuxX86          = "linux_glibc_x86"
-	osArchLinuxX86_64       = "linux_glibc_x86_64"
-	osArchLinuxMuslArm      = "linux_musl_arm"
-	osArchLinuxMuslArm64    = "linux_musl_arm64"
-	osArchLinuxMuslX86      = "linux_musl_x86"
-	osArchLinuxMuslX86_64   = "linux_musl_x86_64"
-	osArchLinuxBionicArm64  = "linux_bionic_arm64"
-	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
-	osArchWindowsX86        = "windows_x86"
-	osArchWindowsX86_64     = "windows_x86_64"
-
-	// This is the string representation of the default condition wherever a
-	// configurable attribute is used in a select statement, i.e.
-	// //conditions:default for Bazel.
-	//
-	// This is consistently named "conditions_default" to mirror the Soong
-	// config variable default key in an Android.bp file, although there's no
-	// integration with Soong config variables (yet).
-	ConditionsDefaultConfigKey = "conditions_default"
-
-	ConditionsDefaultSelectKey = "//conditions:default"
-
-	productVariableBazelPackage = "//build/bazel/product_config/config_settings"
-
-	AndroidAndInApex = "android-in_apex"
-	AndroidPlatform  = "system"
-	Unbundled_app    = "unbundled_app"
-
-	InApex  = "in_apex"
-	NonApex = "non_apex"
-
-	ErrorproneDisabled = "errorprone_disabled"
-	// TODO: b/294868620 - Remove when completing the bug
-	SanitizersEnabled = "sanitizers_enabled"
-)
-
-func PowerSetWithoutEmptySet[T any](items []T) [][]T {
-	resultSize := int(math.Pow(2, float64(len(items))))
-	powerSet := make([][]T, 0, resultSize-1)
-	for i := 1; i < resultSize; i++ {
-		combination := make([]T, 0)
-		for j := 0; j < len(items); j++ {
-			if (i>>j)%2 == 1 {
-				combination = append(combination, items[j])
-			}
-		}
-		powerSet = append(powerSet, combination)
-	}
-	return powerSet
-}
-
-func createPlatformArchMap() map[string]string {
-	// Copy of archFeatures from android/arch_list.go because the bazel
-	// package can't access the android package
-	archFeatures := map[string][]string{
-		"arm": {
-			"neon",
-		},
-		"arm64": {
-			"dotprod",
-		},
-		"riscv64": {},
-		"x86": {
-			"ssse3",
-			"sse4",
-			"sse4_1",
-			"sse4_2",
-			"aes_ni",
-			"avx",
-			"avx2",
-			"avx512",
-			"popcnt",
-			"movbe",
-		},
-		"x86_64": {
-			"ssse3",
-			"sse4",
-			"sse4_1",
-			"sse4_2",
-			"aes_ni",
-			"avx",
-			"avx2",
-			"avx512",
-			"popcnt",
-		},
-	}
-	result := make(map[string]string)
-	for arch, allFeatures := range archFeatures {
-		result[arch] = "//build/bazel_common_rules/platforms/arch:" + arch
-		// Sometimes we want to select on multiple features being active, so
-		// add the power set of all possible features to the map. More details
-		// in android.ModuleBase.GetArchVariantProperties
-		for _, features := range PowerSetWithoutEmptySet(allFeatures) {
-			sort.Strings(features)
-			archFeaturesName := arch + "-" + strings.Join(features, "-")
-			result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
-		}
-	}
-	result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
-	return result
-}
-
-var (
-	// These are the list of OSes and architectures with a Bazel config_setting
-	// and constraint value equivalent. These exist in arch.go, but the android
-	// package depends on the bazel package, so a cyclic dependency prevents
-	// using those variables here.
-
-	// A map of architectures to the Bazel label of the constraint_value
-	// for the @platforms//cpu:cpu constraint_setting
-	platformArchMap = createPlatformArchMap()
-
-	// A map of target operating systems to the Bazel label of the
-	// constraint_value for the @platforms//os:os constraint_setting
-	platformOsMap = map[string]string{
-		OsAndroid:                  "//build/bazel_common_rules/platforms/os:android",
-		OsDarwin:                   "//build/bazel_common_rules/platforms/os:darwin",
-		OsLinux:                    "//build/bazel_common_rules/platforms/os:linux_glibc",
-		osLinuxMusl:                "//build/bazel_common_rules/platforms/os:linux_musl",
-		osLinuxBionic:              "//build/bazel_common_rules/platforms/os:linux_bionic",
-		OsWindows:                  "//build/bazel_common_rules/platforms/os:windows",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
-	}
-
-	platformOsArchMap = map[string]string{
-		osArchAndroidArm:           "//build/bazel_common_rules/platforms/os_arch:android_arm",
-		OsArchAndroidArm64:         "//build/bazel_common_rules/platforms/os_arch:android_arm64",
-		osArchAndroidRiscv64:       "//build/bazel_common_rules/platforms/os_arch:android_riscv64",
-		osArchAndroidX86:           "//build/bazel_common_rules/platforms/os_arch:android_x86",
-		osArchAndroidX86_64:        "//build/bazel_common_rules/platforms/os_arch:android_x86_64",
-		osArchDarwinArm64:          "//build/bazel_common_rules/platforms/os_arch:darwin_arm64",
-		osArchDarwinX86_64:         "//build/bazel_common_rules/platforms/os_arch:darwin_x86_64",
-		osArchLinuxX86:             "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86",
-		osArchLinuxX86_64:          "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86_64",
-		osArchLinuxMuslArm:         "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm",
-		osArchLinuxMuslArm64:       "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm64",
-		osArchLinuxMuslX86:         "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86",
-		osArchLinuxMuslX86_64:      "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86_64",
-		osArchLinuxBionicArm64:     "//build/bazel_common_rules/platforms/os_arch:linux_bionic_arm64",
-		osArchLinuxBionicX86_64:    "//build/bazel_common_rules/platforms/os_arch:linux_bionic_x86_64",
-		osArchWindowsX86:           "//build/bazel_common_rules/platforms/os_arch:windows_x86",
-		osArchWindowsX86_64:        "//build/bazel_common_rules/platforms/os_arch:windows_x86_64",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
-	}
-
-	// Map where keys are OsType names, and values are slices containing the archs
-	// that that OS supports.
-	// These definitions copied from arch.go.
-	// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
-	// in a cyclic dependency.
-	osToArchMap = map[string][]string{
-		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
-		OsLinux:       {archX86, archX86_64},
-		osLinuxMusl:   {archX86, archX86_64},
-		OsDarwin:      {archArm64, archX86_64},
-		osLinuxBionic: {archArm64, archX86_64},
-		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
-		OsWindows: {archX86, archX86_64},
-	}
-
-	osAndInApexMap = map[string]string{
-		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
-		AndroidPlatform:            "//build/bazel/rules/apex:system",
-		Unbundled_app:              "//build/bazel/rules/apex:unbundled_app",
-		OsDarwin:                   "//build/bazel_common_rules/platforms/os:darwin",
-		OsLinux:                    "//build/bazel_common_rules/platforms/os:linux_glibc",
-		osLinuxMusl:                "//build/bazel_common_rules/platforms/os:linux_musl",
-		osLinuxBionic:              "//build/bazel_common_rules/platforms/os:linux_bionic",
-		OsWindows:                  "//build/bazel_common_rules/platforms/os:windows",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-
-	inApexMap = map[string]string{
-		InApex:                     "//build/bazel/rules/apex:in_apex",
-		NonApex:                    "//build/bazel/rules/apex:non_apex",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-
-	errorProneMap = map[string]string{
-		ErrorproneDisabled:         "//build/bazel/rules/java/errorprone:errorprone_globally_disabled",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-
-	// TODO: b/294868620 - Remove when completing the bug
-	sanitizersEnabledMap = map[string]string{
-		SanitizersEnabled:          "//build/bazel/rules/cc:sanitizers_enabled",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-)
-
-// basic configuration types
-type configurationType int
-
-const (
-	noConfig configurationType = iota
-	arch
-	os
-	osArch
-	productVariables
-	osAndInApex
-	inApex
-	errorProneDisabled
-	// TODO: b/294868620 - Remove when completing the bug
-	sanitizersEnabled
-)
-
-func osArchString(os string, arch string) string {
-	return fmt.Sprintf("%s_%s", os, arch)
-}
-
-func (ct configurationType) String() string {
-	return map[configurationType]string{
-		noConfig:           "no_config",
-		arch:               "arch",
-		os:                 "os",
-		osArch:             "arch_os",
-		productVariables:   "product_variables",
-		osAndInApex:        "os_in_apex",
-		inApex:             "in_apex",
-		errorProneDisabled: "errorprone_disabled",
-		// TODO: b/294868620 - Remove when completing the bug
-		sanitizersEnabled: "sanitizers_enabled",
-	}[ct]
-}
-
-func (ct configurationType) validateConfig(config string) {
-	switch ct {
-	case noConfig:
-		if config != "" {
-			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
-		}
-	case arch:
-		if _, ok := platformArchMap[config]; !ok {
-			panic(fmt.Errorf("Unknown arch: %s", config))
-		}
-	case os:
-		if _, ok := platformOsMap[config]; !ok {
-			panic(fmt.Errorf("Unknown os: %s", config))
-		}
-	case osArch:
-		if _, ok := platformOsArchMap[config]; !ok {
-			panic(fmt.Errorf("Unknown os+arch: %s", config))
-		}
-	case productVariables:
-		// do nothing
-	case osAndInApex:
-		// do nothing
-		// this axis can contain additional per-apex keys
-	case inApex:
-		if _, ok := inApexMap[config]; !ok {
-			panic(fmt.Errorf("Unknown in_apex config: %s", config))
-		}
-	case errorProneDisabled:
-		if _, ok := errorProneMap[config]; !ok {
-			panic(fmt.Errorf("Unknown errorprone config: %s", config))
-		}
-	// TODO: b/294868620 - Remove when completing the bug
-	case sanitizersEnabled:
-		if _, ok := sanitizersEnabledMap[config]; !ok {
-			panic(fmt.Errorf("Unknown sanitizers_enabled config: %s", config))
-		}
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
-	}
-}
-
-// SelectKey returns the Bazel select key for a given configurationType and config string.
-func (ca ConfigurationAxis) SelectKey(config string) string {
-	ca.validateConfig(config)
-	switch ca.configurationType {
-	case noConfig:
-		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
-	case arch:
-		return platformArchMap[config]
-	case os:
-		return platformOsMap[config]
-	case osArch:
-		return platformOsArchMap[config]
-	case productVariables:
-		if config == ConditionsDefaultConfigKey {
-			return ConditionsDefaultSelectKey
-		}
-		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
-	case osAndInApex:
-		if ret, exists := osAndInApexMap[config]; exists {
-			return ret
-		}
-		return config
-	case inApex:
-		return inApexMap[config]
-	case errorProneDisabled:
-		return errorProneMap[config]
-	// TODO: b/294868620 - Remove when completing the bug
-	case sanitizersEnabled:
-		return sanitizersEnabledMap[config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
-	}
-}
-
-var (
-	// Indicating there is no configuration axis
-	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
-	// An axis for architecture-specific configurations
-	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
-	// An axis for os-specific configurations
-	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
-	// An axis for arch+os-specific configurations
-	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
-	// An axis for os+in_apex-specific configurations
-	OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
-	// An axis for in_apex-specific configurations
-	InApexAxis = ConfigurationAxis{configurationType: inApex}
-
-	ErrorProneAxis = ConfigurationAxis{configurationType: errorProneDisabled}
-
-	// TODO: b/294868620 - Remove when completing the bug
-	SanitizersEnabledAxis = ConfigurationAxis{configurationType: sanitizersEnabled}
-)
-
-// ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis {
-	return ConfigurationAxis{
-		configurationType: productVariables,
-		subType:           variable,
-		archVariant:       archVariant,
-	}
-}
-
-// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
-// elements within an axis.
-type ConfigurationAxis struct {
-	configurationType
-	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
-	// distinguish between them without needing to list all 17 product variables.
-	subType string
-
-	archVariant bool
-}
-
-func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
-	if ca.configurationType == other.configurationType {
-		return ca.subType < other.subType
-	}
-	return ca.configurationType < other.configurationType
-}
diff --git a/bazel/properties.go b/bazel/properties.go
deleted file mode 100644
index 9c63bc0..0000000
--- a/bazel/properties.go
+++ /dev/null
@@ -1,1467 +0,0 @@
-// Copyright 2020 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 bazel
-
-import (
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"regexp"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-// BazelTargetModuleProperties contain properties and metadata used for
-// Blueprint to BUILD file conversion.
-type BazelTargetModuleProperties struct {
-	// The Bazel rule class for this target.
-	Rule_class string `blueprint:"mutated"`
-
-	// The target label for the bzl file containing the definition of the rule class.
-	Bzl_load_location string `blueprint:"mutated"`
-}
-
-var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
-
-// Label is used to represent a Bazel compatible Label. Also stores the original
-// bp text to support string replacement.
-type Label struct {
-	// The string representation of a Bazel target label. This can be a relative
-	// or fully qualified label. These labels are used for generating BUILD
-	// files with bp2build.
-	Label string
-
-	// The original Soong/Blueprint module name that the label was derived from.
-	// This is used for replacing references to the original name with the new
-	// label, for example in genrule cmds.
-	//
-	// While there is a reversible 1:1 mapping from the module name to Bazel
-	// label with bp2build that could make computing the original module name
-	// from the label automatic, it is not the case for handcrafted targets,
-	// where modules can have a custom label mapping through the { bazel_module:
-	// { label: <label> } } property.
-	//
-	// With handcrafted labels, those modules don't go through bp2build
-	// conversion, but relies on handcrafted targets in the source tree.
-	OriginalModuleName string
-}
-
-// LabelList is used to represent a list of Bazel labels.
-type LabelList struct {
-	Includes []Label
-	Excludes []Label
-}
-
-// MakeLabelList creates a LabelList from a list Label
-func MakeLabelList(labels []Label) LabelList {
-	return LabelList{
-		Includes: labels,
-		Excludes: nil,
-	}
-}
-
-func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(m))
-	for k := range m {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
-}
-
-// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
-// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
-func MakeLabelListFromTargetNames(targetNames []string) LabelList {
-	labels := []Label{}
-	for _, name := range targetNames {
-		label := Label{Label: ":" + name}
-		labels = append(labels, label)
-	}
-	return MakeLabelList(labels)
-}
-
-func (ll *LabelList) Equals(other LabelList) bool {
-	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
-		return false
-	}
-	for i, _ := range ll.Includes {
-		if ll.Includes[i] != other.Includes[i] {
-			return false
-		}
-	}
-	for i, _ := range ll.Excludes {
-		if ll.Excludes[i] != other.Excludes[i] {
-			return false
-		}
-	}
-	return true
-}
-
-func (ll *LabelList) IsNil() bool {
-	return ll.Includes == nil && ll.Excludes == nil
-}
-
-func (ll *LabelList) IsEmpty() bool {
-	return len(ll.Includes) == 0 && len(ll.Excludes) == 0
-}
-
-func (ll *LabelList) deepCopy() LabelList {
-	return LabelList{
-		Includes: ll.Includes[:],
-		Excludes: ll.Excludes[:],
-	}
-}
-
-// uniqueParentDirectories returns a list of the unique parent directories for
-// all files in ll.Includes.
-func (ll *LabelList) uniqueParentDirectories() []string {
-	dirMap := map[string]bool{}
-	for _, label := range ll.Includes {
-		dirMap[filepath.Dir(label.Label)] = true
-	}
-	dirs := []string{}
-	for dir := range dirMap {
-		dirs = append(dirs, dir)
-	}
-	return dirs
-}
-
-// Add inserts the label Label at the end of the LabelList.Includes.
-func (ll *LabelList) Add(label *Label) {
-	if label == nil {
-		return
-	}
-	ll.Includes = append(ll.Includes, *label)
-}
-
-// AddExclude inserts the label Label at the end of the LabelList.Excludes.
-func (ll *LabelList) AddExclude(label *Label) {
-	if label == nil {
-		return
-	}
-	ll.Excludes = append(ll.Excludes, *label)
-}
-
-// Append appends the fields of other labelList to the corresponding fields of ll.
-func (ll *LabelList) Append(other LabelList) {
-	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
-		ll.Includes = append(ll.Includes, other.Includes...)
-	}
-	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
-		ll.Excludes = append(ll.Excludes, other.Excludes...)
-	}
-}
-
-// Partition splits a LabelList into two LabelLists depending on the return value
-// of the predicate.
-// This function preserves the Includes and Excludes, but it does not provide
-// that information to the partition function.
-func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
-	predicated := LabelList{}
-	unpredicated := LabelList{}
-	for _, include := range ll.Includes {
-		if predicate(include) {
-			predicated.Add(&include)
-		} else {
-			unpredicated.Add(&include)
-		}
-	}
-	for _, exclude := range ll.Excludes {
-		if predicate(exclude) {
-			predicated.AddExclude(&exclude)
-		} else {
-			unpredicated.AddExclude(&exclude)
-		}
-	}
-	return predicated, unpredicated
-}
-
-// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
-// the slice in a sorted order.
-func UniqueSortedBazelLabels(originalLabels []Label) []Label {
-	uniqueLabels := FirstUniqueBazelLabels(originalLabels)
-	sort.SliceStable(uniqueLabels, func(i, j int) bool {
-		return uniqueLabels[i].Label < uniqueLabels[j].Label
-	})
-	return uniqueLabels
-}
-
-func FirstUniqueBazelLabels(originalLabels []Label) []Label {
-	var labels []Label
-	found := make(map[string]bool, len(originalLabels))
-	for _, l := range originalLabels {
-		if _, ok := found[l.Label]; ok {
-			continue
-		}
-		labels = append(labels, l)
-		found[l.Label] = true
-	}
-	return labels
-}
-
-func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
-	var uniqueLabelList LabelList
-	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
-	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
-	return uniqueLabelList
-}
-
-func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
-	var uniqueLabelList LabelList
-	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
-	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
-	return uniqueLabelList
-}
-
-// Subtract needle from haystack
-func SubtractStrings(haystack []string, needle []string) []string {
-	// This is really a set
-	needleMap := make(map[string]bool)
-	for _, s := range needle {
-		needleMap[s] = true
-	}
-
-	var strings []string
-	for _, s := range haystack {
-		if exclude := needleMap[s]; !exclude {
-			strings = append(strings, s)
-		}
-	}
-
-	return strings
-}
-
-// Subtract needle from haystack
-func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
-	// This is really a set
-	needleMap := make(map[Label]bool)
-	for _, s := range needle {
-		needleMap[s] = true
-	}
-
-	var labels []Label
-	for _, label := range haystack {
-		if exclude := needleMap[label]; !exclude {
-			labels = append(labels, label)
-		}
-	}
-
-	return labels
-}
-
-// Appends two LabelLists, returning the combined list.
-func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
-	var result LabelList
-	result.Includes = append(a.Includes, b.Includes...)
-	result.Excludes = append(a.Excludes, b.Excludes...)
-	return result
-}
-
-// Subtract needle from haystack
-func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
-	var result LabelList
-	result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
-	// NOTE: Excludes are intentionally not subtracted
-	result.Excludes = haystack.Excludes
-	return result
-}
-
-// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for
-// each axis/configuration by keeping the first instance of a Label and omitting all subsequent
-// repetitions.
-func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute {
-	var result LabelListAttribute
-	result.Value = FirstUniqueBazelLabelList(attr.Value)
-	if attr.HasConfigurableValues() {
-		result.ConfigurableValues = make(configurableLabelLists)
-	}
-	for axis, configToLabels := range attr.ConfigurableValues {
-		for c, l := range configToLabels {
-			result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l))
-		}
-	}
-
-	return result
-}
-
-// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each
-// axis/configuration.
-func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
-	var result LabelListAttribute
-	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
-	if haystack.HasConfigurableValues() {
-		result.ConfigurableValues = make(configurableLabelLists)
-	}
-	for axis, configToLabels := range haystack.ConfigurableValues {
-		for haystackConfig, haystackLabels := range configToLabels {
-			result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig)))
-		}
-	}
-
-	return result
-}
-
-type Attribute interface {
-	HasConfigurableValues() bool
-}
-
-type labelSelectValues map[string]*Label
-
-type configurableLabels map[ConfigurationAxis]labelSelectValues
-
-func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
-	if cl[axis] == nil {
-		cl[axis] = make(labelSelectValues)
-	}
-	cl[axis][config] = value
-}
-
-// Represents an attribute whose value is a single label
-type LabelAttribute struct {
-	Value *Label
-
-	ConfigurableValues configurableLabels
-}
-
-func (la *LabelAttribute) axisTypes() map[configurationType]bool {
-	types := map[configurationType]bool{}
-	for k := range la.ConfigurableValues {
-		if len(la.ConfigurableValues[k]) > 0 {
-			types[k.configurationType] = true
-		}
-	}
-	return types
-}
-
-// Collapse reduces the configurable axes of the label attribute to a single axis.
-// This is necessary for final writing to bp2build, as a configurable label
-// attribute can only be comprised by a single select.
-func (la *LabelAttribute) Collapse() error {
-	axisTypes := la.axisTypes()
-	_, containsOs := axisTypes[os]
-	_, containsArch := axisTypes[arch]
-	_, containsOsArch := axisTypes[osArch]
-	_, containsProductVariables := axisTypes[productVariables]
-	if containsProductVariables {
-		if containsOs || containsArch || containsOsArch {
-			if containsArch {
-				allProductVariablesAreArchVariant := true
-				for k := range la.ConfigurableValues {
-					if k.configurationType == productVariables && !k.archVariant {
-						allProductVariablesAreArchVariant = false
-					}
-				}
-				if !allProductVariablesAreArchVariant {
-					return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
-				}
-			} else {
-				return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
-			}
-		}
-	}
-	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
-		// If a bool attribute has both os and arch configuration axes, the only
-		// way to successfully union their values is to increase the granularity
-		// of the configuration criteria to os_arch.
-		for osType, supportedArchs := range osToArchMap {
-			for _, supportedArch := range supportedArchs {
-				osArch := osArchString(osType, supportedArch)
-				if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
-					// Do nothing, as the arch_os is explicitly defined already.
-				} else {
-					archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
-					osVal := la.SelectValue(OsConfigurationAxis, osType)
-					if osVal != nil && archVal != nil {
-						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
-						// runs after os mutator.
-						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
-					} else if osVal != nil && archVal == nil {
-						la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
-					} else if osVal == nil && archVal != nil {
-						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
-					}
-				}
-			}
-		}
-		// All os_arch values are now set. Clear os and arch axes.
-		delete(la.ConfigurableValues, ArchConfigurationAxis)
-		delete(la.ConfigurableValues, OsConfigurationAxis)
-	}
-	return nil
-}
-
-// HasConfigurableValues returns whether there are configurable values set for this label.
-func (la LabelAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range la.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// SetValue sets the base, non-configured value for the Label
-func (la *LabelAttribute) SetValue(value Label) {
-	la.SetSelectValue(NoConfigAxis, "", value)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		la.Value = &value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		if la.ConfigurableValues == nil {
-			la.ConfigurableValues = make(configurableLabels)
-		}
-		la.ConfigurableValues.setValueForAxis(axis, config, &value)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return la.Value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		return la.ConfigurableValues[axis][config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(la.ConfigurableValues)
-}
-
-// MakeLabelAttribute turns a string into a LabelAttribute
-func MakeLabelAttribute(label string) *LabelAttribute {
-	return &LabelAttribute{
-		Value: &Label{
-			Label: label,
-		},
-	}
-}
-
-type configToBools map[string]bool
-
-func (ctb configToBools) setValue(config string, value *bool) {
-	if value == nil {
-		if _, ok := ctb[config]; ok {
-			delete(ctb, config)
-		}
-		return
-	}
-	ctb[config] = *value
-}
-
-type configurableBools map[ConfigurationAxis]configToBools
-
-func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
-	if cb[axis] == nil {
-		cb[axis] = make(configToBools)
-	}
-	cb[axis].setValue(config, value)
-}
-
-// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
-type BoolAttribute struct {
-	Value *bool
-
-	ConfigurableValues configurableBools
-}
-
-// HasConfigurableValues returns whether there are configurable values for this attribute.
-func (ba BoolAttribute) HasConfigurableValues() bool {
-	for _, cfgToBools := range ba.ConfigurableValues {
-		if len(cfgToBools) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// SetValue sets value for the no config axis
-func (ba *BoolAttribute) SetValue(value *bool) {
-	ba.SetSelectValue(NoConfigAxis, "", value)
-}
-
-// SetSelectValue sets value for the given axis/config.
-func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		ba.Value = value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		if ba.ConfigurableValues == nil {
-			ba.ConfigurableValues = make(configurableBools)
-		}
-		ba.ConfigurableValues.setValueForAxis(axis, config, value)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// ToLabelListAttribute creates and returns a LabelListAttribute from this
-// bool attribute, where each bool in this attribute corresponds to a
-// label list value in the resultant attribute.
-func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
-	getLabelList := func(boolPtr *bool) LabelList {
-		if boolPtr == nil {
-			return LabelList{nil, nil}
-		} else if *boolPtr {
-			return trueVal
-		} else {
-			return falseVal
-		}
-	}
-
-	mainVal := getLabelList(ba.Value)
-	if !ba.HasConfigurableValues() {
-		return MakeLabelListAttribute(mainVal), nil
-	}
-
-	result := LabelListAttribute{}
-	if err := ba.Collapse(); err != nil {
-		return result, err
-	}
-
-	for axis, configToBools := range ba.ConfigurableValues {
-		if len(configToBools) < 1 {
-			continue
-		}
-		for config, boolPtr := range configToBools {
-			val := getLabelList(&boolPtr)
-			if !val.Equals(mainVal) {
-				result.SetSelectValue(axis, config, val)
-			}
-		}
-		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
-	}
-
-	return result, nil
-}
-
-// ToStringListAttribute creates a StringListAttribute from this BoolAttribute,
-// where each bool corresponds to a string list value generated by the provided
-// function.
-// TODO(b/271425661): Generalize this
-func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) {
-	mainVal := valueFunc(ba.Value, NoConfigAxis, "")
-	if !ba.HasConfigurableValues() {
-		return MakeStringListAttribute(mainVal), nil
-	}
-
-	result := StringListAttribute{}
-	if err := ba.Collapse(); err != nil {
-		return result, err
-	}
-
-	for axis, configToBools := range ba.ConfigurableValues {
-		if len(configToBools) < 1 {
-			continue
-		}
-		for config, boolPtr := range configToBools {
-			val := valueFunc(&boolPtr, axis, config)
-			if !reflect.DeepEqual(val, mainVal) {
-				result.SetSelectValue(axis, config, val)
-			}
-		}
-		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
-	}
-
-	return result, nil
-}
-
-// Collapse reduces the configurable axes of the boolean attribute to a single axis.
-// This is necessary for final writing to bp2build, as a configurable boolean
-// attribute can only be comprised by a single select.
-func (ba *BoolAttribute) Collapse() error {
-	axisTypes := ba.axisTypes()
-	_, containsOs := axisTypes[os]
-	_, containsArch := axisTypes[arch]
-	_, containsOsArch := axisTypes[osArch]
-	_, containsProductVariables := axisTypes[productVariables]
-	if containsProductVariables {
-		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
-		}
-	}
-	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
-		// If a bool attribute has both os and arch configuration axes, the only
-		// way to successfully union their values is to increase the granularity
-		// of the configuration criteria to os_arch.
-		for osType, supportedArchs := range osToArchMap {
-			for _, supportedArch := range supportedArchs {
-				osArch := osArchString(osType, supportedArch)
-				if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
-					// Do nothing, as the arch_os is explicitly defined already.
-				} else {
-					archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
-					osVal := ba.SelectValue(OsConfigurationAxis, osType)
-					if osVal != nil && archVal != nil {
-						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
-						// runs after os mutator.
-						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					} else if osVal != nil && archVal == nil {
-						ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
-					} else if osVal == nil && archVal != nil {
-						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					}
-				}
-			}
-		}
-		// All os_arch values are now set. Clear os and arch axes.
-		delete(ba.ConfigurableValues, ArchConfigurationAxis)
-		delete(ba.ConfigurableValues, OsConfigurationAxis)
-		// Verify post-condition; this should never fail, provided no additional
-		// axes are introduced.
-		if len(ba.ConfigurableValues) > 1 {
-			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
-		}
-	}
-	return nil
-}
-
-func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
-	types := map[configurationType]bool{}
-	for k := range ba.ConfigurableValues {
-		if len(ba.ConfigurableValues[k]) > 0 {
-			types[k.configurationType] = true
-		}
-	}
-	return types
-}
-
-// SelectValue gets the value for the given axis/config.
-func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return ba.Value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		if v, ok := ba.ConfigurableValues[axis][config]; ok {
-			return &v
-		} else {
-			return nil
-		}
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(ba.ConfigurableValues)
-}
-
-// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
-type labelListSelectValues map[string]LabelList
-
-func (ll labelListSelectValues) addSelects(label labelSelectValues) {
-	for k, v := range label {
-		if label == nil {
-			continue
-		}
-		l := ll[k]
-		(&l).Add(v)
-		ll[k] = l
-	}
-}
-
-func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
-	for k, v := range other {
-		l := ll[k]
-		if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
-			l.Includes = []Label{}
-		}
-		(&l).Append(v)
-		ll[k] = l
-	}
-}
-
-// HasConfigurableValues returns whether there are configurable values within this set of selects.
-func (ll labelListSelectValues) HasConfigurableValues() bool {
-	for _, v := range ll {
-		if v.Includes != nil {
-			return true
-		}
-	}
-	return false
-}
-
-// LabelListAttribute is used to represent a list of Bazel labels as an
-// attribute.
-type LabelListAttribute struct {
-	// The non-configured attribute label list Value. Required.
-	Value LabelList
-
-	// The configured attribute label list Values. Optional
-	// a map of independent configurability axes
-	ConfigurableValues configurableLabelLists
-
-	// If true, differentiate between "nil" and "empty" list. nil means that
-	// this attribute should not be specified at all, and "empty" means that
-	// the attribute should be explicitly specified as an empty list.
-	// This mode facilitates use of attribute defaults: an empty list should
-	// override the default.
-	ForceSpecifyEmptyList bool
-
-	// If true, signal the intent to the code generator to emit all select keys,
-	// even if the Includes list for that key is empty. This mode facilitates
-	// specific select statements where an empty list for a non-default select
-	// key has a meaning.
-	EmitEmptyList bool
-
-	// If a property has struct tag "variant_prepend", this value should
-	// be set to True, so that when bp2build generates BUILD.bazel, variant
-	// properties(select ...) come before general properties.
-	Prepend bool
-}
-
-type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
-
-func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
-	if list.IsNil() {
-		if _, ok := cll[axis][config]; ok {
-			delete(cll[axis], config)
-		}
-		return
-	}
-	if cll[axis] == nil {
-		cll[axis] = make(labelListSelectValues)
-	}
-
-	cll[axis][config] = list
-}
-
-func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
-	for axis, otherSelects := range other {
-		selects := cll[axis]
-		if selects == nil {
-			selects = make(labelListSelectValues, len(otherSelects))
-		}
-		selects.appendSelects(otherSelects, forceSpecifyEmptyList)
-		cll[axis] = selects
-	}
-}
-
-func (lla *LabelListAttribute) Clone() *LabelListAttribute {
-	result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
-	return result.Append(*lla)
-}
-
-// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
-func MakeLabelListAttribute(value LabelList) LabelListAttribute {
-	return LabelListAttribute{
-		Value:              value,
-		ConfigurableValues: make(configurableLabelLists),
-	}
-}
-
-// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
-func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
-	return MakeLabelListAttribute(MakeLabelList([]Label{value}))
-}
-
-func (lla *LabelListAttribute) SetValue(list LabelList) {
-	lla.SetSelectValue(NoConfigAxis, "", list)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		lla.Value = list
-	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
-		if lla.ConfigurableValues == nil {
-			lla.ConfigurableValues = make(configurableLabelLists)
-		}
-		lla.ConfigurableValues.setValueForAxis(axis, config, list)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return lla.Value
-	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
-		return lla.ConfigurableValues[axis][config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(lla.ConfigurableValues)
-}
-
-// Append all values, including os and arch specific ones, from another
-// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
-func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
-	forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
-	if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
-		lla.Value.Includes = []Label{}
-	}
-	lla.Value.Append(other.Value)
-	if lla.ConfigurableValues == nil {
-		lla.ConfigurableValues = make(configurableLabelLists)
-	}
-	lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
-	return lla
-}
-
-// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
-// LabelList within the LabelListAttribute
-func (lla *LabelListAttribute) Add(label *LabelAttribute) {
-	if label == nil {
-		return
-	}
-
-	lla.Value.Add(label.Value)
-	if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
-		lla.ConfigurableValues = make(configurableLabelLists)
-	}
-	for axis, _ := range label.ConfigurableValues {
-		if _, exists := lla.ConfigurableValues[axis]; !exists {
-			lla.ConfigurableValues[axis] = make(labelListSelectValues)
-		}
-		lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
-	}
-}
-
-// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
-func (lla LabelListAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range lla.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
-func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
-	for _, values := range lla.ConfigurableValues[axis] {
-		if !values.IsNil() {
-			return true
-		}
-	}
-	return false
-}
-
-// IsEmpty returns true if the attribute has no values under any configuration.
-func (lla LabelListAttribute) IsEmpty() bool {
-	if len(lla.Value.Includes) > 0 {
-		return false
-	}
-	for axis, _ := range lla.ConfigurableValues {
-		if lla.ConfigurableValues[axis].HasConfigurableValues() {
-			return false
-		}
-	}
-	return true
-}
-
-// IsNil returns true if the attribute has not been set for any configuration.
-func (lla LabelListAttribute) IsNil() bool {
-	if lla.Value.Includes != nil {
-		return false
-	}
-	return !lla.HasConfigurableValues()
-}
-
-// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
-// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
-// be removed, e.g. if they could cause duplicate element failures.
-func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
-	val := lla.SelectValue(axis, config)
-	newList := SubtractBazelLabelList(val, labelList)
-	newList.Excludes = append(newList.Excludes, labelList.Includes...)
-	lla.SetSelectValue(axis, config, newList)
-}
-
-// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
-// the base value and included in default values as appropriate.
-func (lla *LabelListAttribute) ResolveExcludes() {
-	// If there are OsAndInApexAxis, we need to use
-	//   * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
-	//     included in non-Android OSes
-	//   * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
-	//     be included in the non-Android OSes
-	if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
-		inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
-		for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
-			// OsAndroid has already handled its excludes.
-			// We only need to copy the excludes from other arches, so if there are none, skip it.
-			if config == OsAndroid || len(labels.Excludes) == 0 {
-				continue
-			}
-			lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
-				Includes: inApexLabels.Includes,
-				Excludes: labels.Excludes,
-			}
-		}
-	}
-
-	for axis, configToLabels := range lla.ConfigurableValues {
-		baseLabels := lla.Value.deepCopy()
-		for config, val := range configToLabels {
-			// Exclude config-specific excludes from base value
-			lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
-
-			// add base values to config specific to add labels excluded by others in this axis
-			// then remove all config-specific excludes
-			allLabels := baseLabels.deepCopy()
-			allLabels.Append(val)
-			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
-		}
-
-		// After going through all configs, delete the duplicates in the config
-		// values that are already in the base Value.
-		for config, val := range configToLabels {
-			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
-		}
-
-		// Now that the Value list is finalized for this axis, compare it with
-		// the original list, and union the difference with the default
-		// condition for the axis.
-		difference := SubtractBazelLabelList(baseLabels, lla.Value)
-		existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
-		existingDefaults.Append(difference)
-		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
-
-		// if everything ends up without includes, just delete the axis
-		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
-			delete(lla.ConfigurableValues, axis)
-		}
-	}
-}
-
-// Partition splits a LabelListAttribute into two LabelListAttributes depending
-// on the return value of the predicate.
-// This function preserves the Includes and Excludes, but it does not provide
-// that information to the partition function.
-func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
-	predicated := LabelListAttribute{}
-	unpredicated := LabelListAttribute{}
-
-	valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
-	predicated.SetValue(valuePartitionTrue)
-	unpredicated.SetValue(valuePartitionFalse)
-
-	for axis, selectValueLabelLists := range lla.ConfigurableValues {
-		for config, labelList := range selectValueLabelLists {
-			configPredicated, configUnpredicated := labelList.Partition(predicate)
-			predicated.SetSelectValue(axis, config, configPredicated)
-			unpredicated.SetSelectValue(axis, config, configUnpredicated)
-		}
-	}
-
-	return predicated, unpredicated
-}
-
-// OtherModuleContext is a limited context that has methods with information about other modules.
-type OtherModuleContext interface {
-	ModuleFromName(name string) (blueprint.Module, bool)
-	OtherModuleType(m blueprint.Module) string
-	OtherModuleName(m blueprint.Module) string
-	OtherModuleDir(m blueprint.Module) string
-	ModuleErrorf(fmt string, args ...interface{})
-}
-
-// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
-// label and whether it was changed.
-type LabelMapper func(OtherModuleContext, Label) (string, bool)
-
-// LabelPartition contains descriptions of a partition for labels
-type LabelPartition struct {
-	// Extensions to include in this partition
-	Extensions []string
-	// LabelMapper is a function that can map a label to a new label, and indicate whether to include
-	// the mapped label in the partition
-	LabelMapper LabelMapper
-	// Whether to store files not included in any other partition in a group of LabelPartitions
-	// Only one partition in a group of LabelPartitions can enabled Keep_remainder
-	Keep_remainder bool
-}
-
-// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
-// partition
-type LabelPartitions map[string]LabelPartition
-
-// filter returns a pointer to a label if the label should be included in the partition or nil if
-// not.
-func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
-	if lf.LabelMapper != nil {
-		if newLabel, changed := lf.LabelMapper(ctx, label); changed {
-			return &Label{newLabel, label.OriginalModuleName}
-		}
-	}
-	for _, ext := range lf.Extensions {
-		if strings.HasSuffix(label.Label, ext) {
-			return &label
-		}
-	}
-
-	return nil
-}
-
-// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
-type PartitionToLabelListAttribute map[string]LabelListAttribute
-
-type partitionToLabelList map[string]*LabelList
-
-func (p partitionToLabelList) appendIncludes(partition string, label Label) {
-	if _, ok := p[partition]; !ok {
-		p[partition] = &LabelList{}
-	}
-	p[partition].Includes = append(p[partition].Includes, label)
-}
-
-func (p partitionToLabelList) excludes(partition string, excludes []Label) {
-	if _, ok := p[partition]; !ok {
-		p[partition] = &LabelList{}
-	}
-	p[partition].Excludes = excludes
-}
-
-// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
-func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
-	ret := PartitionToLabelListAttribute{}
-	var partitionNames []string
-	// Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
-	var remainderPartition *string
-	for p, f := range partitions {
-		partitionNames = append(partitionNames, p)
-		if f.Keep_remainder {
-			if remainderPartition != nil {
-				panic("only one partition can store the remainder")
-			}
-			// If we take the address of p in a loop, we'll end up with the last value of p in
-			// remainderPartition, we want the requested partition
-			capturePartition := p
-			remainderPartition = &capturePartition
-		}
-	}
-
-	partitionLabelList := func(axis ConfigurationAxis, config string) {
-		value := lla.SelectValue(axis, config)
-		partitionToLabels := partitionToLabelList{}
-		for _, item := range value.Includes {
-			wasFiltered := false
-			var inPartition *string
-			for partition, f := range partitions {
-				filtered := f.filter(ctx, item)
-				if filtered == nil {
-					// did not match this filter, keep looking
-					continue
-				}
-				wasFiltered = true
-				partitionToLabels.appendIncludes(partition, *filtered)
-				// don't need to check other partitions if this filter used the item,
-				// continue checking if mapped to another name
-				if *filtered == item {
-					if inPartition != nil {
-						ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
-					}
-					capturePartition := partition
-					inPartition = &capturePartition
-				}
-			}
-
-			// if not specified in a partition, add to remainder partition if one exists
-			if !wasFiltered && remainderPartition != nil {
-				partitionToLabels.appendIncludes(*remainderPartition, item)
-			}
-		}
-
-		// ensure empty lists are maintained
-		if value.Excludes != nil {
-			for _, partition := range partitionNames {
-				partitionToLabels.excludes(partition, value.Excludes)
-			}
-		}
-
-		for partition, list := range partitionToLabels {
-			val := ret[partition]
-			(&val).SetSelectValue(axis, config, *list)
-			ret[partition] = val
-		}
-	}
-
-	partitionLabelList(NoConfigAxis, "")
-	for axis, configToList := range lla.ConfigurableValues {
-		for config, _ := range configToList {
-			partitionLabelList(axis, config)
-		}
-	}
-	return ret
-}
-
-// StringAttribute corresponds to the string Bazel attribute type with
-// support for additional metadata, like configurations.
-type StringAttribute struct {
-	// The base value of the string attribute.
-	Value *string
-
-	// The configured attribute label list Values. Optional
-	// a map of independent configurability axes
-	ConfigurableValues configurableStrings
-}
-
-type configurableStrings map[ConfigurationAxis]stringSelectValues
-
-func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
-	if cs[axis] == nil {
-		cs[axis] = make(stringSelectValues)
-	}
-	cs[axis][config] = str
-}
-
-type stringSelectValues map[string]*string
-
-// HasConfigurableValues returns true if the attribute contains axis-specific string values.
-func (sa StringAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range sa.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// SetValue sets the base, non-configured value for the Label
-func (sa *StringAttribute) SetValue(value string) {
-	sa.SetSelectValue(NoConfigAxis, "", &value)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		sa.Value = str
-	case arch, os, osArch, productVariables, sanitizersEnabled:
-		if sa.ConfigurableValues == nil {
-			sa.ConfigurableValues = make(configurableStrings)
-		}
-		sa.ConfigurableValues.setValueForAxis(axis, config, str)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return sa.Value
-	case arch, os, osArch, productVariables, sanitizersEnabled:
-		if v, ok := sa.ConfigurableValues[axis][config]; ok {
-			return v
-		} else {
-			return nil
-		}
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(sa.ConfigurableValues)
-}
-
-// Collapse reduces the configurable axes of the string attribute to a single axis.
-// This is necessary for final writing to bp2build, as a configurable string
-// attribute can only be comprised by a single select.
-func (sa *StringAttribute) Collapse() error {
-	axisTypes := sa.axisTypes()
-	_, containsOs := axisTypes[os]
-	_, containsArch := axisTypes[arch]
-	_, containsOsArch := axisTypes[osArch]
-	_, containsProductVariables := axisTypes[productVariables]
-	if containsProductVariables {
-		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
-		}
-	}
-	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
-		// If a bool attribute has both os and arch configuration axes, the only
-		// way to successfully union their values is to increase the granularity
-		// of the configuration criteria to os_arch.
-		for osType, supportedArchs := range osToArchMap {
-			for _, supportedArch := range supportedArchs {
-				osArch := osArchString(osType, supportedArch)
-				if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
-					// Do nothing, as the arch_os is explicitly defined already.
-				} else {
-					archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
-					osVal := sa.SelectValue(OsConfigurationAxis, osType)
-					if osVal != nil && archVal != nil {
-						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
-						// runs after os mutator.
-						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					} else if osVal != nil && archVal == nil {
-						sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
-					} else if osVal == nil && archVal != nil {
-						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					}
-				}
-			}
-		}
-		/// All os_arch values are now set. Clear os and arch axes.
-		delete(sa.ConfigurableValues, ArchConfigurationAxis)
-		delete(sa.ConfigurableValues, OsConfigurationAxis)
-		// Verify post-condition; this should never fail, provided no additional
-		// axes are introduced.
-		if len(sa.ConfigurableValues) > 1 {
-			panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
-		}
-	} else if containsProductVariables {
-		usedBaseValue := false
-		for a, configToProp := range sa.ConfigurableValues {
-			if a.configurationType == productVariables {
-				for c, p := range configToProp {
-					if p == nil {
-						sa.SetSelectValue(a, c, sa.Value)
-						usedBaseValue = true
-					}
-				}
-			}
-		}
-		if usedBaseValue {
-			sa.Value = nil
-		}
-	}
-	return nil
-}
-
-func (sa *StringAttribute) axisTypes() map[configurationType]bool {
-	types := map[configurationType]bool{}
-	for k := range sa.ConfigurableValues {
-		if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
-			types[k.configurationType] = true
-		}
-	}
-	return types
-}
-
-// StringListAttribute corresponds to the string_list Bazel attribute type with
-// support for additional metadata, like configurations.
-type StringListAttribute struct {
-	// The base value of the string list attribute.
-	Value []string
-
-	// The configured attribute label list Values. Optional
-	// a map of independent configurability axes
-	ConfigurableValues configurableStringLists
-
-	// If a property has struct tag "variant_prepend", this value should
-	// be set to True, so that when bp2build generates BUILD.bazel, variant
-	// properties(select ...) come before general properties.
-	Prepend bool
-}
-
-// IsEmpty returns true if the attribute has no values under any configuration.
-func (sla StringListAttribute) IsEmpty() bool {
-	return len(sla.Value) == 0 && !sla.HasConfigurableValues()
-}
-
-type configurableStringLists map[ConfigurationAxis]stringListSelectValues
-
-func (csl configurableStringLists) Append(other configurableStringLists) {
-	for axis, otherSelects := range other {
-		selects := csl[axis]
-		if selects == nil {
-			selects = make(stringListSelectValues, len(otherSelects))
-		}
-		selects.appendSelects(otherSelects)
-		csl[axis] = selects
-	}
-}
-
-func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
-	if csl[axis] == nil {
-		csl[axis] = make(stringListSelectValues)
-	}
-	csl[axis][config] = list
-}
-
-type stringListSelectValues map[string][]string
-
-func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
-	for k, v := range other {
-		sl[k] = append(sl[k], v...)
-	}
-}
-
-func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
-	for _, val := range sl {
-		if len(val) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
-func MakeStringListAttribute(value []string) StringListAttribute {
-	// NOTE: These strings are not necessarily unique or sorted.
-	return StringListAttribute{
-		Value:              value,
-		ConfigurableValues: make(configurableStringLists),
-	}
-}
-
-// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
-func (sla StringListAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range sla.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// Append appends all values, including os and arch specific ones, from another
-// StringListAttribute to this StringListAttribute
-func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
-	sla.Value = append(sla.Value, other.Value...)
-	if sla.ConfigurableValues == nil {
-		sla.ConfigurableValues = make(configurableStringLists)
-	}
-	sla.ConfigurableValues.Append(other.ConfigurableValues)
-	return sla
-}
-
-func (sla *StringListAttribute) Clone() *StringListAttribute {
-	result := &StringListAttribute{}
-	return result.Append(*sla)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		sla.Value = list
-	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
-		if sla.ConfigurableValues == nil {
-			sla.ConfigurableValues = make(configurableStringLists)
-		}
-		sla.ConfigurableValues.setValueForAxis(axis, config, list)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return sla.Value
-	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
-		return sla.ConfigurableValues[axis][config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(sla.ConfigurableValues)
-}
-
-// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
-// configuration-specific values. For example, if we would convert this StringListAttribute as:
-//
-//	["a", "b", "c"] + select({
-//	   "//condition:one": ["a", "d"],
-//	   "//conditions:default": [],
-//	})
-//
-// after this function, we would convert this StringListAttribute as:
-//
-//	["a", "b", "c"] + select({
-//	   "//condition:one": ["d"],
-//	   "//conditions:default": [],
-//	})
-func (sla *StringListAttribute) DeduplicateAxesFromBase() {
-	base := sla.Value
-	for axis, configToList := range sla.ConfigurableValues {
-		for config, list := range configToList {
-			remaining := SubtractStrings(list, base)
-			if len(remaining) == 0 {
-				delete(sla.ConfigurableValues[axis], config)
-			} else {
-				sla.ConfigurableValues[axis][config] = remaining
-			}
-		}
-	}
-}
-
-// TryVariableSubstitution, replace string substitution formatting within each string in slice with
-// Starlark string.format compatible tag for productVariable.
-func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
-	if len(slice) == 0 {
-		return slice, false
-	}
-	ret := make([]string, 0, len(slice))
-	changesMade := false
-	for _, s := range slice {
-		newS, changed := TryVariableSubstitution(s, productVariable)
-		ret = append(ret, newS)
-		changesMade = changesMade || changed
-	}
-	return ret, changesMade
-}
-
-// TryVariableSubstitution, replace string substitution formatting within s with Starlark
-// string.format compatible tag for productVariable.
-func TryVariableSubstitution(s string, productVariable string) (string, bool) {
-	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
-	return sub, s != sub
-}
-
-// StringMapAttribute is a map of strings.
-// The use case for this is storing the flag_values in a config_setting object.
-// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules.
-type StringMapAttribute map[string]string
-
-// ConfigSettingAttributes stores the keys of a config_setting object.
-type ConfigSettingAttributes struct {
-	// Each key in Flag_values is a label to a custom string_setting
-	Flag_values StringMapAttribute
-	// Each element in Constraint_values is a label to a constraint_value
-	Constraint_values LabelListAttribute
-}
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
deleted file mode 100644
index 751cb8b..0000000
--- a/bazel/properties_test.go
+++ /dev/null
@@ -1,836 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bazel
-
-import (
-	"reflect"
-	"strings"
-	"testing"
-
-	"github.com/google/blueprint/proptools"
-)
-
-func TestUniqueBazelLabels(t *testing.T) {
-	testCases := []struct {
-		originalLabels       []Label
-		expectedUniqueLabels []Label
-	}{
-		{
-			originalLabels: []Label{
-				{Label: "a"},
-				{Label: "b"},
-				{Label: "a"},
-				{Label: "c"},
-				// namespaces
-				{Label: "//foo:bar", OriginalModuleName: "bar"},       // when referenced from foo namespace
-				{Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when reference from root namespace
-			},
-			expectedUniqueLabels: []Label{
-				{Label: "//foo:bar", OriginalModuleName: "bar"},
-				{Label: "a"},
-				{Label: "b"},
-				{Label: "c"},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabels := UniqueSortedBazelLabels(tc.originalLabels)
-		if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels)
-		}
-	}
-}
-
-func TestSubtractStrings(t *testing.T) {
-	testCases := []struct {
-		haystack       []string
-		needle         []string
-		expectedResult []string
-	}{
-		{
-			haystack: []string{
-				"a",
-				"b",
-				"c",
-			},
-			needle: []string{
-				"a",
-			},
-			expectedResult: []string{
-				"b", "c",
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualResult := SubtractStrings(tc.haystack, tc.needle)
-		if !reflect.DeepEqual(tc.expectedResult, actualResult) {
-			t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
-		}
-	}
-}
-
-func TestSubtractBazelLabelList(t *testing.T) {
-	testCases := []struct {
-		haystack       LabelList
-		needle         LabelList
-		expectedResult LabelList
-	}{
-		{
-			haystack: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "c"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-			needle: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-				},
-				Excludes: []Label{
-					{Label: "z"},
-				},
-			},
-			// NOTE: Excludes are intentionally not subtracted
-			expectedResult: LabelList{
-				Includes: []Label{
-					{Label: "b"},
-					{Label: "c"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualResult := SubtractBazelLabelList(tc.haystack, tc.needle)
-		if !reflect.DeepEqual(tc.expectedResult, actualResult) {
-			t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
-		}
-	}
-}
-
-func TestSubtractBazelLabelListAttribute(t *testing.T) {
-	testCases := []struct {
-		haystack LabelListAttribute
-		needle   LabelListAttribute
-		expected LabelListAttribute
-	}{
-		{
-			haystack: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"a", "b", "a", "c"},
-					[]string{"x", "x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"arm_1", "arm_2"}, []string{}),
-						"x86": makeLabelList([]string{"x86_3", "x86_4", "x86_5"}, []string{"x86_5"}),
-					},
-				},
-			},
-			needle: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"d", "a"},
-					[]string{"x", "y2", "z2"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"arm_1", "arm_3"}, []string{}),
-						"x86": makeLabelList([]string{"x86_3", "x86_4"}, []string{"x86_6"}),
-					},
-				},
-			},
-			expected: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"b", "c"},
-					[]string{"x", "x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"arm_2"}, []string{}),
-						"x86": makeLabelList([]string{"x86_5"}, []string{"x86_5"}),
-					},
-				},
-				ForceSpecifyEmptyList: false,
-				EmitEmptyList:         false,
-				Prepend:               false,
-			},
-		},
-	}
-	for _, tc := range testCases {
-		got := SubtractBazelLabelListAttribute(tc.haystack, tc.needle)
-		if !reflect.DeepEqual(tc.expected, got) {
-			t.Fatalf("Expected\n%v, but got\n%v", tc.expected, got)
-		}
-	}
-}
-
-func TestFirstUniqueBazelLabelList(t *testing.T) {
-	testCases := []struct {
-		originalLabelList       LabelList
-		expectedUniqueLabelList LabelList
-	}{
-		{
-			originalLabelList: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "a"},
-					{Label: "c"},
-					// namespaces
-					{Label: "//foo:bar", OriginalModuleName: "bar"},       // when referenced from foo namespace
-					{Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when referenced from root namespace
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-			expectedUniqueLabelList: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "c"},
-					{Label: "//foo:bar", OriginalModuleName: "bar"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList)
-		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
-		}
-	}
-}
-
-func TestFirstUniqueBazelLabelListAttribute(t *testing.T) {
-	testCases := []struct {
-		originalLabelList       LabelListAttribute
-		expectedUniqueLabelList LabelListAttribute
-	}{
-		{
-			originalLabelList: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"a", "b", "a", "c"},
-					[]string{"x", "x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"1", "2", "1"}, []string{}),
-						"x86": makeLabelList([]string{"3", "4", "4"}, []string{"5", "5"}),
-					},
-				},
-			},
-			expectedUniqueLabelList: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"a", "b", "c"},
-					[]string{"x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"1", "2"}, []string{}),
-						"x86": makeLabelList([]string{"3", "4"}, []string{"5"}),
-					},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabelList := FirstUniqueBazelLabelListAttribute(tc.originalLabelList)
-		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
-		}
-	}
-}
-
-func TestUniqueSortedBazelLabelList(t *testing.T) {
-	testCases := []struct {
-		originalLabelList       LabelList
-		expectedUniqueLabelList LabelList
-	}{
-		{
-			originalLabelList: LabelList{
-				Includes: []Label{
-					{Label: "c"},
-					{Label: "a"},
-					{Label: "a"},
-					{Label: "b"},
-				},
-				Excludes: []Label{
-					{Label: "y"},
-					{Label: "z"},
-					{Label: "x"},
-					{Label: "x"},
-				},
-			},
-			expectedUniqueLabelList: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "c"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList)
-		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
-		}
-	}
-}
-
-func makeLabels(labels ...string) []Label {
-	var ret []Label
-	for _, l := range labels {
-		ret = append(ret, Label{Label: l})
-	}
-	return ret
-}
-
-func makeLabelList(includes, excludes []string) LabelList {
-	return LabelList{
-		Includes: makeLabels(includes...),
-		Excludes: makeLabels(excludes...),
-	}
-}
-
-func TestResolveExcludes(t *testing.T) {
-	attr := LabelListAttribute{
-		Value: makeLabelList(
-			[]string{
-				"all_include",
-				"arm_exclude",
-				"android_exclude",
-				"product_config_exclude",
-			},
-			[]string{"all_exclude"},
-		),
-		ConfigurableValues: configurableLabelLists{
-			ArchConfigurationAxis: labelListSelectValues{
-				"arm":                      makeLabelList([]string{}, []string{"arm_exclude"}),
-				"x86":                      makeLabelList([]string{"x86_include"}, []string{}),
-				ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}),
-			},
-			OsConfigurationAxis: labelListSelectValues{
-				"android": makeLabelList([]string{}, []string{"android_exclude"}),
-				"linux":   makeLabelList([]string{"linux_include"}, []string{}),
-			},
-			OsArchConfigurationAxis: labelListSelectValues{
-				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
-			},
-			ProductVariableConfigurationAxis(false, "product_with_defaults"): labelListSelectValues{
-				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
-				"b":                        makeLabelList([]string{"b_val"}, []string{}),
-				"c":                        makeLabelList([]string{"c_val"}, []string{}),
-				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
-			},
-			ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{
-				"a": makeLabelList([]string{}, []string{"product_config_exclude"}),
-			},
-		},
-	}
-
-	attr.ResolveExcludes()
-
-	expectedBaseIncludes := []Label{{Label: "all_include"}}
-	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
-		t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
-	}
-	var nilLabels []Label
-	expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
-		ArchConfigurationAxis: {
-			"arm":                      nilLabels,
-			"x86":                      makeLabels("arm_exclude", "x86_include"),
-			ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"),
-		},
-		OsConfigurationAxis: {
-			"android":                  nilLabels,
-			"linux":                    makeLabels("android_exclude", "linux_include"),
-			ConditionsDefaultConfigKey: makeLabels("android_exclude"),
-		},
-		OsArchConfigurationAxis: {
-			"linux_x86":                makeLabels("linux_x86_include"),
-			ConditionsDefaultConfigKey: nilLabels,
-		},
-		ProductVariableConfigurationAxis(false, "product_with_defaults"): {
-			"a":                        nilLabels,
-			"b":                        makeLabels("b_val"),
-			"c":                        makeLabels("c_val"),
-			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
-		},
-		ProductVariableConfigurationAxis(false, "product_only_with_excludes"): {
-			"a":                        nilLabels,
-			ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
-		},
-	}
-	for _, axis := range attr.SortedConfigurationAxes() {
-		if _, ok := expectedConfiguredIncludes[axis]; !ok {
-			t.Errorf("Found unexpected axis %s", axis)
-			continue
-		}
-		expectedForAxis := expectedConfiguredIncludes[axis]
-		gotForAxis := attr.ConfigurableValues[axis]
-		if len(expectedForAxis) != len(gotForAxis) {
-			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
-		}
-		for config, value := range gotForAxis {
-			if expected, ok := expectedForAxis[config]; ok {
-				if !reflect.DeepEqual(expected, value.Includes) {
-					t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes)
-				}
-			} else {
-				t.Errorf("Got unexpected config %q for %s", config, axis)
-			}
-		}
-	}
-}
-
-func TestLabelListAttributePartition(t *testing.T) {
-	testCases := []struct {
-		name         string
-		input        LabelListAttribute
-		predicated   LabelListAttribute
-		unpredicated LabelListAttribute
-		predicate    func(label Label) bool
-	}{
-		{
-			name: "move all to predicated partition",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			unpredicated: LabelListAttribute{},
-			predicate: func(label Label) bool {
-				return true
-			},
-		},
-		{
-			name: "move all to unpredicated partition",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: LabelListAttribute{},
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicate: func(label Label) bool {
-				return false
-			},
-		},
-		{
-			name: "partition includes and excludes",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "keep2"},
-				[]string{"keep1", "keep2"},
-			)),
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"throw1", "throw2"},
-				[]string{"throw1", "throw2"},
-			)),
-			predicate: func(label Label) bool {
-				return strings.HasPrefix(label.Label, "keep")
-			},
-		},
-		{
-			name: "partition excludes only",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{},
-				[]string{"keep1", "keep2"},
-			)),
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{},
-				[]string{"throw1", "throw2"},
-			)),
-			predicate: func(label Label) bool {
-				return strings.HasPrefix(label.Label, "keep")
-			},
-		},
-		{
-			name: "partition includes only",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "keep2"},
-				[]string{},
-			)),
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"throw1", "throw2"},
-				[]string{},
-			)),
-			predicate: func(label Label) bool {
-				return strings.HasPrefix(label.Label, "keep")
-			},
-		},
-		{
-			name:         "empty partition",
-			input:        MakeLabelListAttribute(makeLabelList([]string{}, []string{})),
-			predicated:   LabelListAttribute{},
-			unpredicated: LabelListAttribute{},
-			predicate: func(label Label) bool {
-				return true
-			},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			predicated, unpredicated := tc.input.Partition(tc.predicate)
-			if !predicated.Value.Equals(tc.predicated.Value) {
-				t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
-			}
-			for axis, configs := range predicated.ConfigurableValues {
-				tcConfigs, ok := tc.predicated.ConfigurableValues[axis]
-				if !ok || !reflect.DeepEqual(configs, tcConfigs) {
-					t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
-				}
-			}
-			if !unpredicated.Value.Equals(tc.unpredicated.Value) {
-				t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
-			}
-			for axis, configs := range unpredicated.ConfigurableValues {
-				tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis]
-				if !ok || !reflect.DeepEqual(configs, tcConfigs) {
-					t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
-				}
-			}
-		})
-	}
-}
-
-// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
-// typ
-func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
-	return func(omc OtherModuleContext, label Label) (string, bool) {
-		m, ok := omc.ModuleFromName(label.Label)
-		if !ok {
-			return label.Label, false
-		}
-		mTyp := omc.OtherModuleType(m)
-		if typ == mTyp {
-			return label.Label + suffix, true
-		}
-		return label.Label, false
-	}
-}
-
-func TestPartitionLabelListAttribute(t *testing.T) {
-	testCases := []struct {
-		name           string
-		ctx            *OtherModuleTestContext
-		labelList      LabelListAttribute
-		filters        LabelPartitions
-		expected       PartitionToLabelListAttribute
-		expectedErrMsg *string
-	}{
-		{
-			name: "no configurable values",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
-				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "no configurable values, remainder partition",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})},
-				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "no configurable values, empty partition",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "no configurable values, has map",
-			ctx: &OtherModuleTestContext{
-				Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}},
-			},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})},
-				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "configurable values, keeps empty if excludes",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"x86":    makeLabelList([]string{"a.a", "c.c"}, []string{}),
-						"arm":    makeLabelList([]string{"b.b"}, []string{}),
-						"x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}),
-					},
-				},
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{
-					ConfigurableValues: configurableLabelLists{
-						ArchConfigurationAxis: labelListSelectValues{
-							"x86":    makeLabelList([]string{"a.a"}, []string{}),
-							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
-						},
-					},
-				},
-				"B": LabelListAttribute{
-					ConfigurableValues: configurableLabelLists{
-						ArchConfigurationAxis: labelListSelectValues{
-							"arm":    makeLabelList([]string{"b.b"}, []string{}),
-							"x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}),
-						},
-					},
-				},
-				"C": LabelListAttribute{
-					ConfigurableValues: configurableLabelLists{
-						ArchConfigurationAxis: labelListSelectValues{
-							"x86":    makeLabelList([]string{"c.c"}, []string{}),
-							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "error for multiple partitions same value",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A":       LabelPartition{Extensions: []string{".a"}},
-				"other A": LabelPartition{Extensions: []string{".a"}},
-			},
-			expected:       PartitionToLabelListAttribute{},
-			expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`),
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters)
-
-			if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr {
-				t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg)
-			} else if tc.expectedErrMsg != nil {
-				found := false
-				for _, err := range tc.ctx.errors {
-					if strings.Contains(err, *tc.expectedErrMsg) {
-						found = true
-						break
-					}
-				}
-
-				if !found {
-					t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors)
-				}
-				return
-			}
-
-			if len(tc.expected) != len(got) {
-				t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got))
-			}
-			for partition, expectedLla := range tc.expected {
-				gotLla, ok := got[partition]
-				if !ok {
-					t.Errorf("Expected partition %q, but it was not found %v", partition, got)
-					continue
-				}
-				expectedLabelList := expectedLla.Value
-				gotLabelList := gotLla.Value
-				if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
-					t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes)
-				}
-				expectedAxes := expectedLla.SortedConfigurationAxes()
-				gotAxes := gotLla.SortedConfigurationAxes()
-				if !reflect.DeepEqual(expectedAxes, gotAxes) {
-					t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla)
-				}
-				for _, axis := range expectedLla.SortedConfigurationAxes() {
-					if _, exists := gotLla.ConfigurableValues[axis]; !exists {
-						t.Errorf("Expected %s to be a supported axis, but it was not found", axis)
-					}
-					if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) {
-						t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got)
-					}
-					for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] {
-						gotLabelList, exists := gotLla.ConfigurableValues[axis][config]
-						if !exists {
-							t.Errorf("Expected %s to be a supported config, but config was not found", config)
-							continue
-						}
-						if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
-							t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes)
-						}
-					}
-				}
-			}
-		})
-	}
-}
-
-func TestDeduplicateAxesFromBase(t *testing.T) {
-	attr := StringListAttribute{
-		Value: []string{
-			"all_include",
-			"arm_include",
-			"android_include",
-			"linux_x86_include",
-		},
-		ConfigurableValues: configurableStringLists{
-			ArchConfigurationAxis: stringListSelectValues{
-				"arm": []string{"arm_include"},
-				"x86": []string{"x86_include"},
-			},
-			OsConfigurationAxis: stringListSelectValues{
-				"android": []string{"android_include"},
-				"linux":   []string{"linux_include"},
-			},
-			OsArchConfigurationAxis: stringListSelectValues{
-				"linux_x86": {"linux_x86_include"},
-			},
-			ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
-				"a": []string{"not_in_value"},
-			},
-		},
-	}
-
-	attr.DeduplicateAxesFromBase()
-
-	expectedBaseIncludes := []string{
-		"all_include",
-		"arm_include",
-		"android_include",
-		"linux_x86_include",
-	}
-	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value) {
-		t.Errorf("Expected Value includes %q, got %q", attr.Value, expectedBaseIncludes)
-	}
-	expectedConfiguredIncludes := configurableStringLists{
-		ArchConfigurationAxis: stringListSelectValues{
-			"x86": []string{"x86_include"},
-		},
-		OsConfigurationAxis: stringListSelectValues{
-			"linux": []string{"linux_include"},
-		},
-		OsArchConfigurationAxis: stringListSelectValues{},
-		ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
-			"a": []string{"not_in_value"},
-		},
-	}
-	for _, axis := range attr.SortedConfigurationAxes() {
-		if _, ok := expectedConfiguredIncludes[axis]; !ok {
-			t.Errorf("Found unexpected axis %s", axis)
-			continue
-		}
-		expectedForAxis := expectedConfiguredIncludes[axis]
-		gotForAxis := attr.ConfigurableValues[axis]
-		if len(expectedForAxis) != len(gotForAxis) {
-			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
-		}
-		for config, value := range gotForAxis {
-			if expected, ok := expectedForAxis[config]; ok {
-				if !reflect.DeepEqual(expected, value) {
-					t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value)
-				}
-			} else {
-				t.Errorf("Got unexpected config %q for %s", config, axis)
-			}
-		}
-	}
-}
diff --git a/bazel/testing.go b/bazel/testing.go
deleted file mode 100644
index 9a43b61..0000000
--- a/bazel/testing.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bazel
-
-import (
-	"fmt"
-
-	"github.com/google/blueprint"
-)
-
-// TestModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
-// a blueprint ModuleContext
-type TestModuleInfo struct {
-	ModuleName string
-	Typ        string
-	Dir        string
-}
-
-// Name returns name for testModuleInfo -- required to implement blueprint.Module
-func (mi TestModuleInfo) Name() string {
-	return mi.ModuleName
-}
-
-// GenerateBuildActions unused, but required to implmeent blueprint.Module
-func (mi TestModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
-
-func (mi TestModuleInfo) equals(other TestModuleInfo) bool {
-	return mi.ModuleName == other.ModuleName && mi.Typ == other.Typ && mi.Dir == other.Dir
-}
-
-// ensure testModuleInfo implements blueprint.Module
-var _ blueprint.Module = TestModuleInfo{}
-
-// OtherModuleTestContext is a mock context that implements OtherModuleContext
-type OtherModuleTestContext struct {
-	Modules []TestModuleInfo
-	errors  []string
-}
-
-// ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists
-func (omc *OtherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
-	for _, m := range omc.Modules {
-		if m.ModuleName == name {
-			return m, true
-		}
-	}
-	return TestModuleInfo{}, false
-}
-
-// testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc
-func (omc *OtherModuleTestContext) testModuleInfo(m blueprint.Module) (TestModuleInfo, bool) {
-	mi, ok := m.(TestModuleInfo)
-	if !ok {
-		return TestModuleInfo{}, false
-	}
-	for _, other := range omc.Modules {
-		if other.equals(mi) {
-			return mi, true
-		}
-	}
-	return TestModuleInfo{}, false
-}
-
-// OtherModuleType returns type of m if it exists in omc
-func (omc *OtherModuleTestContext) OtherModuleType(m blueprint.Module) string {
-	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.Typ
-	}
-	return ""
-}
-
-// OtherModuleName returns name of m if it exists in omc
-func (omc *OtherModuleTestContext) OtherModuleName(m blueprint.Module) string {
-	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.ModuleName
-	}
-	return ""
-}
-
-// OtherModuleDir returns dir of m if it exists in omc
-func (omc *OtherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
-	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.Dir
-	}
-	return ""
-}
-
-func (omc *OtherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
-	omc.errors = append(omc.errors, fmt.Sprintf(format, args...))
-}
-
-// Ensure otherModuleTestContext implements OtherModuleContext
-var _ OtherModuleContext = &OtherModuleTestContext{}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index ba12682..28c0268 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -9,7 +9,6 @@
         "androidbp_to_build_templates.go",
         "build_conversion.go",
         "bzl_conversion.go",
-        "configurability.go",
         "constants.go",
         "conversion.go",
     ],
@@ -21,7 +20,6 @@
         "soong-android-allowlists",
         "soong-android-soongconfig",
         "soong-apex",
-        "soong-bazel",
         "soong-cc",
         "soong-cc-config",
         "soong-etc",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index bd56768..18213a8 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -26,7 +26,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/bazel"
 	"android/soong/starlark_fmt"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -182,12 +181,11 @@
 }
 
 type CodegenContext struct {
-	config             android.Config
-	context            *android.Context
-	mode               CodegenMode
-	additionalDeps     []string
-	unconvertedDepMode unconvertedDepsMode
-	topDir             string
+	config         android.Config
+	context        *android.Context
+	mode           CodegenMode
+	additionalDeps []string
+	topDir         string
 }
 
 func (ctx *CodegenContext) Mode() CodegenMode {
@@ -207,16 +205,6 @@
 	QueryView CodegenMode = iota
 )
 
-type unconvertedDepsMode int
-
-const (
-	// Include a warning in conversion metrics about converted modules with unconverted direct deps
-	warnUnconvertedDeps unconvertedDepsMode = iota
-	// Error and fail conversion if encountering a module with unconverted direct deps
-	// Enabled by setting environment variable `BP2BUILD_ERROR_UNCONVERTED`
-	errorModulesUnconvertedDeps
-)
-
 func (mode CodegenMode) String() string {
 	switch mode {
 	case QueryView:
@@ -245,13 +233,11 @@
 // NewCodegenContext creates a wrapper context that conforms to PathContext for
 // writing BUILD files in the output directory.
 func NewCodegenContext(config android.Config, context *android.Context, mode CodegenMode, topDir string) *CodegenContext {
-	var unconvertedDeps unconvertedDepsMode
 	return &CodegenContext{
-		context:            context,
-		config:             config,
-		mode:               mode,
-		unconvertedDepMode: unconvertedDeps,
-		topDir:             topDir,
+		context: context,
+		config:  config,
+		mode:    mode,
+		topDir:  topDir,
 	}
 }
 
@@ -482,14 +468,6 @@
 		}), nil
 
 	case reflect.Struct:
-		// Special cases where the bp2build sends additional information to the codegenerator
-		// by wrapping the attributes in a custom struct type.
-		if attr, ok := propertyValue.Interface().(bazel.Attribute); ok {
-			return prettyPrintAttribute(attr, indent)
-		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
-			return fmt.Sprintf("%q", label.Label), nil
-		}
-
 		// Sort and print the struct props by the key.
 		structProps, err := extractStructProperties(propertyValue, indent)
 
@@ -506,7 +484,7 @@
 		// Interfaces are used for for arch, multilib and target properties.
 		return "", nil
 	case reflect.Map:
-		if v, ok := propertyValue.Interface().(bazel.StringMapAttribute); ok {
+		if v, ok := propertyValue.Interface().(map[string]string); ok {
 			return starlark_fmt.PrintStringStringDict(v, indent), nil
 		}
 		return "", fmt.Errorf("bp2build expects map of type map[string]string for field: %s", propertyValue)
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
deleted file mode 100644
index 3d9f0a2..0000000
--- a/bp2build/configurability.go
+++ /dev/null
@@ -1,328 +0,0 @@
-package bp2build
-
-import (
-	"fmt"
-	"reflect"
-
-	"android/soong/android"
-	"android/soong/bazel"
-	"android/soong/starlark_fmt"
-)
-
-// Configurability support for bp2build.
-
-type selects map[string]reflect.Value
-
-func getStringValue(str bazel.StringAttribute) (reflect.Value, []selects) {
-	value := reflect.ValueOf(str.Value)
-
-	if !str.HasConfigurableValues() {
-		return value, []selects{}
-	}
-
-	ret := selects{}
-	for _, axis := range str.SortedConfigurationAxes() {
-		configToStrs := str.ConfigurableValues[axis]
-		for config, strs := range configToStrs {
-			selectKey := axis.SelectKey(config)
-			ret[selectKey] = reflect.ValueOf(strs)
-		}
-	}
-
-	// if there is a select, use the base value as the conditions default value
-	if len(ret) > 0 {
-		if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok {
-			ret[bazel.ConditionsDefaultSelectKey] = value
-			value = reflect.Zero(value.Type())
-		}
-	}
-
-	return value, []selects{ret}
-}
-
-func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects, bool) {
-	value := reflect.ValueOf(list.Value)
-	prepend := list.Prepend
-	if !list.HasConfigurableValues() {
-		return value, []selects{}, prepend
-	}
-
-	var ret []selects
-	for _, axis := range list.SortedConfigurationAxes() {
-		configToLists := list.ConfigurableValues[axis]
-		archSelects := map[string]reflect.Value{}
-		for config, labels := range configToLists {
-			selectKey := axis.SelectKey(config)
-			archSelects[selectKey] = reflect.ValueOf(labels)
-		}
-		if len(archSelects) > 0 {
-			ret = append(ret, archSelects)
-		}
-	}
-
-	return value, ret, prepend
-}
-
-func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
-	value := reflect.ValueOf(label.Value)
-	if !label.HasConfigurableValues() {
-		return value, []selects{}
-	}
-
-	ret := selects{}
-	for _, axis := range label.SortedConfigurationAxes() {
-		configToLabels := label.ConfigurableValues[axis]
-		for config, labels := range configToLabels {
-			selectKey := axis.SelectKey(config)
-			ret[selectKey] = reflect.ValueOf(labels)
-		}
-	}
-
-	// if there is a select, use the base value as the conditions default value
-	if len(ret) > 0 {
-		ret[bazel.ConditionsDefaultSelectKey] = value
-		value = reflect.Zero(value.Type())
-	}
-
-	return value, []selects{ret}
-}
-
-func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) {
-	value := reflect.ValueOf(boolAttr.Value)
-	if !boolAttr.HasConfigurableValues() {
-		return value, []selects{}
-	}
-
-	ret := selects{}
-	for _, axis := range boolAttr.SortedConfigurationAxes() {
-		configToBools := boolAttr.ConfigurableValues[axis]
-		for config, bools := range configToBools {
-			selectKey := axis.SelectKey(config)
-			ret[selectKey] = reflect.ValueOf(bools)
-		}
-	}
-	// if there is a select, use the base value as the conditions default value
-	if len(ret) > 0 {
-		ret[bazel.ConditionsDefaultSelectKey] = value
-		value = reflect.Zero(value.Type())
-	}
-
-	return value, []selects{ret}
-}
-func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects, bool) {
-	value := reflect.ValueOf(list.Value.Includes)
-	prepend := list.Prepend
-	var ret []selects
-	for _, axis := range list.SortedConfigurationAxes() {
-		configToLabels := list.ConfigurableValues[axis]
-		if !configToLabels.HasConfigurableValues() {
-			continue
-		}
-		archSelects := map[string]reflect.Value{}
-		defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
-		// Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default.
-		emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0
-		for config, labels := range configToLabels {
-			// Omit any entries in the map which match the default value, for brevity.
-			if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
-				continue
-			}
-			selectKey := axis.SelectKey(config)
-			if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use {
-				archSelects[selectKey] = value
-			}
-		}
-		if len(archSelects) > 0 {
-			ret = append(ret, archSelects)
-		}
-	}
-
-	return value, ret, prepend
-}
-
-func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
-	if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 {
-		return true, reflect.ValueOf(list.Includes)
-	} else if len(list.Excludes) > 0 {
-		// if there is still an excludes -- we need to have an empty list for this select & use the
-		// value in conditions default Includes
-		return true, reflect.ValueOf([]string{})
-	}
-	return false, reflect.Zero(reflect.TypeOf([]string{}))
-}
-
-var (
-	emptyBazelList = "[]"
-	bazelNone      = "None"
-)
-
-// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
-// select statements.
-func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
-	var value reflect.Value
-	// configurableAttrs is the list of individual select statements to be
-	// concatenated together. These select statements should be along different
-	// axes. For example, one element may be
-	// `select({"//color:red": "one", "//color:green": "two"})`, and the second
-	// element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
-	// These selects should be sorted by axis identifier.
-	var configurableAttrs []selects
-	var prepend bool
-	var defaultSelectValue *string
-	var emitZeroValues bool
-	// If true, print the default attribute value, even if the attribute is zero.
-	shouldPrintDefault := false
-	switch list := v.(type) {
-	case bazel.StringAttribute:
-		if err := list.Collapse(); err != nil {
-			return "", err
-		}
-		value, configurableAttrs = getStringValue(list)
-		defaultSelectValue = &bazelNone
-	case bazel.StringListAttribute:
-		value, configurableAttrs, prepend = getStringListValues(list)
-		defaultSelectValue = &emptyBazelList
-	case bazel.LabelListAttribute:
-		value, configurableAttrs, prepend = getLabelListValues(list)
-		emitZeroValues = list.EmitEmptyList
-		defaultSelectValue = &emptyBazelList
-		if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
-			shouldPrintDefault = true
-		}
-	case bazel.LabelAttribute:
-		if err := list.Collapse(); err != nil {
-			return "", err
-		}
-		value, configurableAttrs = getLabelValue(list)
-		defaultSelectValue = &bazelNone
-	case bazel.BoolAttribute:
-		if err := list.Collapse(); err != nil {
-			return "", err
-		}
-		value, configurableAttrs = getBoolValue(list)
-		defaultSelectValue = &bazelNone
-	default:
-		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
-	}
-
-	var err error
-	ret := ""
-	if value.Kind() != reflect.Invalid {
-		s, err := prettyPrint(value, indent, false) // never emit zero values for the base value
-		if err != nil {
-			return ret, err
-		}
-
-		ret += s
-	}
-	// Convenience function to prepend/append selects components to an attribute value.
-	concatenateSelects := func(selectsData selects, defaultValue *string, s string, prepend bool) (string, error) {
-		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
-		if err != nil {
-			return "", err
-		}
-		var left, right string
-		if prepend {
-			left, right = selectMap, s
-		} else {
-			left, right = s, selectMap
-		}
-		if left != "" && right != "" {
-			left += " + "
-		}
-		left += right
-
-		return left, nil
-	}
-
-	for _, configurableAttr := range configurableAttrs {
-		ret, err = concatenateSelects(configurableAttr, defaultSelectValue, ret, prepend)
-		if err != nil {
-			return "", err
-		}
-	}
-
-	if ret == "" && shouldPrintDefault {
-		return *defaultSelectValue, nil
-	}
-	return ret, nil
-}
-
-// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
-// to construct a select map for any kind of attribute type.
-func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) {
-	if selectMap == nil {
-		return "", nil
-	}
-
-	var selects string
-	for _, selectKey := range android.SortedKeys(selectMap) {
-		if selectKey == bazel.ConditionsDefaultSelectKey {
-			// Handle default condition later.
-			continue
-		}
-		value := selectMap[selectKey]
-		if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) {
-			// Ignore zero values to not generate empty lists. However, always note zero values if
-			// the default value is non-zero.
-			continue
-		}
-		s, err := prettyPrintSelectEntry(value, selectKey, indent, true)
-		if err != nil {
-			return "", err
-		}
-		// s could still be an empty string, e.g. unset slices of structs with
-		// length of 0.
-		if s != "" {
-			selects += s + ",\n"
-		}
-	}
-
-	if len(selects) == 0 {
-		// If there is a default value, and there are no selects for this axis, print that without any selects.
-		if val, exists := selectMap[bazel.ConditionsDefaultSelectKey]; exists {
-			return prettyPrint(val, indent, emitZeroValues)
-		}
-		// No conditions (or all values are empty lists), so no need for a map.
-		return "", nil
-	}
-
-	// Create the map.
-	ret := "select({\n"
-	ret += selects
-
-	// Handle the default condition
-	s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues)
-	if err != nil {
-		return "", err
-	}
-	if s != "" {
-		// Print the custom default value.
-		ret += s
-		ret += ",\n"
-	} else if defaultValue != nil {
-		// Print an explicit empty list (the default value) even if the value is
-		// empty, to avoid errors about not finding a configuration that matches.
-		ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
-	}
-
-	ret += starlark_fmt.Indention(indent)
-	ret += "})"
-
-	return ret, nil
-}
-
-// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
-// with a provided key.
-func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
-	s := starlark_fmt.Indention(indent + 1)
-	v, err := prettyPrint(value, indent+1, emitZeroValues)
-	if err != nil {
-		return "", err
-	}
-	if v == "" {
-		return "", nil
-	}
-	s += fmt.Sprintf("\"%s\": %s", key, v)
-	return s, nil
-}
diff --git a/bp2build/constants.go b/bp2build/constants.go
index 4870dff..76ba106 100644
--- a/bp2build/constants.go
+++ b/bp2build/constants.go
@@ -20,9 +20,4 @@
 
 	// The file name used for automatically generated files.
 	GeneratedBuildFileName = "BUILD.bazel"
-
-	// The file name used for hand-crafted build targets.
-	// NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files
-	// FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android
-	HandcraftedBuildFileName = "BUILD.bazel"
 )
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 e68e4a3..88a793c 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -16,7 +16,6 @@
         "soong-etc",
         "soong-fuzz",
         "soong-genrule",
-        "soong-multitree",
         "soong-testing",
         "soong-tradefed",
     ],
@@ -65,7 +64,6 @@
         "library.go",
         "library_headers.go",
         "library_sdk_member.go",
-        "library_stub.go",
         "native_bridge_sdk_trait.go",
         "object.go",
         "test.go",
@@ -104,6 +102,7 @@
         "orderfile_test.go",
         "prebuilt_test.go",
         "proto_test.go",
+        "sabi_test.go",
         "sanitize_test.go",
         "sdk_test.go",
         "test_data_test.go",
@@ -118,4 +117,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/androidmk.go b/cc/androidmk.go
index cecaae2..6966f76 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -21,7 +21,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/multitree"
 )
 
 var (
@@ -479,34 +478,6 @@
 	androidMkWritePrebuiltOptions(p.baseLinker, entries)
 }
 
-func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-	entries.SubName += multitree.GetApiImportSuffix()
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		a.libraryDecorator.androidMkWriteExportedFlags(entries)
-		src := *a.properties.Src
-		path, file := filepath.Split(src)
-		stem, suffix, ext := android.SplitFileExt(file)
-		entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
-		entries.SetString("LOCAL_MODULE_PATH", path)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-		entries.SetString("LOCAL_SOONG_TOC", a.toc().String())
-	})
-}
-
-func (a *apiHeadersDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "HEADER_LIBRARIES"
-	entries.SubName += multitree.GetApiImportSuffix()
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		a.libraryDecorator.androidMkWriteExportedFlags(entries)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-	})
-}
-
 func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkEntries) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 8a7ea88..4063714 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -132,7 +132,7 @@
 
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
-		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx, ccModule, specifiedDeps)
+		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps)
 
 		p.SharedLibs = specifiedDeps.sharedLibs
 		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
diff --git a/cc/cc.go b/cc/cc.go
index b534737..1b7624d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,6 @@
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
-	"android/soong/multitree"
 )
 
 func init() {
@@ -60,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{})
 
@@ -74,12 +73,12 @@
 		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) {
+	ctx.PostApexMutators(func(ctx android.RegisterMutatorsContext) {
 		// sabi mutator needs to be run after apex mutator finishes.
-		ctx.TopDown("sabi_deps", sabiDepsMutator)
+		ctx.Transition("sabi", &sabiTransitionMutator{})
 	})
 
 	ctx.RegisterParallelSingletonType("kythe_extract_all", kytheExtractAllFactory)
@@ -138,7 +137,7 @@
 
 	// LLNDK headers for the ABI checker to check LLNDK implementation library.
 	// An LLNDK implementation is the core variant. LLNDK header libs are reexported by the vendor variant.
-	// The core variant cannot depend on the vendor variant because of the order of CreateVariations.
+	// The core variant cannot depend on the vendor variant because of the order of imageTransitionMutator.Split().
 	// Instead, the LLNDK implementation depends on the LLNDK header libs.
 	LlndkHeaderLibs []string
 }
@@ -613,7 +612,7 @@
 	coverageOutputFilePath() android.OptionalPath
 
 	// Get the deps that have been explicitly specified in the properties.
-	linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps
+	linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps
 
 	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
 }
@@ -970,7 +969,7 @@
 	return c.Properties.HideFromMake
 }
 
-func (c *Module) RequiredModuleNames(ctx android.ConfigAndErrorContext) []string {
+func (c *Module) RequiredModuleNames(ctx android.ConfigurableEvaluatorContext) []string {
 	required := android.CopyOf(c.ModuleBase.RequiredModuleNames(ctx))
 	if c.ImageVariation().Variation == android.CoreVariation {
 		required = append(required, c.Properties.Target.Platform.Required...)
@@ -2361,24 +2360,6 @@
 	}
 }
 
-func GetApiImports(c LinkableInterface, actx android.BottomUpMutatorContext) multitree.ApiImportInfo {
-	apiImportInfo := multitree.ApiImportInfo{}
-
-	if c.Device() {
-		var apiImportModule []blueprint.Module
-		if actx.OtherModuleExists("api_imports") {
-			apiImportModule = actx.AddDependency(c, nil, "api_imports")
-			if len(apiImportModule) > 0 && apiImportModule[0] != nil {
-				apiInfo, _ := android.OtherModuleProvider(actx, apiImportModule[0], multitree.ApiImportsProvider)
-				apiImportInfo = apiInfo
-				android.SetProvider(actx, multitree.ApiImportsProvider, apiInfo)
-			}
-		}
-	}
-
-	return apiImportInfo
-}
-
 func GetReplaceModuleName(lib string, replaceMap map[string]string) string {
 	if snapshot, ok := replaceMap[lib]; ok {
 		return snapshot
@@ -2448,11 +2429,6 @@
 			// NDK Variant
 			return true
 		}
-
-		if c.isImportedApiLibrary() {
-			// API Library should depend on API headers
-			return true
-		}
 	}
 
 	return false
@@ -2472,19 +2448,10 @@
 	ctx.ctx = ctx
 
 	deps := c.deps(ctx)
-	apiImportInfo := GetApiImports(c, actx)
 
 	apiNdkLibs := []string{}
 	apiLateNdkLibs := []string{}
 
-	if c.shouldUseApiSurface() {
-		deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config())
-		deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
-		deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
-		deps.ReexportHeaderLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportHeaderLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
-		deps.ReexportSharedLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportSharedLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
-	}
-
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
 
 	variantNdkLibs := []string{}
@@ -2501,11 +2468,6 @@
 			depTag.reexportFlags = true
 		}
 
-		// Check header lib replacement from API surface first, and then check again with VSDK
-		if c.shouldUseApiSurface() {
-			lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
-		}
-
 		if c.isNDKStubLibrary() {
 			variationExists := actx.OtherModuleDependencyVariantExists(nil, lib)
 			if variationExists {
@@ -2515,7 +2477,7 @@
 				// any variants.
 				actx.AddFarVariationDependencies([]blueprint.Variation{}, depTag, lib)
 			}
-		} else if c.IsStubs() && !c.isImportedApiLibrary() {
+		} else if c.IsStubs() {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
 				depTag, lib)
 		} else {
@@ -2591,22 +2553,12 @@
 		}
 
 		name, version := StubsLibNameAndVersion(lib)
-		if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) {
-			name = apiLibraryName
-		}
 		sharedLibNames = append(sharedLibNames, name)
 
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-
-		if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
-			AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
-		}
-
-		if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
-			AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, apiLibraryName, version, false)
-		}
+		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
 	}
 
 	for _, lib := range deps.LateStaticLibs {
@@ -2701,7 +2653,6 @@
 		)
 	}
 
-	updateImportedLibraryDependency(ctx)
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
@@ -2730,10 +2681,6 @@
 		return
 	}
 
-	// TODO(b/244244438) : Remove this once all variants are implemented
-	if ccFrom, ok := from.(*Module); ok && ccFrom.isImportedApiLibrary() {
-		return
-	}
 	if from.SdkVersion() == "" {
 		// Platform code can link to anything
 		return
@@ -2756,10 +2703,6 @@
 			// the NDK.
 			return
 		}
-		if c.isImportedApiLibrary() {
-			// Imported library from the API surface is a stub library built against interface definition.
-			return
-		}
 	}
 
 	if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" {
@@ -2840,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 {
@@ -2935,47 +2878,6 @@
 
 	skipModuleList := map[string]bool{}
 
-	var apiImportInfo multitree.ApiImportInfo
-	hasApiImportInfo := false
-
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		if dep.Name() == "api_imports" {
-			apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider)
-			hasApiImportInfo = true
-		}
-	})
-
-	if hasApiImportInfo {
-		targetStubModuleList := map[string]string{}
-		targetOrigModuleList := map[string]string{}
-
-		// Search for dependency which both original module and API imported library with APEX stub exists
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
-				targetStubModuleList[apiLibrary] = depName
-			}
-		})
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if origLibrary, ok := targetStubModuleList[depName]; ok {
-				targetOrigModuleList[origLibrary] = depName
-			}
-		})
-
-		// Decide which library should be used between original and API imported library
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := targetOrigModuleList[depName]; ok {
-				if ShouldUseStubForApex(ctx, dep) {
-					skipModuleList[depName] = true
-				} else {
-					skipModuleList[apiLibrary] = true
-				}
-			}
-		})
-	}
-
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
@@ -3404,17 +3306,7 @@
 		// bootstrap modules, always link to non-stub variant
 		isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
 
-		isApexImportedApiLibrary := false
-
-		if cc, ok := dep.(*Module); ok {
-			if apiLibrary, ok := cc.linker.(*apiLibraryDecorator); ok {
-				if apiLibrary.hasApexStubs() {
-					isApexImportedApiLibrary = true
-				}
-			}
-		}
-
-		useStubs = (isNotInPlatform || isApexImportedApiLibrary) && !bootstrap
+		useStubs = isNotInPlatform && !bootstrap
 
 		if useStubs {
 			// Another exception: if this module is a test for an APEX, then
@@ -3439,7 +3331,7 @@
 			// only partially overlapping apex_available. For that test_for
 			// modules would need to be split into APEX variants and resolved
 			// separately for each APEX they have access to.
-			if !isApexImportedApiLibrary && android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
+			if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
 				useStubs = false
 			}
 		}
@@ -4023,11 +3915,6 @@
 	return c.Properties.IsSdkVariant
 }
 
-func (c *Module) isImportedApiLibrary() bool {
-	_, ok := c.linker.(*apiLibraryDecorator)
-	return ok
-}
-
 func kytheExtractAllFactory() android.Singleton {
 	return &kytheExtractAllSingleton{}
 }
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/check.go b/cc/check.go
index fa1926d..8e2844f 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -40,6 +40,8 @@
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
 		} else if flag == "-fwhole-program-vtables" {
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use whole_program_vtables instead", flag)
+		} else if flag == "-gsplit-dwarf" {
+			ctx.PropertyErrorf(prop, "Bad flag: `%s`, soong cannot track dependencies to split dwarf debuginfo", flag)
 		} else if flag == "-fno-integrated-as" {
 			ctx.PropertyErrorf("Bad flag: `%s` is disallowed as it may invoke the `as` from the build host", flag)
 		} else if flag == "-Weverything" {
diff --git a/cc/compiler.go b/cc/compiler.go
index 396ec88..022b712 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -228,9 +228,6 @@
 		Static *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// Stores the original list of source files before being cleared by library reuse
-	OriginalSrcs proptools.Configurable[[]string] `blueprint:"mutated"`
-
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
 }
@@ -363,10 +360,20 @@
 	tc := ctx.toolchain()
 	modulePath := ctx.ModuleDir()
 
-	srcs := compiler.Properties.Srcs.GetOrDefault(ctx, nil)
-	exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
-	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
-	compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
+	reuseObjs := false
+	if len(ctx.GetDirectDepsWithTag(reuseObjTag)) > 0 {
+		reuseObjs = true
+	}
+
+	// If a reuseObjTag dependency exists then this module is reusing the objects (generally the shared variant
+	// reusing objects from the static variant), and doesn't need to compile any sources of its own.
+	var srcs []string
+	if !reuseObjs {
+		srcs = compiler.Properties.Srcs.GetOrDefault(ctx, nil)
+		exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
+		compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
+		compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
+	}
 
 	cflags := compiler.Properties.Cflags.GetOrDefault(ctx, nil)
 	cppflags := compiler.Properties.Cppflags.GetOrDefault(ctx, nil)
@@ -437,18 +444,20 @@
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
 		if ctx.inVendor() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR__")
-
-			vendorApiLevel := ctx.Config().VendorApiLevel()
-			if vendorApiLevel == "" {
-				// TODO(b/314036847): This is a fallback for UDC targets.
-				// This must be a build failure when UDC is no longer built
-				// from this source tree.
-				vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
-			}
-			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 		} else if ctx.inProduct() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_PRODUCT__")
 		}
+
+		// Define __ANDROID_VENDOR_API__ for both product and vendor variants
+		// because they both use the same LLNDK libraries.
+		vendorApiLevel := ctx.Config().VendorApiLevel()
+		if vendorApiLevel == "" {
+			// TODO(b/314036847): This is a fallback for UDC targets.
+			// This must be a build failure when UDC is no longer built
+			// from this source tree.
+			vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+		}
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 	}
 
 	if ctx.inRecovery() {
@@ -719,11 +728,6 @@
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.OriginalSrcs.GetOrDefault(ctx, nil) {
-		if filepath.Ext(src) == ext {
-			return true
-		}
-	}
 
 	return false
 }
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/config/x86_64_device.go b/cc/config/x86_64_device.go
index 5aa2a7e..e7ac038 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -40,6 +40,9 @@
 			"-march=x86-64",
 		},
 
+		"alderlake": []string{
+			"-march=alderlake",
+		},
 		"broadwell": []string{
 			"-march=broadwell",
 		},
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 4b0041c..a92881d 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -42,6 +42,9 @@
 		"x86_64": []string{
 			"-march=prescott",
 		},
+		"alderlake": []string{
+			"-march=alderlake",
+		},
 		"atom": []string{
 			"-march=atom",
 		},
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/generated_cc_library.go b/cc/generated_cc_library.go
index b1084e4..709586b 100644
--- a/cc/generated_cc_library.go
+++ b/cc/generated_cc_library.go
@@ -18,7 +18,7 @@
 	"android/soong/android"
 )
 
-func GeneratedCcLibraryModuleFactory(moduleName string, callbacks Generator) android.Module {
+func GeneratedCcLibraryModuleFactory(callbacks Generator) android.Module {
 	module, _ := NewLibrary(android.HostAndDeviceSupported)
 
 	// Can be used as both a static and a shared library.
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/library.go b/cc/library.go
index 03f7174..988a7fa 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -548,8 +548,7 @@
 	return flags
 }
 
-func (library *libraryDecorator) getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties {
-	m := ctx.Module().(*Module)
+func (library *libraryDecorator) getHeaderAbiCheckerProperties(m *Module) headerAbiCheckerProperties {
 	variantProps := &library.Properties.Target.Platform.Header_abi_checker
 	if m.InVendor() {
 		variantProps = &library.Properties.Target.Vendor.Header_abi_checker
@@ -559,7 +558,7 @@
 	props := library.Properties.Header_abi_checker
 	err := proptools.AppendProperties(&props, variantProps, nil)
 	if err != nil {
-		ctx.ModuleErrorf("Cannot merge headerAbiCheckerProperties: %s", err.Error())
+		panic(fmt.Errorf("Cannot merge headerAbiCheckerProperties: %s", err.Error()))
 	}
 	return props
 }
@@ -718,7 +717,7 @@
 	setShared()
 
 	// Gets the ABI properties for vendor, product, or platform variant
-	getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties
+	getHeaderAbiCheckerProperties(m *Module) headerAbiCheckerProperties
 
 	// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
 	androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
@@ -919,7 +918,7 @@
 	return deps
 }
 
-func (library *libraryDecorator) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+func (library *libraryDecorator) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
 	specifiedDeps = library.baseLinker.linkerSpecifiedDeps(ctx, module, specifiedDeps)
 	var properties StaticOrSharedProperties
 	if library.static() {
@@ -1365,7 +1364,7 @@
 	sourceVersion, errorMessage string) {
 
 	extraFlags := []string{"-target-version", sourceVersion}
-	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module))
 	if Bool(headerAbiChecker.Check_all_apis) {
 		extraFlags = append(extraFlags, "-check-all-apis")
 	} else {
@@ -1437,7 +1436,7 @@
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, objs Objects, fileName string, soFile android.Path) {
 	if library.sabi.shouldCreateSourceAbiDump() {
 		exportedIncludeDirs := library.exportedIncludeDirsForAbiCheck(ctx)
-		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module))
 		currSdkVersion := currRefAbiDumpSdkVersion(ctx)
 		currVendorVersion := ctx.Config().VendorApiLevel()
 
@@ -1451,7 +1450,7 @@
 			[]string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */)
 
 		var llndkDump, apexVariantDump android.Path
-		tags := classifySourceAbiDump(ctx)
+		tags := classifySourceAbiDump(ctx.Module().(*Module))
 		optInTags := []lsdumpTag{}
 		for _, tag := range tags {
 			if tag == llndkLsdumpTag && currVendorVersion != "" {
@@ -1868,7 +1867,7 @@
 }
 
 func (library *libraryDecorator) symbolFileForAbiCheck(ctx ModuleContext) *string {
-	if props := library.getHeaderAbiCheckerProperties(ctx); props.Symbol_file != nil {
+	if props := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module)); props.Symbol_file != nil {
 		return props.Symbol_file
 	}
 	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
@@ -2071,12 +2070,7 @@
 			sharedCompiler.StaticProperties.Static.System_shared_libs == nil &&
 			sharedCompiler.SharedProperties.Shared.System_shared_libs == nil {
 
-			// TODO: namespaces?
 			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, reuseObjTag, ctx.ModuleName())
-			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
-				sharedCompiler.baseCompiler.Properties.Srcs
-			sharedCompiler.baseCompiler.Properties.Srcs = proptools.NewConfigurable[[]string](nil, nil)
-			sharedCompiler.baseCompiler.Properties.Generated_sources = nil
 		}
 
 		// This dep is just to reference static variant from shared variant
@@ -2350,9 +2344,8 @@
 	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
 		isLLNDK := m.IsLlndk()
 		isVendorPublicLibrary := m.IsVendorPublicLibrary()
-		isImportedApiLibrary := m.isImportedApiLibrary()
 
-		if variation != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary {
+		if variation != "" || isLLNDK || isVendorPublicLibrary {
 			// A stubs or LLNDK stubs variant.
 			if m.sanitize != nil {
 				m.sanitize.Properties.ForceDisable = true
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 053c460..af3658d 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -543,7 +543,7 @@
 	p.ExportedFlags = exportedInfo.Flags
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
-		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx, ccModule, specifiedDeps)
+		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps)
 
 		if lib := ccModule.library; lib != nil {
 			if !lib.hasStubsVariants() {
diff --git a/cc/library_stub.go b/cc/library_stub.go
deleted file mode 100644
index 6367825..0000000
--- a/cc/library_stub.go
+++ /dev/null
@@ -1,512 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cc
-
-import (
-	"regexp"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/multitree"
-
-	"github.com/google/blueprint/proptools"
-)
-
-var (
-	ndkVariantRegex  = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
-	stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
-)
-
-func init() {
-	RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
-}
-
-func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
-	ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
-	ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory)
-}
-
-func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok {
-		return
-	}
-
-	apiLibrary, ok := m.linker.(*apiLibraryDecorator)
-	if !ok {
-		return
-	}
-
-	if m.InVendorOrProduct() && apiLibrary.hasLLNDKStubs() {
-		// Add LLNDK variant dependency
-		if inList("llndk", apiLibrary.properties.Variants) {
-			variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
-			ctx.AddDependency(m, nil, variantName)
-		}
-	} else if m.IsSdkVariant() {
-		// Add NDK variant dependencies
-		targetVariant := "ndk." + m.StubsVersion()
-		if inList(targetVariant, apiLibrary.properties.Variants) {
-			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
-			ctx.AddDependency(m, nil, variantName)
-		}
-	} else if m.IsStubs() {
-		targetVariant := "apex." + m.StubsVersion()
-		if inList(targetVariant, apiLibrary.properties.Variants) {
-			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
-			ctx.AddDependency(m, nil, variantName)
-		}
-	}
-}
-
-// 'cc_api_library' is a module type which is from the exported API surface
-// with C shared library type. The module will replace original module, and
-// offer a link to the module that generates shared library object from the
-// map file.
-type apiLibraryProperties struct {
-	Src      *string `android:"arch_variant"`
-	Variants []string
-}
-
-type apiLibraryDecorator struct {
-	*libraryDecorator
-	properties apiLibraryProperties
-}
-
-func CcApiLibraryFactory() android.Module {
-	module, decorator := NewLibrary(android.DeviceSupported)
-	apiLibraryDecorator := &apiLibraryDecorator{
-		libraryDecorator: decorator,
-	}
-	apiLibraryDecorator.BuildOnlyShared()
-
-	module.stl = nil
-	module.sanitize = nil
-	decorator.disableStripping()
-
-	module.compiler = nil
-	module.linker = apiLibraryDecorator
-	module.installer = nil
-	module.library = apiLibraryDecorator
-	module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
-		apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	module.Init()
-
-	return module
-}
-
-func (d *apiLibraryDecorator) Name(basename string) string {
-	return basename + multitree.GetApiImportSuffix()
-}
-
-// Export include dirs without checking for existence.
-// The directories are not guaranteed to exist during Soong analysis.
-func (d *apiLibraryDecorator) exportIncludes(ctx ModuleContext) {
-	exporterProps := d.flagExporter.Properties
-	for _, dir := range exporterProps.Export_include_dirs.GetOrDefault(ctx, nil) {
-		d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
-	}
-	// system headers
-	for _, dir := range exporterProps.Export_system_include_dirs {
-		d.systemDirs = append(d.systemDirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
-	}
-}
-
-func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) {
-	d.baseLinker.linkerInit(ctx)
-
-	if d.hasNDKStubs() {
-		// Set SDK version of module as current
-		ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current")
-
-		// Add NDK stub as NDK known libs
-		name := ctx.ModuleName()
-
-		ndkKnownLibsLock.Lock()
-		ndkKnownLibs := getNDKKnownLibs(ctx.Config())
-		if !inList(name, *ndkKnownLibs) {
-			*ndkKnownLibs = append(*ndkKnownLibs, name)
-		}
-		ndkKnownLibsLock.Unlock()
-	}
-}
-
-func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
-	m, _ := ctx.Module().(*Module)
-
-	var in android.Path
-
-	// src might not exist during the beginning of soong analysis in Multi-tree
-	if src := String(d.properties.Src); src != "" {
-		in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
-	}
-
-	libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
-
-	load_cc_variant := func(apiVariantModule string) {
-		var mod android.Module
-
-		ctx.VisitDirectDeps(func(depMod android.Module) {
-			if depMod.Name() == apiVariantModule {
-				mod = depMod
-				libName = apiVariantModule
-			}
-		})
-
-		if mod != nil {
-			variantMod, ok := mod.(*CcApiVariant)
-			if ok {
-				in = variantMod.Src()
-
-				// Copy LLDNK properties to cc_api_library module
-				exportIncludeDirs := append(d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil),
-					variantMod.exportProperties.Export_include_dirs...)
-				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](
-					nil,
-					[]proptools.ConfigurableCase[[]string]{
-						proptools.NewConfigurableCase[[]string](nil, &exportIncludeDirs),
-					},
-				)
-
-				// Export headers as system include dirs if specified. Mostly for libc
-				if Bool(variantMod.exportProperties.Export_headers_as_system) {
-					d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
-						d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
-						d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil)...)
-					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil)
-				}
-			}
-		}
-	}
-
-	if m.InVendorOrProduct() && d.hasLLNDKStubs() {
-		// LLNDK variant
-		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
-	} else if m.IsSdkVariant() {
-		// NDK Variant
-		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
-	} else if m.IsStubs() {
-		// APEX Variant
-		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
-	}
-
-	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
-	d.exportIncludes(ctx)
-	d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
-	d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
-	d.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
-	d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
-	d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
-
-	if in == nil {
-		ctx.PropertyErrorf("src", "Unable to locate source property")
-		return nil
-	}
-
-	// Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file)
-	// The .so file itself has an order-only dependency on the headers contributed by this library.
-	// Creating this dependency ensures that the headers are assembled before compilation of rdeps begins.
-	d.libraryDecorator.reexportDeps(in)
-	d.libraryDecorator.flagExporter.setProvider(ctx)
-
-	d.unstrippedOutputFile = in
-	libName += flags.Toolchain.ShlibSuffix()
-
-	tocFile := android.PathForModuleOut(ctx, libName+".toc")
-	d.tocFile = android.OptionalPathForPath(tocFile)
-	TransformSharedObjectToToc(ctx, in, tocFile)
-
-	outputFile := android.PathForModuleOut(ctx, libName)
-
-	// TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
-	// We can just use original input if there is any way to avoid name conflict without copy.
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cp,
-		Description: "API surface imported library",
-		Input:       in,
-		Output:      outputFile,
-		Args: map[string]string{
-			"cpFlags": "-L",
-		},
-	})
-
-	android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
-		SharedLibrary: outputFile,
-		Target:        ctx.Target(),
-
-		TableOfContents: d.tocFile,
-	})
-
-	d.shareStubs(ctx)
-
-	return outputFile
-}
-
-// Share additional information about stub libraries with provider
-func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
-	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
-	if len(stubs) > 0 {
-		var stubsInfo []SharedStubLibrary
-		for _, stub := range stubs {
-			stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
-			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
-			stubsInfo = append(stubsInfo, SharedStubLibrary{
-				Version:           moduleLibraryInterface(stub).stubsVersion(),
-				SharedLibraryInfo: stubInfo,
-				FlagExporterInfo:  flagInfo,
-			})
-		}
-		android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
-			SharedStubLibraries: stubsInfo,
-
-			IsLLNDK: ctx.IsLlndk(),
-		})
-	}
-}
-
-func (d *apiLibraryDecorator) availableFor(what string) bool {
-	// Stub from API surface should be available for any APEX.
-	return true
-}
-
-func (d *apiLibraryDecorator) hasApexStubs() bool {
-	for _, variant := range d.properties.Variants {
-		if strings.HasPrefix(variant, "apex") {
-			return true
-		}
-	}
-	return false
-}
-
-func (d *apiLibraryDecorator) hasStubsVariants() bool {
-	return d.hasApexStubs()
-}
-
-func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseModuleContext) []string {
-	m, ok := ctx.Module().(*Module)
-
-	if !ok {
-		return nil
-	}
-
-	// TODO(b/244244438) Create more version information for NDK and APEX variations
-	// NDK variants
-	if m.IsSdkVariant() {
-		// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
-		if d.hasNDKStubs() {
-			return d.getNdkVersions()
-		}
-	}
-
-	if d.hasLLNDKStubs() && m.InVendorOrProduct() {
-		// LLNDK libraries only need a single stubs variant.
-		return []string{android.FutureApiLevel.String()}
-	}
-
-	stubsVersions := d.getStubVersions()
-
-	if len(stubsVersions) != 0 {
-		return stubsVersions
-	}
-
-	if m.MinSdkVersion() == "" {
-		return nil
-	}
-
-	firstVersion, err := nativeApiLevelFromUser(ctx,
-		m.MinSdkVersion())
-
-	if err != nil {
-		return nil
-	}
-
-	return ndkLibraryVersions(ctx, firstVersion)
-}
-
-func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
-	return inList("llndk", d.properties.Variants)
-}
-
-func (d *apiLibraryDecorator) hasNDKStubs() bool {
-	for _, variant := range d.properties.Variants {
-		if ndkVariantRegex.MatchString(variant) {
-			return true
-		}
-	}
-	return false
-}
-
-func (d *apiLibraryDecorator) getNdkVersions() []string {
-	ndkVersions := []string{}
-
-	for _, variant := range d.properties.Variants {
-		if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
-			ndkVersions = append(ndkVersions, match[1])
-		}
-	}
-
-	return ndkVersions
-}
-
-func (d *apiLibraryDecorator) getStubVersions() []string {
-	stubVersions := []string{}
-
-	for _, variant := range d.properties.Variants {
-		if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
-			stubVersions = append(stubVersions, match[1])
-		}
-	}
-
-	return stubVersions
-}
-
-// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
-// header libraries. The module will replace any dependencies to existing
-// original header libraries.
-type apiHeadersDecorator struct {
-	*libraryDecorator
-}
-
-func CcApiHeadersFactory() android.Module {
-	module, decorator := NewLibrary(android.DeviceSupported)
-	apiHeadersDecorator := &apiHeadersDecorator{
-		libraryDecorator: decorator,
-	}
-	apiHeadersDecorator.HeaderOnly()
-
-	module.stl = nil
-	module.sanitize = nil
-	decorator.disableStripping()
-
-	module.compiler = nil
-	module.linker = apiHeadersDecorator
-	module.installer = nil
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
-		apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	module.Init()
-
-	return module
-}
-
-func (d *apiHeadersDecorator) Name(basename string) string {
-	return basename + multitree.GetApiImportSuffix()
-}
-
-func (d *apiHeadersDecorator) availableFor(what string) bool {
-	// Stub from API surface should be available for any APEX.
-	return true
-}
-
-type ccApiexportProperties struct {
-	Src     *string `android:"arch_variant"`
-	Variant *string
-	Version *string
-}
-
-type variantExporterProperties struct {
-	// Header directory to export
-	Export_include_dirs []string `android:"arch_variant"`
-
-	// Export all headers as system include
-	Export_headers_as_system *bool
-}
-
-type CcApiVariant struct {
-	android.ModuleBase
-
-	properties       ccApiexportProperties
-	exportProperties variantExporterProperties
-
-	src android.Path
-}
-
-var _ android.Module = (*CcApiVariant)(nil)
-var _ android.ImageInterface = (*CcApiVariant)(nil)
-
-func CcApiVariantFactory() android.Module {
-	module := &CcApiVariant{}
-
-	module.AddProperties(&module.properties)
-	module.AddProperties(&module.exportProperties)
-
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
-	return module
-}
-
-func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// No need to build
-
-	if String(v.properties.Src) == "" {
-		ctx.PropertyErrorf("src", "src is a required property")
-	}
-
-	// Skip the existence check of the stub prebuilt file.
-	// The file is not guaranteed to exist during Soong analysis.
-	// Build orchestrator will be responsible for creating a connected ninja graph.
-	v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src))
-}
-
-func (v *CcApiVariant) Name() string {
-	version := String(v.properties.Version)
-	return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
-}
-
-func (v *CcApiVariant) Src() android.Path {
-	return v.src
-}
-
-func BuildApiVariantName(baseName string, variant string, version string) string {
-	names := []string{baseName, variant}
-	if version != "" {
-		names = append(names, version)
-	}
-
-	return strings.Join(names[:], ".") + multitree.GetApiImportSuffix()
-}
-
-// Implement ImageInterface to generate image variants
-func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
-func (v *CcApiVariant) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
-	return String(v.properties.Variant) == "llndk"
-}
-func (v *CcApiVariant) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
-	return String(v.properties.Variant) == "llndk"
-}
-func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return inList(String(v.properties.Variant), []string{"ndk", "apex"})
-}
-func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool       { return false }
-func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
-func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool  { return false }
-func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool      { return false }
-func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string   { return nil }
-func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string) {
-}
diff --git a/cc/linker.go b/cc/linker.go
index 0056817..1efacad 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -645,7 +645,7 @@
 	panic(fmt.Errorf("baseLinker doesn't know how to link"))
 }
 
-func (linker *baseLinker) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+func (linker *baseLinker) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
 	eval := module.ConfigurableEvaluator(ctx)
 	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, linker.Properties.Shared_libs.GetOrDefault(eval, nil)...)
 
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..01551ab 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,
@@ -421,41 +429,45 @@
 	// Also ensure that the ABI of the next API level (if there is one) matches
 	// this API level. *New* ABI is allowed, but any changes to APIs that exist
 	// in this API level are disallowed.
-	if !this.apiLevel.IsCurrent() && prebuiltAbiDump.Valid() {
+	if prebuiltAbiDump.Valid() {
 		nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
 		if nextApiLevel == nil {
 			panic(fmt.Errorf("could not determine which API level follows "+
 				"non-current API level %s", this.apiLevel))
 		}
-		nextAbiDiffPath := android.PathForModuleOut(ctx,
-			"abidiff_next.timestamp")
-		nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
-		missingNextPrebuiltError := fmt.Sprintf(
-			missingPrebuiltErrorTemplate, this.libraryName(ctx),
-			nextAbiDump.InvalidReason())
-		if !nextAbiDump.Valid() {
-			ctx.Build(pctx, android.BuildParams{
-				Rule:   android.ErrorRule,
-				Output: nextAbiDiffPath,
-				Args: map[string]string{
-					"error": missingNextPrebuiltError,
-				},
-			})
-		} else {
-			ctx.Build(pctx, android.BuildParams{
-				Rule: stgdiff,
-				Description: fmt.Sprintf(
-					"Comparing ABI to the next API level %s %s",
-					prebuiltAbiDump, nextAbiDump),
-				Output: nextAbiDiffPath,
-				Inputs: android.Paths{
-					prebuiltAbiDump.Path(), nextAbiDump.Path()},
-				Args: map[string]string{
-					"args": "--format=small --ignore=interface_addition",
-				},
-			})
+
+		// "current" ABI is not tracked.
+		if !nextApiLevel.IsCurrent() {
+			nextAbiDiffPath := android.PathForModuleOut(ctx,
+				"abidiff_next.timestamp")
+			nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
+			missingNextPrebuiltError := fmt.Sprintf(
+				missingPrebuiltErrorTemplate, this.libraryName(ctx),
+				nextAbiDump.InvalidReason())
+			if !nextAbiDump.Valid() {
+				ctx.Build(pctx, android.BuildParams{
+					Rule:   android.ErrorRule,
+					Output: nextAbiDiffPath,
+					Args: map[string]string{
+						"error": missingNextPrebuiltError,
+					},
+				})
+			} else {
+				ctx.Build(pctx, android.BuildParams{
+					Rule: stgdiff,
+					Description: fmt.Sprintf(
+						"Comparing ABI to the next API level %s %s",
+						prebuiltAbiDump, nextAbiDump),
+					Output: nextAbiDiffPath,
+					Inputs: android.Paths{
+						prebuiltAbiDump.Path(), nextAbiDump.Path()},
+					Args: map[string]string{
+						"args": "--format=small --ignore=interface_addition",
+					},
+				})
+			}
+			this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
 		}
-		this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
 	}
 }
 
@@ -478,7 +490,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/object.go b/cc/object.go
index a4f4c84..c89520a 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -203,7 +203,7 @@
 	return outputFile
 }
 
-func (object *objectLinker) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+func (object *objectLinker) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
 	eval := module.ConfigurableEvaluator(ctx)
 	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs.GetOrDefault(eval, nil)...)
 
diff --git a/cc/sabi.go b/cc/sabi.go
index 64eab41..2caf0d4 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -84,8 +84,8 @@
 
 type SAbiProperties struct {
 	// Whether ABI dump should be created for this module.
-	// Set by `sabiDepsMutator` if this module is a shared library that needs ABI check, or a static
-	// library that is depended on by an ABI checked library.
+	// Set by `sabiTransitionMutator` if this module is a shared library that needs ABI check,
+	// or a static library that is depended on by an ABI checked library.
 	ShouldCreateSourceAbiDump bool `blueprint:"mutated"`
 
 	// Include directories that may contain ABI information exported by a library.
@@ -121,10 +121,9 @@
 }
 
 // Returns a slice of strings that represent the ABI dumps generated for this module.
-func classifySourceAbiDump(ctx android.BaseModuleContext) []lsdumpTag {
+func classifySourceAbiDump(m *Module) []lsdumpTag {
 	result := []lsdumpTag{}
-	m := ctx.Module().(*Module)
-	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(ctx)
+	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(m)
 	if headerAbiChecker.explicitlyDisabled() {
 		return result
 	}
@@ -149,24 +148,37 @@
 	return result
 }
 
-// Called from sabiDepsMutator to check whether ABI dumps should be created for this module.
+type shouldCreateAbiDumpContext interface {
+	android.ModuleProviderContext
+	Module() android.Module
+	Config() android.Config
+}
+
+var _ shouldCreateAbiDumpContext = android.ModuleContext(nil)
+var _ shouldCreateAbiDumpContext = android.OutgoingTransitionContext(nil)
+
+// Called from sabiTransitionMutator to check whether ABI dumps should be created for this module.
 // ctx should be wrapping a native library type module.
-func shouldCreateSourceAbiDumpForLibrary(ctx android.BaseModuleContext) bool {
-	// Only generate ABI dump for device modules.
-	if !ctx.Device() {
+func shouldCreateSourceAbiDumpForLibrary(ctx shouldCreateAbiDumpContext) bool {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
 		return false
 	}
 
-	m := ctx.Module().(*Module)
+	// Only generate ABI dump for device modules.
+	if !m.Device() {
+		return false
+	}
 
 	// Only create ABI dump for native library module types.
 	if m.library == nil {
 		return false
 	}
 
-	// Create ABI dump for static libraries only if they are dependencies of ABI checked libraries.
+	// Don't create ABI dump for static libraries
+	// The sabi variant will be propagated to dependencies of ABI checked libraries.
 	if m.library.static() {
-		return m.sabi.shouldCreateSourceAbiDump()
+		return false
 	}
 
 	// Module is shared library type.
@@ -215,31 +227,64 @@
 			return false
 		}
 	}
-	return len(classifySourceAbiDump(ctx)) > 0
+	return len(classifySourceAbiDump(m)) > 0
 }
 
 // Mark the direct and transitive dependencies of libraries that need ABI check, so that ABI dumps
 // of their dependencies would be generated.
-func sabiDepsMutator(mctx android.TopDownMutatorContext) {
+type sabiTransitionMutator struct{}
+
+func (s *sabiTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (s *sabiTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
 	// Escape hatch to not check any ABI dump.
-	if mctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
-		return
+	if ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+		return ""
 	}
+
 	// Only create ABI dump for native shared libraries and their static library dependencies.
-	if m, ok := mctx.Module().(*Module); ok && m.sabi != nil {
-		if shouldCreateSourceAbiDumpForLibrary(mctx) {
-			// Mark this module so that .sdump / .lsdump for this library can be generated.
+	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+		if shouldCreateSourceAbiDumpForLibrary(ctx) {
+			if IsStaticDepTag(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
+				return "sabi"
+			}
+		} else if sourceVariation == "sabi" {
+			if IsWholeStaticLib(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
+				return "sabi"
+			}
+		}
+	}
+
+	return ""
+}
+
+func (s *sabiTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if incomingVariation == "" {
+		return ""
+	}
+
+	if incomingVariation == "sabi" {
+		if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+			return "sabi"
+		}
+	}
+
+	return ""
+}
+
+func (s *sabiTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+		if variation == "sabi" {
 			m.sabi.Properties.ShouldCreateSourceAbiDump = true
-			// Mark all of its static library dependencies.
-			mctx.VisitDirectDeps(func(child android.Module) {
-				depTag := mctx.OtherModuleDependencyTag(child)
-				if IsStaticDepTag(depTag) || depTag == reuseObjTag {
-					if c, ok := child.(*Module); ok && c.sabi != nil {
-						// Mark this module so that .sdump for this static library can be generated.
-						c.sabi.Properties.ShouldCreateSourceAbiDump = true
-					}
-				}
-			})
+			m.HideFromMake()
+			m.Properties.PreventInstall = true
+		} else if shouldCreateSourceAbiDumpForLibrary(ctx) {
+			// Escape hatch to not check any ABI dump.
+			if !ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+				m.sabi.Properties.ShouldCreateSourceAbiDump = true
+			}
 		}
 	}
 }
diff --git a/cc/sabi_test.go b/cc/sabi_test.go
new file mode 100644
index 0000000..6b8cc17
--- /dev/null
+++ b/cc/sabi_test.go
@@ -0,0 +1,66 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestSabi(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libsabi",
+			srcs: ["sabi.cpp"],
+			static_libs: ["libdirect"],
+			header_abi_checker: {
+				enabled: true,
+				symbol_file: "libsabi.map.txt",
+                ref_dump_dirs: ["abi-dumps"],
+			},
+		}
+
+		cc_library {
+			name: "libdirect",
+			srcs: ["direct.cpp"],
+			whole_static_libs: ["libtransitive"],
+		}
+
+		cc_library {
+			name: "libtransitive",
+			srcs: ["transitive.cpp"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithCcDefaultModules,
+	).RunTestWithBp(t, bp)
+
+	libsabiStatic := result.ModuleForTests("libsabi", "android_arm64_armv8-a_static_sabi")
+	sabiObjSDump := libsabiStatic.Output("obj/sabi.sdump")
+
+	libDirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static_sabi")
+	directObjSDump := libDirect.Output("obj/direct.sdump")
+
+	libTransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static_sabi")
+	transitiveObjSDump := libTransitive.Output("obj/transitive.sdump")
+
+	libsabiShared := result.ModuleForTests("libsabi", "android_arm64_armv8-a_shared")
+	sabiLink := libsabiShared.Rule("sAbiLink")
+
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), sabiObjSDump.Output.String())
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), directObjSDump.Output.String())
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), transitiveObjSDump.Output.String())
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 73b6271..094c36a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -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).Parallel()
 		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/cc/sdk.go b/cc/sdk.go
index dc1261d..5dd44d8 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -51,13 +51,6 @@
 				return []string{""}
 			}
 		}
-	case *CcApiVariant:
-		ccApiVariant, _ := ctx.Module().(*CcApiVariant)
-		if String(ccApiVariant.properties.Variant) == "ndk" {
-			return []string{"sdk"}
-		} else {
-			return []string{""}
-		}
 	}
 
 	return []string{""}
@@ -84,11 +77,6 @@
 				return incomingVariation
 			}
 		}
-	case *CcApiVariant:
-		ccApiVariant, _ := ctx.Module().(*CcApiVariant)
-		if String(ccApiVariant.properties.Variant) == "ndk" {
-			return "sdk"
-		}
 	}
 
 	if ctx.IsAddingDependency() {
diff --git a/cc/testing.go b/cc/testing.go
index 159f86c..14a6b7a 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -20,7 +20,6 @@
 
 	"android/soong/android"
 	"android/soong/genrule"
-	"android/soong/multitree"
 )
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -29,9 +28,6 @@
 	RegisterBinaryBuildComponents(ctx)
 	RegisterLibraryBuildComponents(ctx)
 	RegisterLibraryHeadersBuildComponents(ctx)
-	RegisterLibraryStubBuildComponents(ctx)
-
-	multitree.RegisterApiImportsModule(ctx)
 
 	ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
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/release_config/release_config_contributions/Android.bp b/cmd/release_config/release_config_contributions/Android.bp
new file mode 100644
index 0000000..6882ea2
--- /dev/null
+++ b/cmd/release_config/release_config_contributions/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "release-config-contributions",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-release_config_contributions",
+    pkgPath: "android/soong/cmd/release_config/release_config_contributions",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/release_config/release_config_contributions/main.go b/cmd/release_config/release_config_contributions/main.go
new file mode 100644
index 0000000..a954cf1
--- /dev/null
+++ b/cmd/release_config/release_config_contributions/main.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"slices"
+	"strings"
+
+	rc_lib "android/soong/cmd/release_config/release_config_lib"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+)
+
+type Flags struct {
+	// The path to the top of the workspace.  Default: ".".
+	top string
+
+	// Output file.
+	output string
+
+	// Format for output file
+	format string
+
+	// List of release config directories to process.
+	dirs rc_lib.StringList
+
+	// Disable warning messages
+	quiet bool
+
+	// Panic on errors.
+	debug bool
+}
+
+func sortDirectories(dirList []string) {
+	order := func(dir string) int {
+		switch {
+		// These three are always in this order.
+		case dir == "build/release":
+			return 1
+		case dir == "vendor/google_shared/build/release":
+			return 2
+		case dir == "vendor/google/release":
+			return 3
+		// Keep their subdirs in the same order.
+		case strings.HasPrefix(dir, "build/release/"):
+			return 21
+		case strings.HasPrefix(dir, "vendor/google_shared/build/release/"):
+			return 22
+		case strings.HasPrefix(dir, "vendor/google/release/"):
+			return 23
+		// Everything else sorts by directory path.
+		default:
+			return 99
+		}
+	}
+
+	slices.SortFunc(dirList, func(a, b string) int {
+		aOrder, bOrder := order(a), order(b)
+		if aOrder != bOrder {
+			return aOrder - bOrder
+		}
+		return strings.Compare(a, b)
+	})
+}
+
+func main() {
+	var flags Flags
+	topDir, err := rc_lib.GetTopDir()
+
+	// Handle the common arguments
+	flag.StringVar(&flags.top, "top", topDir, "path to top of workspace")
+	flag.Var(&flags.dirs, "dir", "path to a release config contribution directory. May be repeated")
+	flag.StringVar(&flags.format, "format", "pb", "output file format")
+	flag.StringVar(&flags.output, "output", "release_config_contributions.pb", "output file")
+	flag.BoolVar(&flags.debug, "debug", false, "turn on debugging output for errors")
+	flag.BoolVar(&flags.quiet, "quiet", false, "disable warning messages")
+	flag.Parse()
+
+	errorExit := func(err error) {
+		if flags.debug {
+			panic(err)
+		}
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+
+	if flags.quiet {
+		rc_lib.DisableWarnings()
+	}
+
+	if err = os.Chdir(flags.top); err != nil {
+		errorExit(err)
+	}
+
+	contributingDirsMap := make(map[string][]string)
+	for _, dir := range flags.dirs {
+		contributions, err := rc_lib.EnumerateReleaseConfigs(dir)
+		if err != nil {
+			errorExit(err)
+		}
+		for _, name := range contributions {
+			contributingDirsMap[name] = append(contributingDirsMap[name], dir)
+		}
+	}
+
+	releaseConfigNames := []string{}
+	for name := range contributingDirsMap {
+		releaseConfigNames = append(releaseConfigNames, name)
+	}
+	slices.Sort(releaseConfigNames)
+
+	message := &rc_proto.ReleaseConfigContributionsArtifacts{
+		ReleaseConfigContributionsArtifactList: []*rc_proto.ReleaseConfigContributionsArtifact{},
+	}
+	for _, name := range releaseConfigNames {
+		dirs := contributingDirsMap[name]
+		sortDirectories(dirs)
+		message.ReleaseConfigContributionsArtifactList = append(
+			message.ReleaseConfigContributionsArtifactList,
+			&rc_proto.ReleaseConfigContributionsArtifact{
+				Name:                    &name,
+				ContributingDirectories: dirs,
+			})
+	}
+
+	err = rc_lib.WriteFormattedMessage(flags.output, flags.format, message)
+	if err != nil {
+		errorExit(err)
+	}
+}
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index adf0e62..ee71336 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -280,11 +280,28 @@
 
 	directories := []string{}
 	valueDirectories := []string{}
+	// These path prefixes are exclusive for a release config.
+	// "A release config shall exist in at most one of these."
+	// If we find a benefit to generalizing this, we can do so at that time.
+	exclusiveDirPrefixes := []string{
+		"build/release",
+		"vendor/google_shared/build/release",
+	}
+	var exclusiveDir string
 	for idx, confDir := range configs.configDirs {
 		if _, ok := myDirsMap[idx]; ok {
 			directories = append(directories, confDir)
 		}
 		if _, ok := myValueDirsMap[idx]; ok {
+			for _, dir := range exclusiveDirPrefixes {
+				if strings.HasPrefix(confDir, dir) {
+					if exclusiveDir != "" && !strings.HasPrefix(exclusiveDir, dir) {
+						return fmt.Errorf("%s is declared in both %s and %s",
+							config.Name, exclusiveDir, confDir)
+					}
+					exclusiveDir = confDir
+				}
+			}
 			valueDirectories = append(valueDirectories, confDir)
 		}
 	}
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 97eb8f1..831ec02 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -248,6 +248,18 @@
 	return configs.configDirs[index], nil
 }
 
+// Return the (unsorted) release configs contributed to by `dir`.
+func EnumerateReleaseConfigs(dir string) ([]string, error) {
+	var ret []string
+	err := WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error {
+		// Strip off the trailing `.textproto` from the name.
+		name := filepath.Base(path)
+		ret = append(ret, name[:len(name)-10])
+		return err
+	})
+	return ret, err
+}
+
 func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
 	if _, err := os.Stat(path); err != nil {
 		return fmt.Errorf("%s does not exist\n", path)
diff --git a/cmd/release_config/release_config_proto/Android.bp b/cmd/release_config/release_config_proto/Android.bp
index c34d203..c6869b1 100644
--- a/cmd/release_config/release_config_proto/Android.bp
+++ b/cmd/release_config/release_config_proto/Android.bp
@@ -28,5 +28,6 @@
         "build_flags_declarations.pb.go",
         "build_flags_src.pb.go",
         "build_flags_out.pb.go",
+        "release_configs_contributions.pb.go",
     ],
 }
diff --git a/cmd/release_config/release_config_proto/regen.sh b/cmd/release_config/release_config_proto/regen.sh
index 23e3115..a92bfc0 100644
--- a/cmd/release_config/release_config_proto/regen.sh
+++ b/cmd/release_config/release_config_proto/regen.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
 
-aprotoc --go_out=paths=source_relative:. build_flags_src.proto build_flags_out.proto build_flags_common.proto build_flags_declarations.proto
+aprotoc --go_out=paths=source_relative:. build_flags_src.proto build_flags_out.proto build_flags_common.proto build_flags_declarations.proto release_configs_contributions.proto
diff --git a/cmd/release_config/release_config_proto/release_configs_contributions.pb.go b/cmd/release_config/release_config_proto/release_configs_contributions.pb.go
new file mode 100644
index 0000000..54854f1
--- /dev/null
+++ b/cmd/release_config/release_config_proto/release_configs_contributions.pb.go
@@ -0,0 +1,252 @@
+//
+// Copyright (C) 2025 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
+// source: release_configs_contributions.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type ReleaseConfigContributionsArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the release config.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The release config contribution directories that may contribute to this
+	// release config.
+	ContributingDirectories []string `protobuf:"bytes,2,rep,name=contributing_directories,json=contributingDirectories" json:"contributing_directories,omitempty"`
+}
+
+func (x *ReleaseConfigContributionsArtifact) Reset() {
+	*x = ReleaseConfigContributionsArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_release_configs_contributions_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigContributionsArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigContributionsArtifact) ProtoMessage() {}
+
+func (x *ReleaseConfigContributionsArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_release_configs_contributions_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReleaseConfigContributionsArtifact.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigContributionsArtifact) Descriptor() ([]byte, []int) {
+	return file_release_configs_contributions_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *ReleaseConfigContributionsArtifact) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *ReleaseConfigContributionsArtifact) GetContributingDirectories() []string {
+	if x != nil {
+		return x.ContributingDirectories
+	}
+	return nil
+}
+
+type ReleaseConfigContributionsArtifacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The artifacts
+	ReleaseConfigContributionsArtifactList []*ReleaseConfigContributionsArtifact `protobuf:"bytes,1,rep,name=release_config_contributions_artifact_list,json=releaseConfigContributionsArtifactList" json:"release_config_contributions_artifact_list,omitempty"`
+}
+
+func (x *ReleaseConfigContributionsArtifacts) Reset() {
+	*x = ReleaseConfigContributionsArtifacts{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_release_configs_contributions_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigContributionsArtifacts) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigContributionsArtifacts) ProtoMessage() {}
+
+func (x *ReleaseConfigContributionsArtifacts) ProtoReflect() protoreflect.Message {
+	mi := &file_release_configs_contributions_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReleaseConfigContributionsArtifacts.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigContributionsArtifacts) Descriptor() ([]byte, []int) {
+	return file_release_configs_contributions_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ReleaseConfigContributionsArtifacts) GetReleaseConfigContributionsArtifactList() []*ReleaseConfigContributionsArtifact {
+	if x != nil {
+		return x.ReleaseConfigContributionsArtifactList
+	}
+	return nil
+}
+
+var File_release_configs_contributions_proto protoreflect.FileDescriptor
+
+var file_release_configs_contributions_proto_rawDesc = []byte{
+	0x0a, 0x23, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x22, 0x73, 0x0a, 0x22, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a,
+	0x18, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69,
+	0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x17, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72,
+	0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0xc4, 0x01, 0x0a, 0x23, 0x52, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69,
+	0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73,
+	0x12, 0x9c, 0x01, 0x0a, 0x2a, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41,
+	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x26, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x42,
+	0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_release_configs_contributions_proto_rawDescOnce sync.Once
+	file_release_configs_contributions_proto_rawDescData = file_release_configs_contributions_proto_rawDesc
+)
+
+func file_release_configs_contributions_proto_rawDescGZIP() []byte {
+	file_release_configs_contributions_proto_rawDescOnce.Do(func() {
+		file_release_configs_contributions_proto_rawDescData = protoimpl.X.CompressGZIP(file_release_configs_contributions_proto_rawDescData)
+	})
+	return file_release_configs_contributions_proto_rawDescData
+}
+
+var file_release_configs_contributions_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_release_configs_contributions_proto_goTypes = []interface{}{
+	(*ReleaseConfigContributionsArtifact)(nil),  // 0: android.release_config_proto.ReleaseConfigContributionsArtifact
+	(*ReleaseConfigContributionsArtifacts)(nil), // 1: android.release_config_proto.ReleaseConfigContributionsArtifacts
+}
+var file_release_configs_contributions_proto_depIdxs = []int32{
+	0, // 0: android.release_config_proto.ReleaseConfigContributionsArtifacts.release_config_contributions_artifact_list:type_name -> android.release_config_proto.ReleaseConfigContributionsArtifact
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_release_configs_contributions_proto_init() }
+func file_release_configs_contributions_proto_init() {
+	if File_release_configs_contributions_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_release_configs_contributions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigContributionsArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_release_configs_contributions_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigContributionsArtifacts); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_release_configs_contributions_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_release_configs_contributions_proto_goTypes,
+		DependencyIndexes: file_release_configs_contributions_proto_depIdxs,
+		MessageInfos:      file_release_configs_contributions_proto_msgTypes,
+	}.Build()
+	File_release_configs_contributions_proto = out.File
+	file_release_configs_contributions_proto_rawDesc = nil
+	file_release_configs_contributions_proto_goTypes = nil
+	file_release_configs_contributions_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/release_configs_contributions.proto b/cmd/release_config/release_config_proto/release_configs_contributions.proto
new file mode 100644
index 0000000..bc7aeda
--- /dev/null
+++ b/cmd/release_config/release_config_proto/release_configs_contributions.proto
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2025 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+message ReleaseConfigContributionsArtifact {
+  // The name of the release config.
+  optional string name = 1;
+
+  // The release config contribution directories that may contribute to this
+  // release config.
+  repeated string contributing_directories = 2;
+}
+
+message ReleaseConfigContributionsArtifacts {
+  // The artifacts
+  repeated ReleaseConfigContributionsArtifact release_config_contributions_artifact_list = 1;
+}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 24a44b4..5b1ae54 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))
@@ -368,6 +375,7 @@
 	ctx.Register()
 	finalOutputFile, ninjaDeps := runSoongOnlyBuild(ctx)
 
+	ninjaDeps = append(ninjaDeps, configuration.ProductVariablesFileName)
 	ninjaDeps = append(ninjaDeps, usedEnvFile)
 	if shared.IsDebugging() {
 		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
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/Android.bp b/compliance/Android.bp
new file mode 100644
index 0000000..72c2f27
--- /dev/null
+++ b/compliance/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-compliance",
+    pkgPath: "android/soong/compliance",
+    deps: [
+        "soong-android",
+    ],
+    srcs: [
+        "notice.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+}
+
+notice_xml {
+    name: "notice_xml_system",
+    partition_name: "system",
+    visibility: [
+        "//build/make/target/product/generic",
+    ],
+}
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/notice.go b/compliance/notice.go
new file mode 100644
index 0000000..4fc83ab
--- /dev/null
+++ b/compliance/notice.go
@@ -0,0 +1,100 @@
+// 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 compliance
+
+import (
+	"path/filepath"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterNoticeXmlBuildComponents(android.InitRegistrationContext)
+}
+
+var PrepareForTestWithNoticeXmlBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(RegisterNoticeXmlBuildComponents),
+)
+
+var PrepareForTestWithNoticeXml = android.GroupFixturePreparers(
+	PrepareForTestWithNoticeXmlBuildComponents,
+)
+
+func RegisterNoticeXmlBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("notice_xml", NoticeXmlFactory)
+}
+
+var (
+	pctx = android.NewPackageContext("android/soong/compliance")
+
+	genNoticeXml = pctx.HostBinToolVariable("genNoticeXml", "gen_notice_xml")
+
+	// Command to generate NOTICE.xml.gz for a partition
+	genNoticeXmlRule = pctx.AndroidStaticRule("genNoticeXmlRule", blueprint.RuleParams{
+		Command: "rm -rf $out && " +
+			"${genNoticeXml} --output_file ${out} --metadata ${in} --partition ${partition} --product_out ${productOut} --soong_out ${soongOut}",
+		CommandDeps: []string{"${genNoticeXml}"},
+	}, "partition", "productOut", "soongOut")
+)
+
+func NoticeXmlFactory() android.Module {
+	m := &NoticeXmlModule{}
+	m.AddProperties(&m.props)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibFirst)
+	return m
+}
+
+type NoticeXmlModule struct {
+	android.ModuleBase
+
+	props noticeXmlProperties
+
+	outputFile  android.OutputPath
+	installPath android.InstallPath
+}
+
+type noticeXmlProperties struct {
+	Partition_name string
+}
+
+func (nx *NoticeXmlModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	output := android.PathForModuleOut(ctx, "NOTICE.xml.gz")
+	metadataDb := android.PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   genNoticeXmlRule,
+		Input:  metadataDb,
+		Output: output,
+		Args: map[string]string{
+			"productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()),
+			"soongOut":   ctx.Config().SoongOutDir(),
+			"partition":  nx.props.Partition_name,
+		},
+	})
+
+	nx.outputFile = output.OutputPath
+
+	if android.Bool(ctx.Config().ProductVariables().UseSoongSystemImage) {
+		nx.installPath = android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
+		ctx.InstallFile(nx.installPath, "NOTICE.xml.gz", nx.outputFile)
+	}
+}
+
+func (nx *NoticeXmlModule) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(nx.outputFile),
+	}}
+}
diff --git a/compliance/notice_test.go b/compliance/notice_test.go
new file mode 100644
index 0000000..6187e53
--- /dev/null
+++ b/compliance/notice_test.go
@@ -0,0 +1,38 @@
+// 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 compliance
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+var prepareForNoticeXmlTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithArchMutator,
+	PrepareForTestWithNoticeXml,
+)
+
+func TestPrebuiltEtcOutputFile(t *testing.T) {
+	result := prepareForNoticeXmlTest.RunTestWithBp(t, `
+		notice_xml {
+			name: "notice_xml_system",
+			partition_name: "system",
+		}
+	`)
+
+	m := result.Module("notice_xml_system", "android_arm64_armv8-a").(*NoticeXmlModule)
+	android.AssertStringEquals(t, "output file", "NOTICE.xml.gz", m.outputFile.Base())
+}
\ No newline at end of file
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/dexpreopt/config.go b/dexpreopt/config.go
index fe6317c..84d4f10 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -191,6 +191,10 @@
 	ForceCreateAppImage bool
 
 	PresignedPrebuilt bool
+
+	// ApexPartition is the partition in which the dexpreopt files of apex system server jars (if any) are installed.
+	// This is a noop unless the module is apex system server jar.
+	ApexPartition string
 }
 
 type globalSoongConfigSingleton struct{}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 5616483..7a39fa1 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -219,9 +219,9 @@
 }
 
 // Returns the location to the odex file for the dex file at `path`.
-func ToOdexPath(path string, arch android.ArchType) string {
+func ToOdexPath(path string, arch android.ArchType, partition string) string {
 	if strings.HasPrefix(path, "/apex/") {
-		return filepath.Join("/system/framework/oat", arch.String(),
+		return filepath.Join(partition, "framework/oat", arch.String(),
 			strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
 	}
 
@@ -245,7 +245,7 @@
 
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
 	odexSymbolsPath := odexPath.ReplaceExtension(ctx, "symbols.odex")
-	odexInstallPath := ToOdexPath(module.DexLocation, arch)
+	odexInstallPath := ToOdexPath(module.DexLocation, arch, module.ApexPartition)
 	if odexOnSystemOther(module, global) {
 		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 6f7d3bb..7b0f51f 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -42,12 +42,14 @@
 }
 
 func testApexModuleConfig(ctx android.PathContext, name, apexName string) *ModuleConfig {
-	return createTestModuleConfig(
+	ret := createTestModuleConfig(
 		name,
 		fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, name),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+	ret.ApexPartition = "/system"
+	return ret
 }
 
 func testPlatformSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
@@ -221,6 +223,49 @@
 	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
 }
 
+// Same as `TestDexPreoptApexSystemServerJars`, but the apex jar is in /system_ext
+func TestDexPreoptApexSystemServerJarsSystemExt(t *testing.T) {
+	// modify the global variable for test
+	var oldDexpreoptRunningInSoong = DexpreoptRunningInSoong
+	DexpreoptRunningInSoong = true
+
+	// test begin
+	config := android.TestConfig("out", nil, "", nil)
+	ctx := android.BuilderContextForTesting(config)
+	globalSoong := globalSoongConfigForTests(ctx)
+	global := GlobalConfigForTests(ctx)
+	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	module.ApexPartition = "/system_ext"
+	productPackages := android.PathForTesting("product_packages.txt")
+
+	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
+		[]string{"com.android.apex1:service-A"})
+
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wantInstalls := android.RuleBuilderInstalls{
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system_ext/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system_ext/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+	}
+
+	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+
+	android.AssertStringListContains(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// rule with apex sscp cp as false
+	rule, err = GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	android.AssertStringListDoesNotContain(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// cleanup the global variable for test
+	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
+}
+
 func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
diff --git a/docs/OWNERS b/docs/OWNERS
new file mode 100644
index 0000000..776beca
--- /dev/null
+++ b/docs/OWNERS
@@ -0,0 +1 @@
+per-file map_files.md = danalbert@google.com
diff --git a/docs/map_files.md b/docs/map_files.md
index e1ddefc..8d6af87 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -88,12 +88,17 @@
 
 ### introduced
 
-Indicates the version in which an API was first introduced. For example,
-`introduced=21` specifies that the API was first added (or first made public) in
-API level 21. This tag can be applied to either a version definition or an
-individual symbol. If applied to a version, all symbols contained in the version
-will have the tag applied. An `introduced` tag on a symbol overrides the value
-set for the version, if both are defined.
+Indicates the version in which an API was first introduced in the NDK. For
+example, `introduced=21` specifies that the API was first added (or first made
+public) in API level 21. This tag can be applied to either a version definition
+or an individual symbol. If applied to a version, all symbols contained in the
+version will have the tag applied. An `introduced` tag on a symbol overrides the
+value set for the version, if both are defined.
+
+The `introduced` tag should only be used with NDK APIs. Other API surface tags
+(such as `apex`) will override `introduced`. APIs that are in the NDK should
+never use tags like `apex`, and APIs that are not in the NDK should never use
+`introduced`.
 
 Note: The map file alone does not contain all the information needed to
 determine which API level an API was added in. The `first_version` property of
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..8e043b8 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -11,6 +11,7 @@
         "soong-android",
     ],
     srcs: [
+        "adb_keys.go",
         "install_symlink.go",
         "otacerts_zip.go",
         "prebuilt_etc.go",
@@ -20,4 +21,6 @@
         "install_symlink_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/etc/adb_keys.go b/etc/adb_keys.go
new file mode 100644
index 0000000..1bce2f1
--- /dev/null
+++ b/etc/adb_keys.go
@@ -0,0 +1,66 @@
+// 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 etc
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("adb_keys", AdbKeysModuleFactory)
+}
+
+type AdbKeysModule struct {
+	android.ModuleBase
+	outputPath  android.OutputPath
+	installPath android.InstallPath
+}
+
+func AdbKeysModuleFactory() android.Module {
+	module := &AdbKeysModule{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+func (m *AdbKeysModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	productVariables := ctx.Config().ProductVariables()
+	if !(android.Bool(productVariables.Debuggable) && len(android.String(productVariables.AdbKeys)) > 0) {
+		m.Disable()
+		m.SkipInstall()
+		return
+	}
+
+	m.outputPath = android.PathForModuleOut(ctx, "adb_keys").OutputPath
+	input := android.ExistentPathForSource(ctx, android.String(productVariables.AdbKeys))
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: m.outputPath,
+		Input:  input.Path(),
+	})
+	m.installPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().ProductPath(), "etc/security")
+	ctx.InstallFile(m.installPath, "adb_keys", m.outputPath)
+}
+
+func (m *AdbKeysModule) AndroidMkEntries() []android.AndroidMkEntries {
+	if m.IsSkipInstall() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{
+		{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(m.outputPath),
+		}}
+}
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
index 5c047bc..608fccd 100644
--- a/filesystem/aconfig_files.go
+++ b/filesystem/aconfig_files.go
@@ -76,9 +76,13 @@
 		cmd.ImplicitOutput(outputPath)
 		f.appendToEntry(ctx, outputPath)
 	}
-	generatePartitionAconfigStorageFile("package_map", "package.map")
-	generatePartitionAconfigStorageFile("flag_map", "flag.map")
-	generatePartitionAconfigStorageFile("flag_val", "flag.val")
+
+	if ctx.Config().ReleaseCreateAconfigStorageFile() {
+		generatePartitionAconfigStorageFile("package_map", "package.map")
+		generatePartitionAconfigStorageFile("flag_map", "flag.map")
+		generatePartitionAconfigStorageFile("flag_val", "flag.val")
+		generatePartitionAconfigStorageFile("flag_info", "flag.info")
+	}
 
 	android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String())
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index a8f97e3..09d8fba 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -35,9 +35,9 @@
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("android_filesystem", filesystemFactory)
+	ctx.RegisterModuleType("android_filesystem", FilesystemFactory)
 	ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory)
-	ctx.RegisterModuleType("android_system_image", systemImageFactory)
+	ctx.RegisterModuleType("android_system_image", SystemImageFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer_defaults", avbAddHashFooterDefaultsFactory)
 	ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory)
@@ -49,7 +49,7 @@
 	android.PackagingBase
 	android.DefaultableModuleBase
 
-	properties filesystemProperties
+	properties FilesystemProperties
 
 	// Function that builds extra files under the root directory and returns the files
 	buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
@@ -71,7 +71,7 @@
 	Name   *string
 }
 
-type filesystemProperties struct {
+type FilesystemProperties struct {
 	// When set to true, sign the image with avbtool. Default is false.
 	Use_avb *bool
 
@@ -136,10 +136,13 @@
 	// Install aconfig_flags.pb file for the modules installed in this partition.
 	Gen_aconfig_flags_pb *bool
 
-	// Update the Base_dir of the $PRODUCT_OUT directory with the packaging files.
-	Update_product_out *bool
-
 	Fsverity fsverityProperties
+
+	// If this property is set to true, the filesystem will call ctx.UncheckedModule(), causing
+	// it to not be built on checkbuilds. Used for the automatic migration from make to soong
+	// build modules, where we want to emit some not-yet-working filesystems and we don't want them
+	// to be built.
+	Unchecked_module *bool `blueprint:"mutated"`
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -147,17 +150,17 @@
 // modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
 // The modules are placed in the filesystem image just like they are installed to the ordinary
 // partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
-func filesystemFactory() android.Module {
+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)
 }
@@ -180,6 +183,13 @@
 	unknown
 )
 
+type FilesystemInfo struct {
+	// A text file containing the list of paths installed on the partition.
+	FileListFile android.Path
+}
+
+var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()
+
 func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
 	typeStr := proptools.StringDefault(f.properties.Type, "ext4")
 	switch typeStr {
@@ -230,6 +240,14 @@
 
 	f.fileListFile = android.PathForModuleOut(ctx, "fileList").OutputPath
 	android.WriteFileRule(ctx, f.fileListFile, f.installedFilesList())
+
+	android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
+		FileListFile: f.fileListFile,
+	})
+
+	if proptools.Bool(f.properties.Unchecked_module) {
+		ctx.UncheckedModule()
+	}
 }
 
 func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.OutputPath) {
@@ -335,7 +353,7 @@
 }
 
 func (f *filesystem) copyFilesToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
-	if !proptools.Bool(f.properties.Update_product_out) {
+	if f.Name() != ctx.Config().SoongDefinedSystemImage() {
 		return
 	}
 	installPath := android.PathForModuleInPartitionInstall(ctx, f.partitionName())
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index a8fd368..57239ae 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -27,21 +27,25 @@
 
 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.
 // Currently, the only difference is the inclusion of linker.config.pb file which specifies
 // the provided and the required libraries to and from APEXes.
-func systemImageFactory() android.Module {
+func SystemImageFactory() android.Module {
 	module := &systemImage{}
 	module.AddProperties(&module.properties)
 	module.filesystem.buildExtraFiles = module.buildExtraFiles
 	module.filesystem.filterPackagingSpec = module.filterPackagingSpec
-	initFilesystemModule(&module.filesystem)
+	initFilesystemModule(module, &module.filesystem)
 	return module
 }
 
+func (s systemImage) FsProps() FilesystemProperties {
+	return s.filesystem.properties
+}
+
 func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths {
 	if s.filesystem.properties.Partition_type != nil {
 		ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.")
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 3a9a64d..1d64796 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -213,6 +213,7 @@
 	ctx.InstallFile(v.installDir, v.installFileName(), v.output)
 
 	ctx.SetOutputFiles([]android.Path{v.output}, "")
+	android.SetProvider(ctx, android.AndroidMkInfoProvider, v.prepareAndroidMKProviderInfo())
 }
 
 // Returns the embedded shell command that prints the rollback index
@@ -265,20 +266,17 @@
 	return result
 }
 
-var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil)
-
-// Implements android.AndroidMkEntriesProvider
-func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(v.output),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", v.installDir.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
-			},
+func (v *vbmeta) prepareAndroidMKProviderInfo() *android.AndroidMkProviderInfo {
+	providerData := android.AndroidMkProviderInfo{
+		PrimaryInfo: android.AndroidMkInfo{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(v.output),
+			EntryMap:   make(map[string][]string),
 		},
-	}}
+	}
+	providerData.PrimaryInfo.SetString("LOCAL_MODULE_PATH", v.installDir.String())
+	providerData.PrimaryInfo.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
+	return &providerData
 }
 
 var _ Filesystem = (*vbmeta)(nil)
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
new file mode 100644
index 0000000..9fa9557
--- /dev/null
+++ b/fsgen/Android.bp
@@ -0,0 +1,25 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-fsgen",
+    pkgPath: "android/soong/fsgen",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-filesystem",
+    ],
+    srcs: [
+        "filesystem_creator.go",
+    ],
+    testSrcs: [
+        "filesystem_creator_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
+
+soong_filesystem_creator {
+    name: "soong_filesystem_creator",
+}
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
new file mode 100644
index 0000000..a9f4256
--- /dev/null
+++ b/fsgen/filesystem_creator.go
@@ -0,0 +1,217 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+	"android/soong/android"
+	"android/soong/filesystem"
+	"crypto/sha256"
+	"fmt"
+	"strconv"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var pctx = android.NewPackageContext("android/soong/fsgen")
+
+func init() {
+	registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
+}
+
+type filesystemCreatorProps struct {
+	Generated_partition_types   []string `blueprint:"mutated"`
+	Unsupported_partition_types []string `blueprint:"mutated"`
+}
+
+type filesystemCreator struct {
+	android.ModuleBase
+
+	properties filesystemCreatorProps
+}
+
+func filesystemCreatorFactory() android.Module {
+	module := &filesystemCreator{}
+
+	android.InitAndroidModule(module)
+	module.AddProperties(&module.properties)
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		module.createInternalModules(ctx)
+	})
+
+	return module
+}
+
+func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
+	for _, partitionType := range []string{"system"} {
+		if f.createPartition(ctx, partitionType) {
+			f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
+		}
+	}
+}
+
+func (f *filesystemCreator) generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
+	prefix := "soong"
+	if cfg.HasDeviceProduct() {
+		prefix = cfg.DeviceProduct()
+	}
+	return fmt.Sprintf("%s_generated_%s_image", prefix, partitionType)
+}
+
+// Creates a soong module to build the given partition. Returns false if we can't support building
+// it.
+func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitionType string) bool {
+	baseProps := &struct {
+		Name *string
+	}{
+		Name: proptools.StringPtr(f.generatedModuleNameForPartition(ctx.Config(), partitionType)),
+	}
+
+	fsProps := &filesystem.FilesystemProperties{}
+
+	// Don't build this module on checkbuilds, the soong-built partitions are still in-progress
+	// and sometimes don't build.
+	fsProps.Unchecked_module = proptools.BoolPtr(true)
+
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
+
+	// BOARD_AVB_ENABLE
+	fsProps.Use_avb = proptools.BoolPtr(partitionVars.BoardAvbEnable)
+	// BOARD_AVB_KEY_PATH
+	fsProps.Avb_private_key = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
+	// BOARD_AVB_ALGORITHM
+	fsProps.Avb_algorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
+	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX
+	if rollbackIndex, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64); err == nil {
+		fsProps.Rollback_index = proptools.Int64Ptr(rollbackIndex)
+	}
+
+	fsProps.Partition_name = proptools.StringPtr(partitionType)
+	// BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE
+	fsProps.Type = proptools.StringPtr(specificPartitionVars.BoardFileSystemType)
+	if *fsProps.Type != "ext4" {
+		// Currently the android_filesystem module type only supports ext4:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/soong/filesystem/filesystem.go;l=416;drc=98047cfd07944b297a12d173453bc984806760d2
+		return false
+	}
+
+	fsProps.Base_dir = proptools.StringPtr(partitionType)
+
+	fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
+
+	// Identical to that of the generic_system_image
+	fsProps.Fsverity.Inputs = []string{
+		"etc/boot-image.prof",
+		"etc/dirty-image-objects",
+		"etc/preloaded-classes",
+		"etc/classpaths/*.pb",
+		"framework/*",
+		"framework/*/*",     // framework/{arch}
+		"framework/oat/*/*", // framework/oat/{arch}
+	}
+
+	// system_image properties that are not set:
+	// - filesystemProperties.Avb_hash_algorithm
+	// - filesystemProperties.File_contexts
+	// - filesystemProperties.Dirs
+	// - filesystemProperties.Symlinks
+	// - filesystemProperties.Fake_timestamp
+	// - filesystemProperties.Uuid
+	// - filesystemProperties.Mount_point
+	// - filesystemProperties.Include_make_built_files
+	// - filesystemProperties.Build_logtags
+	// - filesystemProperties.Fsverity.Libs
+	// - systemImageProperties.Linker_config_src
+	var module android.Module
+	if partitionType == "system" {
+		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
+	} else {
+		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
+	}
+	module.HideFromMake()
+	return true
+}
+
+func (f *filesystemCreator) createDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
+	partitionModuleName := f.generatedModuleNameForPartition(ctx.Config(), partitionType)
+	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
+	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
+	if !ok {
+		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
+	}
+	makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
+	// For now, don't allowlist anything. The test will fail, but that's fine in the current
+	// early stages where we're just figuring out what we need
+	emptyAllowlistFile := android.PathForModuleOut(ctx, fmt.Sprintf("allowlist_%s.txt", partitionModuleName))
+	android.WriteFileRule(ctx, emptyAllowlistFile, "")
+	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().BuiltTool("file_list_diff").
+		Input(makeFileList).
+		Input(filesystemInfo.FileListFile).
+		Text(partitionModuleName).
+		FlagWithInput("--allowlists ", emptyAllowlistFile)
+	builder.Command().Text("touch").Output(diffTestResultFile)
+	builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
+	return diffTestResultFile
+}
+
+func createFailingCommand(ctx android.ModuleContext, message string) android.Path {
+	hasher := sha256.New()
+	hasher.Write([]byte(message))
+	filename := fmt.Sprintf("failing_command_%x.txt", hasher.Sum(nil))
+	file := android.PathForModuleOut(ctx, filename)
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Textf("echo %s", proptools.NinjaAndShellEscape(message))
+	builder.Command().Text("exit 1 #").Output(file)
+	builder.Build("failing command "+filename, "failing command "+filename)
+	return file
+}
+
+type systemImageDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var generatedFilesystemDepTag systemImageDepTagType
+
+func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, partitionType := range f.properties.Generated_partition_types {
+		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, f.generatedModuleNameForPartition(ctx.Config(), partitionType))
+	}
+}
+
+func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if ctx.ModuleDir() != "build/soong/fsgen" {
+		ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
+	}
+	f.HideFromMake()
+
+	var diffTestFiles []android.Path
+	for _, partitionType := range f.properties.Generated_partition_types {
+		diffTestFiles = append(diffTestFiles, f.createDiffTest(ctx, partitionType))
+	}
+	for _, partitionType := range f.properties.Unsupported_partition_types {
+		diffTestFiles = append(diffTestFiles, createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType)))
+	}
+	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
+}
diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go
new file mode 100644
index 0000000..554b66b
--- /dev/null
+++ b/fsgen/filesystem_creator_test.go
@@ -0,0 +1,88 @@
+// 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 fsgen
+
+import (
+	"android/soong/android"
+	"android/soong/filesystem"
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var prepareForTestWithFsgenBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func TestFileSystemCreatorSystemImageProps(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.BoardAvbEnable = true
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardAvbKeyPath:       "external/avb/test/data/testkey_rsa4096.pem",
+						BoardAvbAlgorithm:     "SHA256_RSA4096",
+						BoardAvbRollbackIndex: "0",
+						BoardFileSystemType:   "ext4",
+					},
+				}
+		}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+		}),
+	).RunTest(t)
+
+	fooSystem := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().(interface {
+		FsProps() filesystem.FilesystemProperties
+	})
+	android.AssertBoolEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_ENABLE'",
+		true,
+		proptools.Bool(fooSystem.FsProps().Use_avb),
+	)
+	android.AssertStringEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_KEY_PATH'",
+		"external/avb/test/data/testkey_rsa4096.pem",
+		proptools.String(fooSystem.FsProps().Avb_private_key),
+	)
+	android.AssertStringEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_ALGORITHM'",
+		"SHA256_RSA4096",
+		proptools.String(fooSystem.FsProps().Avb_algorithm),
+	)
+	android.AssertIntEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_SYSTEM_ROLLBACK_INDEX'",
+		0,
+		proptools.Int(fooSystem.FsProps().Rollback_index),
+	)
+	android.AssertStringEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE'",
+		"ext4",
+		proptools.String(fooSystem.FsProps().Type),
+	)
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 306d65e..a059837 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -449,7 +449,7 @@
 	}
 }
 
-func IsValid(ctx android.ConfigAndErrorContext, fuzzModule FuzzModule) bool {
+func IsValid(ctx android.ConfigurableEvaluatorContext, fuzzModule FuzzModule) bool {
 	// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
 	// fuzz targets we're going to package anyway.
 	if !fuzzModule.Enabled(ctx) || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 7331741..49df480 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -22,4 +22,56 @@
         "genrule_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
+}
+
+genrule {
+    name: "nsjail_genrule_test_input",
+    cmd: "echo nsjail_genrule_test_input > $(out)",
+    out: ["nsjail_genrule_test_input.txt"],
+}
+
+// Pseudo-test that's run on checkbuilds to verify consistent directory
+// structure for genrules using sbox or nsjail.
+genrule_defaults {
+    name: "nsjail_genrule_test_gen_defaults",
+    // verify both relative paths and its contents
+    cmd: "(echo $(out) $(genDir) && sha256sum " +
+        "$(location get_clang_version) " +
+        "$(location py3-cmd) " +
+        "$(location genrule.go) " +
+        "$(location :nsjail_genrule_test_input) " +
+        "$(locations *.go)) | sed 's@\\./@@g' > $(out)",
+    tools: [
+        "get_clang_version", // random tool
+        "py3-cmd", // random prebuilt tool
+    ],
+    tool_files: ["genrule.go"], // random local file
+    srcs: [
+        ":nsjail_genrule_test_input", // random OutputFileProducer
+        "*.go", // random glob
+    ],
+    out: ["nsjail_genrule_test.txt"],
+}
+
+genrule {
+    name: "nsjail_genrule_test_gen_without_nsjail",
+    defaults: ["nsjail_genrule_test_gen_defaults"],
+}
+
+genrule {
+    name: "nsjail_genrule_test_gen_with_nsjail",
+    defaults: ["nsjail_genrule_test_gen_defaults"],
+    use_nsjail: true,
+}
+
+genrule {
+    name: "nsjail_genrule_test",
+    srcs: [
+        ":nsjail_genrule_test_gen_without_nsjail",
+        ":nsjail_genrule_test_gen_with_nsjail",
+    ],
+    cmd: "diff $(in) > $(out)",
+    out: ["nsjail_genrule_test"],
 }
diff --git a/genrule/genrule.go b/genrule/genrule.go
index fd72d3c..e5222a4 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -21,11 +21,11 @@
 import (
 	"fmt"
 	"io"
+	"path/filepath"
 	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -211,6 +211,9 @@
 	// For gensrsc sharding.
 	shard  int
 	shards int
+
+	// For nsjail tasks
+	useNsjail bool
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -314,16 +317,14 @@
 	if len(g.properties.Tools) > 0 {
 		seenTools := make(map[string]bool)
 
-		ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+		ctx.VisitDirectDepsAllowDisabled(func(module android.Module) {
 			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
 			case hostToolDependencyTag:
 				tool := ctx.OtherModuleName(module)
-				if m, ok := module.(android.Module); ok {
-					// Necessary to retrieve any prebuilt replacement for the tool, since
-					// toolDepsMutator runs too late for the prebuilt mutators to have
-					// replaced the dependency.
-					module = android.PrebuiltGetPreferred(ctx, m)
-				}
+				// Necessary to retrieve any prebuilt replacement for the tool, since
+				// toolDepsMutator runs too late for the prebuilt mutators to have
+				// replaced the dependency.
+				module = android.PrebuiltGetPreferred(ctx, module)
 
 				switch t := module.(type) {
 				case android.HostToolProvider:
@@ -365,11 +366,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
@@ -460,21 +456,26 @@
 
 		// Pick a unique path outside the task.genDir for the sbox manifest textproto,
 		// a unique rule name, and the user-visible description.
-		manifestName := "genrule.sbox.textproto"
+		var rule *android.RuleBuilder
 		desc := "generate"
 		name := "generator"
-		if task.shards > 0 {
-			manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
-			desc += " " + strconv.Itoa(task.shard)
-			name += strconv.Itoa(task.shard)
-		} else if len(task.out) == 1 {
-			desc += " " + task.out[0].Base()
+		if task.useNsjail {
+			rule = android.NewRuleBuilder(pctx, ctx).Nsjail(task.genDir, android.PathForModuleOut(ctx, "nsjail_build_sandbox"))
+		} else {
+			manifestName := "genrule.sbox.textproto"
+			if task.shards > 0 {
+				manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
+				desc += " " + strconv.Itoa(task.shard)
+				name += strconv.Itoa(task.shard)
+			} else if len(task.out) == 1 {
+				desc += " " + task.out[0].Base()
+			}
+
+			manifestPath := android.PathForModuleOut(ctx, manifestName)
+
+			// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
+			rule = getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
 		}
-
-		manifestPath := android.PathForModuleOut(ctx, manifestName)
-
-		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
-		rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
 		if Bool(g.properties.Write_if_changed) {
 			rule.Restat()
 		}
@@ -575,6 +576,15 @@
 			cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
 		}
 
+		if task.useNsjail {
+			for _, input := range task.in {
+				// can fail if input is a file.
+				if paths, err := ctx.GlobWithDeps(filepath.Join(input.String(), "**/*"), nil); err == nil {
+					rule.NsjailImplicits(android.PathsForSource(ctx, paths))
+				}
+			}
+		}
+
 		// Create the rule to run the genrule command inside sbox.
 		rule.Build(name, desc)
 
@@ -838,15 +848,18 @@
 	properties := &genRuleProperties{}
 
 	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
+		useNsjail := Bool(properties.Use_nsjail)
+
 		outs := make(android.WritablePaths, len(properties.Out))
 		for i, out := range properties.Out {
 			outs[i] = android.PathForModuleGen(ctx, out)
 		}
 		return []generateTask{{
-			in:     srcFiles,
-			out:    outs,
-			genDir: android.PathForModuleGen(ctx),
-			cmd:    rawCommand,
+			in:        srcFiles,
+			out:       outs,
+			genDir:    android.PathForModuleGen(ctx),
+			cmd:       rawCommand,
+			useNsjail: useNsjail,
 		}}
 	}
 
@@ -861,6 +874,8 @@
 }
 
 type genRuleProperties struct {
+	Use_nsjail *bool
+
 	// names of the output files that will be generated
 	Out []string `android:"arch_variant"`
 }
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..6ee924f
--- /dev/null
+++ b/golang/golang.go
@@ -0,0 +1,137 @@
+// 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.HostSupportedNoCross, 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..1101d7a 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -72,7 +72,7 @@
         "rro.go",
         "sdk.go",
         "sdk_library.go",
-        "sdk_library_external.go",
+        "sdk_library_internal.go",
         "support_libraries.go",
         "system_modules.go",
         "systemserver_classpath_fragment.go",
@@ -120,4 +120,5 @@
         "test_spec_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/java/aar.go b/java/aar.go
index b5e24c4..41cc24a 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -45,7 +45,7 @@
 	ctx.RegisterModuleType("android_library_import", AARImportFactory)
 	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator)
+		ctx.Transition("propagate_rro_enforcement", &propagateRROEnforcementTransitionMutator{})
 	})
 }
 
@@ -151,15 +151,67 @@
 	path   android.Path
 }
 
-// Propagate RRO enforcement flag to static lib dependencies transitively.
-func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) {
+// Propagate RRO enforcement flag to static lib dependencies transitively.  If EnforceRROGlobally is set then
+// all modules will use the "" variant.  If specific modules have RRO enforced, then modules (usually apps) with
+// RRO enabled will use the "" variation for themselves, but use the "rro" variant of direct and transitive static
+// android_library dependencies.
+type propagateRROEnforcementTransitionMutator struct{}
+
+func (p propagateRROEnforcementTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	// Never split modules, apps with or without RRO enabled use the "" variant, static android_library dependencies
+	// will use create the "rro" variant from incoming tranisitons.
+	return []string{""}
+}
+
+func (p propagateRROEnforcementTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	// Non-static dependencies are not involved in RRO and always use the empty variant.
+	if ctx.DepTag() != staticLibTag {
+		return ""
+	}
+
 	m := ctx.Module()
-	if d, ok := m.(AndroidLibraryDependency); ok && d.IsRROEnforced(ctx) {
-		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
-			if a, ok := d.(AndroidLibraryDependency); ok {
-				a.SetRROEnforcedForDependent(true)
-			}
-		})
+	if _, ok := m.(AndroidLibraryDependency); ok {
+		// If RRO is enforced globally don't bother using "rro" variants, the empty variant will have RRO enabled.
+		if ctx.Config().EnforceRROGlobally() {
+			return ""
+		}
+
+		// If RRO is enabled for this module use the "rro" variants of static dependencies.  IncomingTransition will
+		// rewrite this back to "" if the dependency is not an android_library.
+		if ctx.Config().EnforceRROForModule(ctx.Module().Name()) {
+			return "rro"
+		}
+	}
+
+	return sourceVariation
+}
+
+func (p propagateRROEnforcementTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	// Propagate the "rro" variant to android_library modules, but use the empty variant for everything else.
+	if incomingVariation == "rro" {
+		m := ctx.Module()
+		if _, ok := m.(AndroidLibraryDependency); ok {
+			return "rro"
+		}
+		return ""
+	}
+
+	return ""
+}
+
+func (p propagateRROEnforcementTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	m := ctx.Module()
+	if d, ok := m.(AndroidLibraryDependency); ok {
+		if variation == "rro" {
+			// This is the "rro" variant of a module that has both variants, mark this one as RRO enabled and
+			// hide it from make to avoid collisions with the non-RRO empty variant.
+			d.SetRROEnforcedForDependent(true)
+			m.HideFromMake()
+		} else if ctx.Config().EnforceRROGlobally() {
+			// RRO is enabled globally, mark it enabled for this module, but there is only one variant so no
+			// need to hide it from make.
+			d.SetRROEnforcedForDependent(true)
+		}
 	}
 }
 
@@ -431,9 +483,9 @@
 	}
 
 	linkFlags = append(linkFlags, "--no-static-lib-packages")
-	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
-		// When building an android_library using ResourceProcessorBusyBox pass --merge-only to skip resource
-		// references validation until the final app link step when all static libraries are present.
+	if a.isLibrary {
+		// Pass --merge-only to skip resource references validation until the final
+		// app link step when when all static libraries are present.
 		linkFlags = append(linkFlags, "--merge-only")
 	}
 
diff --git a/java/androidmk.go b/java/androidmk.go
index a1bc904..2dff6cd 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -313,6 +313,7 @@
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetBool("LOCAL_STRIP_MODULE", false)
+					entries.AddStrings("LOCAL_REQUIRED_MODULES", binary.androidMkNamesOfJniLibs...)
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -415,7 +416,7 @@
 				} else {
 					var names []string
 					for _, jniLib := range app.jniLibs {
-						names = append(names, jniLib.name)
+						names = append(names, jniLib.name+":"+jniLib.target.Arch.ArchType.Bitness())
 					}
 					entries.AddStrings("LOCAL_REQUIRED_MODULES", names...)
 				}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 243a279..1d98b18 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -286,7 +286,7 @@
 	}{
 		{
 			name:     "app",
-			expected: []string{"libjni"},
+			expected: []string{"libjni:64"},
 		},
 		{
 			name:     "app_embedded",
diff --git a/java/app.go b/java/app.go
index 4ac42a7..dd99675 100644
--- a/java/app.go
+++ b/java/app.go
@@ -63,6 +63,16 @@
 	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
 }
 
+type AppInfo struct {
+	// Updatable is set to the value of the updatable property
+	Updatable bool
+
+	// TestHelperApp is true if the module is a android_test_helper_app
+	TestHelperApp bool
+}
+
+var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
+
 // AndroidManifest.xml merging
 // package splits
 
@@ -83,7 +93,7 @@
 	Package_splits []string
 
 	// list of native libraries that will be provided in or alongside the resulting jar
-	Jni_libs []string `android:"arch_variant"`
+	Jni_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// if true, use JNI libraries that link against platform APIs even if this module sets
 	// sdk_version.
@@ -162,7 +172,7 @@
 	RotationMinSdkVersion *string
 
 	// the package name of this app. The package name in the manifest file is used if one was not given.
-	Package_name *string
+	Package_name proptools.Configurable[string]
 
 	// the logging parent of this app.
 	Logging_parent *string
@@ -311,7 +321,7 @@
 		} else {
 			tag = jniInstallTag
 		}
-		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
+		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs.GetOrDefault(ctx, nil)...)
 	}
 	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
@@ -376,7 +386,8 @@
 	checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx))
 	applicationId := a.appTestHelperAppProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
-		if a.overridableAppProperties.Package_name != nil {
+		packageName := a.overridableAppProperties.Package_name.Get(ctx)
+		if packageName.IsPresent() {
 			ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.")
 		}
 		a.aapt.manifestValues.applicationId = *applicationId
@@ -385,7 +396,10 @@
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly: true,
 	})
-
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		Updatable:     Bool(a.appProperties.Updatable),
+		TestHelperApp: true,
+	})
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -393,6 +407,10 @@
 	a.checkEmbedJnis(ctx)
 	a.generateAndroidBuildActions(ctx)
 	a.generateJavaUsedByApex(ctx)
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		Updatable:     Bool(a.appProperties.Updatable),
+		TestHelperApp: false,
+	})
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
@@ -428,7 +446,7 @@
 func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) {
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	apkInApex := !apexInfo.IsForPlatform()
-	hasJnis := len(a.appProperties.Jni_libs) > 0
+	hasJnis := len(a.appProperties.Jni_libs.GetOrDefault(ctx, nil)) > 0
 
 	if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) {
 		ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true")
@@ -569,10 +587,11 @@
 	}
 
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
-	if overridden || a.overridableAppProperties.Package_name != nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if overridden || packageNameProp.IsPresent() {
 		// The product override variable has a priority over the package_name property.
 		if !overridden {
-			manifestPackageName = *a.overridableAppProperties.Package_name
+			manifestPackageName = packageNameProp.Get()
 		}
 		aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, a.renameResourcesPackage())...)
 		a.overriddenManifestPackageName = manifestPackageName
@@ -586,7 +605,7 @@
 		if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
 			a.aapt.defaultManifestVersion = override
 		} else {
-			a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion
+			a.aapt.defaultManifestVersion = ctx.Config().ReleaseDefaultUpdatableModuleVersion()
 		}
 	}
 
@@ -812,11 +831,12 @@
 		return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist)
 	}
 
-	if a.overridableAppProperties.Package_name == nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if packageNameProp.IsEmpty() {
 		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
 	}
 
-	packageName := *a.overridableAppProperties.Package_name
+	packageName := packageNameProp.Get()
 	fileName := "privapp_allowlist_" + packageName + ".xml"
 	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
 	ctx.Build(pctx, android.BuildParams{
@@ -1135,7 +1155,7 @@
 	return jniLibs, prebuiltJniPackages
 }
 
-func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
+func (a *AndroidApp) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) {
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		isExternal := !a.DepIsInSameApex(ctx, child)
 		if am, ok := child.(android.ApexModule); ok {
@@ -1153,7 +1173,7 @@
 	}
 
 	depsInfo := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		depName := to.Name()
 
 		// Skip dependencies that are only available to APEXes; they are developed with updatability
@@ -1206,10 +1226,6 @@
 	return Bool(a.appProperties.Updatable)
 }
 
-func (a *AndroidApp) SetUpdatable(val bool) {
-	a.appProperties.Updatable = &val
-}
-
 func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string {
 	certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
 	if overridden {
@@ -1405,7 +1421,8 @@
 	}
 	applicationId := a.appTestProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
-		if a.overridableAppProperties.Package_name != nil {
+		packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+		if packageNameProp.IsPresent() {
 			ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.")
 		}
 		a.aapt.manifestValues.applicationId = *applicationId
@@ -1417,7 +1434,8 @@
 	}
 
 	testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
-		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config, configs)
+		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites,
+		a.testProperties.Auto_gen_config, configs, a.testProperties.Test_options.Test_runner_options)
 	a.testConfig = a.FixTestConfig(ctx, testConfig)
 	a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
@@ -1455,10 +1473,11 @@
 		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
 	}
 
-	if a.overridableAppProperties.Package_name != nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if packageNameProp.IsPresent() {
 		fixNeeded = true
 		command.FlagWithInput("--manifest ", a.manifestPath).
-			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
+			FlagWithArg("--package-name ", packageNameProp.Get())
 	}
 
 	if a.appTestProperties.Mainline_package_name != nil {
@@ -1781,16 +1800,15 @@
 			}
 		}
 
-		// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
-		// This will be restricted to optional_uses_libs
-		if sdklib, ok := m.(SdkLibraryDependency); ok {
-			if tag == usesLibOptTag && sdklib.DexJarBuildPath(ctx).PathOrNil() == nil {
-				u.shouldDisableDexpreopt = true
-				return
-			}
-		}
-
 		if lib, ok := m.(UsesLibraryDependency); ok {
+			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
+				// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
+				// This will be restricted to optional_uses_libs
+				if tag == usesLibOptTag && lib.DexJarBuildPath(ctx).PathOrNil() == nil {
+					u.shouldDisableDexpreopt = true
+					return
+				}
+			}
 			libName := dep
 			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
 				libName = *ulib.ProvidesUsesLib()
diff --git a/java/app_import.go b/java/app_import.go
index 045a89a..a54cf2f 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -533,10 +533,6 @@
 	return android.SdkSpecPrivate.ApiLevel
 }
 
-func (a *AndroidAppImport) LintDepSets() LintDepSets {
-	return LintDepSets{}
-}
-
 var _ android.ApexModule = (*AndroidAppImport)(nil)
 
 // Implements android.ApexModule
diff --git a/java/app_test.go b/java/app_test.go
index ec97a55..dd672a0 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -530,9 +530,9 @@
 	`)
 	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesContain(t,
-		"com.android.foo: expected manifest fixer to set override-placeholder-version to android.DefaultUpdatableModuleVersion",
+		"com.android.foo: expected manifest fixer to set override-placeholder-version to RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION",
 		foo.BuildParams.Args["args"],
-		fmt.Sprintf("--override-placeholder-version %s", android.DefaultUpdatableModuleVersion),
+		fmt.Sprintf("--override-placeholder-version %s", testDefaultUpdatableModuleVersion),
 	)
 }
 
@@ -1419,26 +1419,31 @@
 }
 
 func TestAndroidResourceOverlays(t *testing.T) {
+	type moduleAndVariant struct {
+		module  string
+		variant string
+	}
+
 	testCases := []struct {
 		name                       string
 		enforceRROTargets          []string
 		enforceRROExcludedOverlays []string
-		resourceFiles              map[string][]string
-		overlayFiles               map[string][]string
-		rroDirs                    map[string][]string
+		resourceFiles              map[moduleAndVariant][]string
+		overlayFiles               map[moduleAndVariant][]string
+		rroDirs                    map[moduleAndVariant][]string
 	}{
 		{
 			name:                       "no RRO",
 			enforceRROTargets:          nil,
 			enforceRROExcludedOverlays: nil,
-			resourceFiles: map[string][]string{
-				"foo":  nil,
-				"bar":  {"bar/res/res/values/strings.xml"},
-				"lib":  nil,
-				"lib2": {"lib2/res/res/values/strings.xml"},
+			resourceFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}:  nil,
+				{"bar", "android_common"}:  {"bar/res/res/values/strings.xml"},
+				{"lib", "android_common"}:  nil,
+				{"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"},
 			},
-			overlayFiles: map[string][]string{
-				"foo": {
+			overlayFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"out/soong/.intermediates/lib/android_common/package-res.apk",
 					"out/soong/.intermediates/lib3/android_common/package-res.apk",
@@ -1447,57 +1452,65 @@
 					"device/vendor/blah/overlay/foo/res/values/strings.xml",
 					"product/vendor/blah/overlay/foo/res/values/strings.xml",
 				},
-				"bar": {
+				{"bar", "android_common"}: {
 					"device/vendor/blah/static_overlay/bar/res/values/strings.xml",
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
-				"lib": {
+				{"lib", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
 			},
-			rroDirs: map[string][]string{
-				"foo": nil,
-				"bar": nil,
+			rroDirs: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: nil,
+				{"bar", "android_common"}: nil,
 			},
 		},
 		{
 			name:                       "enforce RRO on foo",
 			enforceRROTargets:          []string{"foo"},
 			enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
-			resourceFiles: map[string][]string{
-				"foo":  nil,
-				"bar":  {"bar/res/res/values/strings.xml"},
-				"lib":  nil,
-				"lib2": {"lib2/res/res/values/strings.xml"},
+			resourceFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}:      nil,
+				{"bar", "android_common"}:      {"bar/res/res/values/strings.xml"},
+				{"lib", "android_common"}:      nil,
+				{"lib", "android_common_rro"}:  nil,
+				{"lib2", "android_common"}:     {"lib2/res/res/values/strings.xml"},
+				{"lib2", "android_common_rro"}: {"lib2/res/res/values/strings.xml"},
 			},
-			overlayFiles: map[string][]string{
-				"foo": {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
-					"out/soong/.intermediates/lib/android_common/package-res.apk",
-					"out/soong/.intermediates/lib3/android_common/package-res.apk",
+			overlayFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
+					"out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
+					"out/soong/.intermediates/lib/android_common_rro/package-res.apk",
+					"out/soong/.intermediates/lib3/android_common_rro/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 				},
-				"bar": {
+				{"bar", "android_common"}: {
 					"device/vendor/blah/static_overlay/bar/res/values/strings.xml",
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
-				"lib": {
+				{"lib", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
+					"device/vendor/blah/overlay/lib/res/values/strings.xml",
+				},
+				{"lib", "android_common_rro"}: {
+					"out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
+					"lib/res/res/values/strings.xml",
 				},
 			},
 
-			rroDirs: map[string][]string{
-				"foo": {
+			rroDirs: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"device:device/vendor/blah/overlay/foo/res",
 					"product:product/vendor/blah/overlay/foo/res",
 					"device:device/vendor/blah/overlay/lib/res",
 				},
-				"bar": nil,
-				"lib": {"device:device/vendor/blah/overlay/lib/res"},
+				{"bar", "android_common"}:     nil,
+				{"lib", "android_common"}:     nil,
+				{"lib", "android_common_rro"}: {"device:device/vendor/blah/overlay/lib/res"},
 			},
 		},
 		{
@@ -1508,35 +1521,35 @@
 				"device/vendor/blah/static_overlay/foo",
 				"device/vendor/blah/static_overlay/bar/res",
 			},
-			resourceFiles: map[string][]string{
-				"foo":  nil,
-				"bar":  {"bar/res/res/values/strings.xml"},
-				"lib":  nil,
-				"lib2": {"lib2/res/res/values/strings.xml"},
+			resourceFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}:  nil,
+				{"bar", "android_common"}:  {"bar/res/res/values/strings.xml"},
+				{"lib", "android_common"}:  nil,
+				{"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"},
 			},
-			overlayFiles: map[string][]string{
-				"foo": {
+			overlayFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"out/soong/.intermediates/lib/android_common/package-res.apk",
 					"out/soong/.intermediates/lib3/android_common/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 				},
-				"bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
-				"lib": {
+				{"bar", "android_common"}: {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
+				{"lib", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
-			rroDirs: map[string][]string{
-				"foo": {
+			rroDirs: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"device:device/vendor/blah/overlay/foo/res",
 					"product:product/vendor/blah/overlay/foo/res",
 					// Lib dep comes after the direct deps
 					"device:device/vendor/blah/overlay/lib/res",
 				},
-				"bar": {"device:device/vendor/blah/overlay/bar/res"},
-				"lib": {"device:device/vendor/blah/overlay/lib/res"},
+				{"bar", "android_common"}: {"device:device/vendor/blah/overlay/bar/res"},
+				{"lib", "android_common"}: {"device:device/vendor/blah/overlay/lib/res"},
 			},
 		},
 	}
@@ -1621,19 +1634,19 @@
 				for _, o := range list {
 					res := module.MaybeOutput(o)
 					if res.Rule != nil {
-						// If the overlay is compiled as part of this module (i.e. a .arsc.flat file),
+						// If the overlay is compiled as part of this moduleAndVariant (i.e. a .arsc.flat file),
 						// verify the inputs to the .arsc.flat rule.
 						files = append(files, res.Inputs.Strings()...)
 					} else {
-						// Otherwise, verify the full path to the output of the other module
+						// Otherwise, verify the full path to the output of the other moduleAndVariant
 						files = append(files, o)
 					}
 				}
 				return files
 			}
 
-			getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
-				module := result.ModuleForTests(moduleName, "android_common")
+			getResources := func(moduleName, variantName string) (resourceFiles, overlayFiles, rroDirs []string) {
+				module := result.ModuleForTests(moduleName, variantName)
 				resourceList := module.MaybeOutput("aapt2/res.list")
 				if resourceList.Rule != nil {
 					resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
@@ -1658,21 +1671,33 @@
 				return resourceFiles, overlayFiles, rroDirs
 			}
 
-			modules := []string{"foo", "bar", "lib", "lib2"}
-			for _, module := range modules {
-				resourceFiles, overlayFiles, rroDirs := getResources(module)
+			modules := []moduleAndVariant{
+				{"foo", "android_common"},
+				{"foo", "android_common_rro"},
+				{"bar", "android_common"},
+				{"bar", "android_common_rro"},
+				{"lib", "android_common"},
+				{"lib", "android_common_rro"},
+				{"lib2", "android_common"},
+				{"lib2", "android_common_rro"},
+			}
+			for _, moduleAndVariant := range modules {
+				if _, exists := testCase.resourceFiles[moduleAndVariant]; !exists {
+					continue
+				}
+				resourceFiles, overlayFiles, rroDirs := getResources(moduleAndVariant.module, moduleAndVariant.variant)
 
-				if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[module]) {
+				if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[moduleAndVariant]) {
 					t.Errorf("expected %s resource files:\n  %#v\n got:\n  %#v",
-						module, testCase.resourceFiles[module], resourceFiles)
+						moduleAndVariant, testCase.resourceFiles[moduleAndVariant], resourceFiles)
 				}
-				if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[module]) {
+				if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[moduleAndVariant]) {
 					t.Errorf("expected %s overlay files:\n  %#v\n got:\n  %#v",
-						module, testCase.overlayFiles[module], overlayFiles)
+						moduleAndVariant, testCase.overlayFiles[moduleAndVariant], overlayFiles)
 				}
-				if !reflect.DeepEqual(rroDirs, testCase.rroDirs[module]) {
+				if !reflect.DeepEqual(rroDirs, testCase.rroDirs[moduleAndVariant]) {
 					t.Errorf("expected %s rroDirs:  %#v\n got:\n  %#v",
-						module, testCase.rroDirs[module], rroDirs)
+						moduleAndVariant, testCase.rroDirs[moduleAndVariant], rroDirs)
 				}
 			}
 		})
@@ -3241,7 +3266,7 @@
 		java_library {
 			name: "static-runtime-helper",
 			srcs: ["a.java"],
-			libs: ["runtime-library"],
+			libs: ["runtime-library.impl"],
 			sdk_version: "current",
 		}
 
@@ -3305,7 +3330,7 @@
 			name: "app",
 			srcs: ["a.java"],
 			libs: [
-				"qux",
+				"qux.impl",
 				"quuz.stubs"
 			],
 			static_libs: [
diff --git a/java/base.go b/java/base.go
index ef299b2..7a95735 100644
--- a/java/base.go
+++ b/java/base.go
@@ -83,9 +83,6 @@
 	// list of java libraries that will be compiled into the resulting jar
 	Static_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
-	// list of java libraries that should not be used to build this module
-	Exclude_static_libs []string `android:"arch_variant"`
-
 	// manifest file to be included in resulting jar
 	Manifest *string `android:"path"`
 
@@ -221,11 +218,15 @@
 	// the stubs via static libs.
 	Is_stubs_module *bool
 
-	// If true, enable the "Ravenizer" tool on the output jar.
-	// "Ravenizer" is a tool for Ravenwood tests, but it can also be enabled on other kinds
-	// of java targets.
 	Ravenizer struct {
+		// If true, enable the "Ravenizer" tool on the output jar.
+		// "Ravenizer" is a tool for Ravenwood tests, but it can also be enabled on other kinds
+		// of java targets.
 		Enabled *bool
+
+		// If true, the "Ravenizer" tool will remove all Mockito and DexMaker
+		// classes from the output jar.
+		Strip_mockito *bool
 	}
 
 	// Contributing api surface of the stub module. Is not visible to bp modules, and should
@@ -535,7 +536,8 @@
 	linter
 
 	// list of the xref extraction files
-	kytheFiles android.Paths
+	kytheFiles       android.Paths
+	kytheKotlinFiles android.Paths
 
 	hideApexVariantFromMake bool
 
@@ -707,9 +709,6 @@
 		ctx.SetOutputFiles(android.Paths{m.dexer.proguardDictionary.Path()}, ".proguard_map")
 	}
 	ctx.SetOutputFiles(m.properties.Generated_srcjars, ".generated_srcjars")
-	if m.linter.outputs.xml != nil {
-		ctx.SetOutputFiles(android.Paths{m.linter.outputs.xml}, ".lint")
-	}
 }
 
 func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
@@ -829,7 +828,7 @@
 }
 
 func (j *Module) staticLibs(ctx android.BaseModuleContext) []string {
-	return android.RemoveListFromList(j.properties.Static_libs.GetOrDefault(ctx, nil), j.properties.Exclude_static_libs)
+	return j.properties.Static_libs.GetOrDefault(ctx, nil)
 }
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
@@ -853,33 +852,6 @@
 	// Add dependency on libraries that provide additional hidden api annotations.
 	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
 
-	if ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
-		// Require java_sdk_library at inter-partition java dependency to ensure stable
-		// interface between partitions. If inter-partition java_library dependency is detected,
-		// raise build error because java_library doesn't have a stable interface.
-		//
-		// Inputs:
-		//    PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
-		//      if true, enable enforcement
-		//    PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
-		//      exception list of java_library names to allow inter-partition dependency
-		for idx := range j.properties.Libs {
-			if libDeps[idx] == nil {
-				continue
-			}
-
-			if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
-				// java_sdk_library is always allowed at inter-partition dependency.
-				// So, skip check.
-				if _, ok := javaDep.(*SdkLibrary); ok {
-					continue
-				}
-
-				j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
-			}
-		}
-	}
-
 	// For library dependencies that are component libraries (like stubs), add the implementation
 	// as a dependency (dexpreopt needs to be against the implementation library, not stubs).
 	for _, dep := range libDeps {
@@ -1166,8 +1138,9 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
-	if re := proptools.Bool(j.properties.Ravenizer.Enabled); re {
-		j.ravenizer.enabled = re
+	// Only override the original value if explicitly set
+	if j.properties.Ravenizer.Enabled != nil {
+		j.ravenizer.enabled = *j.properties.Ravenizer.Enabled
 	}
 
 	deps := j.collectDeps(ctx)
@@ -1370,7 +1343,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
 		}
@@ -1655,16 +1628,23 @@
 
 	if j.ravenizer.enabled {
 		ravenizerInput := outputFile
-		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", jarName)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        ravenizer,
-			Description: "ravenizer",
-			Input:       ravenizerInput,
-			Output:      ravenizerOutput,
-		})
+		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", "", jarName)
+		ravenizerArgs := ""
+		if proptools.Bool(j.properties.Ravenizer.Strip_mockito) {
+			ravenizerArgs = "--strip-mockito"
+		}
+		TransformRavenizer(ctx, ravenizerOutput, ravenizerInput, ravenizerArgs)
 		outputFile = ravenizerOutput
 		localImplementationJars = android.Paths{ravenizerOutput}
 		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+		if combinedResourceJar != nil {
+			ravenizerInput = combinedResourceJar
+			ravenizerOutput = android.PathForModuleOut(ctx, "ravenizer", "resources", jarName)
+			TransformRavenizer(ctx, ravenizerOutput, ravenizerInput, ravenizerArgs)
+			combinedResourceJar = ravenizerOutput
+			localResourceJars = android.Paths{ravenizerOutput}
+			completeStaticLibsResourceJars = android.NewDepSet(android.PREORDER, localResourceJars, nil)
+		}
 	}
 
 	if j.shouldApiMapper() {
@@ -1735,8 +1715,12 @@
 		return
 	}
 
+	completeStaticLibsImplementationJarsToCombine := completeStaticLibsImplementationJars
+
 	if j.shouldInstrument(ctx) {
-		outputFile = j.instrument(ctx, flags, outputFile, jarName, specs)
+		instrumentedOutputFile := j.instrument(ctx, flags, outputFile, jarName, specs)
+		completeStaticLibsImplementationJarsToCombine = android.NewDepSet(android.PREORDER, android.Paths{instrumentedOutputFile}, nil)
+		outputFile = instrumentedOutputFile
 	}
 
 	// merge implementation jar with resources if necessary
@@ -1744,7 +1728,7 @@
 	if ctx.Config().UseTransitiveJarsInClasspath() {
 		resourceJars := completeStaticLibsResourceJars.ToList()
 		if len(resourceJars) > 0 {
-			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJars.ToList()...)
+			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJarsToCombine.ToList()...)
 			implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...)
 		}
 	} else {
@@ -1789,14 +1773,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
@@ -2413,18 +2397,12 @@
 			return
 		}
 
-		if dep, ok := module.(SdkLibraryDependency); ok {
+		if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 			switch tag {
-			case sdkLibTag, libTag:
-				depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
-				deps.classpath = append(deps.classpath, depHeaderJars...)
-				deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...)
-
-				// TODO: SDK libraries should export a provider with TransitiveClasspathHeaderJars
-				depHeaderJarsSet := android.NewDepSet(android.PREORDER, depHeaderJars, nil)
-				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, depHeaderJarsSet)
-			case staticLibTag:
-				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
+			case sdkLibTag, libTag, staticLibTag:
+				generatingLibsString := android.PrettyConcat(
+					getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or")
+				ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString)
 			}
 		} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			if sdkLinkType != javaPlatform {
@@ -2730,7 +2708,7 @@
 	module := ctx.Module()
 	moduleName := module.Name()
 
-	ctx.VisitDirectDepsIgnoreBlueprint(func(m android.Module) {
+	ctx.VisitDirectDeps(func(m android.Module) {
 		tag := ctx.OtherModuleDependencyTag(m)
 		// This logic mirrors that in (*Module).collectDeps above.  There are several places
 		// where we explicitly return RenameUseExclude, even though it is the default, to
@@ -2753,7 +2731,7 @@
 			if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag {
 				return RenameUseExclude, "tags"
 			}
-			if _, ok := m.(SdkLibraryDependency); ok {
+			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
 				switch tag {
 				case sdkLibTag, libTag:
 					return RenameUseExclude, "sdklibdep" // matches collectDeps()
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 6223ded..3c3bd55 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -21,7 +21,7 @@
 // isActiveModule returns true if the given module should be considered for boot
 // jars, i.e. if it's enabled and the preferred one in case of source and
 // prebuilt alternatives.
-func isActiveModule(ctx android.ConfigAndErrorContext, module android.Module) bool {
+func isActiveModule(ctx android.ConfigurableEvaluatorContext, module android.Module) bool {
 	if !module.Enabled(ctx) {
 		return false
 	}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index fe4cc76..4fcd40b 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -414,6 +414,12 @@
 		// Cross-cutting metadata dependencies are metadata.
 		return false
 	}
+	// Dependency to the bootclasspath fragment of another apex
+	// e.g. concsrypt-bootclasspath-fragment --> art-bootclasspath-fragment
+	if tag == bootclasspathFragmentDepTag {
+		return false
+
+	}
 	panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
 }
 
@@ -1099,22 +1105,10 @@
 	return &output
 }
 
+// DEPRECATED: this information is now generated in the context of the top level prebuilt apex.
 // produceBootImageProfile extracts the boot image profile from the APEX if available.
 func (module *PrebuiltBootclasspathFragmentModule) produceBootImageProfile(ctx android.ModuleContext) android.WritablePath {
-	// This module does not provide a boot image profile.
-	if module.getProfileProviderApex(ctx) == "" {
-		return nil
-	}
-
-	di, err := android.FindDeapexerProviderForModule(ctx)
-	if err != nil {
-		// An error was found, possibly due to multiple apexes in the tree that export this library
-		// Defer the error till a client tries to call getProfilePath
-		module.profilePathErr = err
-		return nil // An error has been reported by FindDeapexerProviderForModule.
-	}
-
-	return di.PrebuiltExportPath(ProfileInstallPathInApex)
+	return android.PathForModuleInstall(ctx, "intentionally_no_longer_supported")
 }
 
 func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
diff --git a/java/builder.go b/java/builder.go
index 81b0feb..e5d5109 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -260,10 +260,10 @@
 
 	ravenizer = pctx.AndroidStaticRule("ravenizer",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out",
+			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out $ravenizerArgs",
 			CommandDeps: []string{"${ravenizer}"},
 		},
-	)
+		"ravenizerArgs")
 
 	apimapper = pctx.AndroidStaticRule("apimapper",
 		blueprint.RuleParams{
@@ -703,6 +703,7 @@
 	// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
 	// for downstream tools like desugar.
 	jarArgs = append(jarArgs, "-stripFile module-info.class")
+	jarArgs = append(jarArgs, "-stripFile META-INF/versions/*/module-info.class")
 
 	if stripDirEntries {
 		jarArgs = append(jarArgs, "-D")
@@ -782,12 +783,15 @@
 }
 
 func TransformRavenizer(ctx android.ModuleContext, outputFile android.WritablePath,
-	inputFile android.Path) {
+	inputFile android.Path, ravenizerArgs string) {
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        ravenizer,
 		Description: "ravenizer",
 		Output:      outputFile,
 		Input:       inputFile,
+		Args: map[string]string{
+			"ravenizerArgs": ravenizerArgs,
+		},
 	})
 }
 
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..87703d8 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -97,11 +97,19 @@
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 	}, dexerJavaVmFlagsList...), " "))
-	pctx.StaticVariable("R8Flags", strings.Join(append([]string{
-		"-JXmx4096M",
-		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
-		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
-	}, dexerJavaVmFlagsList...), " "))
+
+	pctx.VariableFunc("R8Flags", func(ctx android.PackageVarContext) string {
+		r8flags := append([]string{
+			"-JXmx4096M",
+			"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
+			"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
+		}, dexerJavaVmFlagsList...)
+		if r8DumpDir := ctx.Config().Getenv("R8_DUMP_DIRECTORY"); r8DumpDir != "" {
+			r8flags = append(r8flags, "-JDcom.android.tools.r8.dumpinputtodirectory="+r8DumpDir)
+		}
+		return strings.Join(r8flags, " ")
+
+	})
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
@@ -145,6 +153,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..a3f699b 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 {
@@ -133,7 +133,7 @@
 			`$d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
-			`rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`,
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.SoongZipCmd}",
@@ -172,7 +172,7 @@
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
-			`rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`,
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
 		Depfile: "${out}.d",
 		Deps:    blueprint.DepsGCC,
 		CommandDeps: []string{
@@ -220,14 +220,21 @@
 		deps = append(deps, f)
 	}
 
-	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
+	var requestReleaseMode bool
+	requestReleaseMode, flags = android.RemoveFromList("--release", flags)
+
+	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" || ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
 		flags = append(flags, "--debug")
+		requestReleaseMode = false
 	}
 
-	if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
-		flags = append(flags,
-			"--debug",
-			"--verbose")
+	// Don't strip out debug information for eng builds, unless the target
+	// explicitly provided the `--release` build flag. This allows certain
+	// test targets to remain optimized as part of eng test_suites builds.
+	if requestReleaseMode {
+		flags = append(flags, "--release")
+	} else if ctx.Config().Eng() {
+		flags = append(flags, "--debug")
 	}
 
 	// Supplying the platform build flag disables various features like API modeling and desugaring.
@@ -245,6 +252,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.
@@ -374,11 +391,6 @@
 	// TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
 	// dictionary of the app and move the app from libraryjars to injars.
 
-	// Don't strip out debug information for eng builds.
-	if ctx.Config().Eng() {
-		r8Flags = append(r8Flags, "--debug")
-	}
-
 	// TODO(b/180878971): missing classes should be added to the relevant builds.
 	// TODO(b/229727645): do not use true as default for Android platform builds.
 	if proptools.BoolDefault(opt.Ignore_warnings, true) {
@@ -390,7 +402,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/dex_test.go b/java/dex_test.go
index 4862d06..8bc28e6 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -713,3 +713,97 @@
 	}
 }`)
 }
+
+func TestDebugReleaseFlags(t *testing.T) {
+	bp := `
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			dxflags: ["%s"]
+		}
+	`
+
+	testcases := []struct {
+		name          string
+		envVar        string
+		isEng         bool
+		dxFlags       string
+		expectedFlags string
+	}{
+		{
+			name:          "app_no_optimize_dx",
+			envVar:        "NO_OPTIMIZE_DX",
+			expectedFlags: "--debug",
+		},
+		{
+			name:    "app_release_no_optimize_dx",
+			envVar:  "NO_OPTIMIZE_DX",
+			dxFlags: "--release",
+			// Global env vars override explicit dxflags.
+			expectedFlags: "--debug",
+		},
+		{
+			name:          "app_generate_dex_debug",
+			envVar:        "GENERATE_DEX_DEBUG",
+			expectedFlags: "--debug",
+		},
+		{
+			name:    "app_release_generate_dex_debug",
+			envVar:  "GENERATE_DEX_DEBUG",
+			dxFlags: "--release",
+			// Global env vars override explicit dxflags.
+			expectedFlags: "--debug",
+		},
+		{
+			name:          "app_eng",
+			isEng:         true,
+			expectedFlags: "--debug",
+		},
+		{
+			name:    "app_release_eng",
+			isEng:   true,
+			dxFlags: "--release",
+			// Eng mode does *not* override explicit dxflags.
+			expectedFlags: "--release",
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixturePreparer := PrepareForTestWithJavaDefaultModules
+			fixturePreparer = android.GroupFixturePreparers(
+				fixturePreparer,
+				android.FixtureModifyProductVariables(
+					func(variables android.FixtureProductVariables) {
+						variables.Eng = proptools.BoolPtr(tc.isEng)
+					},
+				),
+			)
+			if tc.envVar != "" {
+				fixturePreparer = android.GroupFixturePreparers(
+					fixturePreparer,
+					android.FixtureMergeEnv(map[string]string{
+						tc.envVar: "true",
+					}),
+				)
+			}
+			result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, tc.dxFlags))
+
+			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+			android.AssertStringDoesContain(t, "expected flag in R8 flags",
+				appR8.Args["r8Flags"], tc.expectedFlags)
+
+			var unexpectedFlags string
+			if tc.expectedFlags == "--debug" {
+				unexpectedFlags = "--release"
+			} else if tc.expectedFlags == "--release" {
+				unexpectedFlags = "--debug"
+			}
+			if unexpectedFlags != "" {
+				android.AssertStringDoesNotContain(t, "unexpected flag in R8 flags",
+					appR8.Args["r8Flags"], unexpectedFlags)
+			}
+		})
+	}
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 4734357..637da36 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,12 +488,18 @@
 		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,
 	}
 
+	if ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
+		dexpreoptConfig.ApexPartition = android.PathForModuleInstall(ctx).Partition()
+	} else {
+		dexpreoptConfig.ApexPartition = "system"
+	}
+
 	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
 	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
 	ctx.CheckbuildFile(d.configPath)
@@ -657,16 +665,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/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index a2e4734..5c69ff1 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -463,6 +463,7 @@
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
 	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterModuleType("art_boot_images", artBootImagesFactory)
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator).Parallel()
 	})
@@ -601,6 +602,7 @@
 	d.defaultBootImage = defaultBootImageConfig(ctx)
 	d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
 	var profileInstalls android.RuleBuilderInstalls
+	var artBootImageHostInstalls android.RuleBuilderInstalls
 	for _, name := range getImageNames() {
 		config := imageConfigs[name]
 		if config != d.defaultBootImage {
@@ -616,6 +618,18 @@
 			d.bootFrameworkProfile = bootProfile
 			profileInstalls = append(profileInstalls, installs...)
 		}
+		// Gather the install files of the host variant of the ART boot image.
+		// These installed files will be used in ART tests.
+		if config.name == "art" {
+			for _, variant := range config.variants {
+				if variant.target.Os != ctx.Config().BuildOS {
+					// not a host variant
+					continue
+				}
+				artBootImageHostInstalls = append(artBootImageHostInstalls, variant.installs...)
+				artBootImageHostInstalls = append(artBootImageHostInstalls, variant.vdexInstalls...)
+			}
+		}
 	}
 	if len(profileInstalls) > 0 {
 		android.SetProvider(ctx, profileInstallInfoProvider, profileInstallInfo{
@@ -626,6 +640,15 @@
 			installFile(ctx, install)
 		}
 	}
+	// Set a provider containing the install files of the host variant of the ART boot image.
+	// The actual install rules will be created by `art_boot_images`
+	android.SetProvider(
+		ctx,
+		artBootImageHostInfoProvider,
+		artBootImageHostInfo{
+			installs: artBootImageHostInstalls,
+		},
+	)
 }
 
 // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
@@ -968,6 +991,14 @@
 	}
 }
 
+var artBootImageHostInfoProvider = blueprint.NewProvider[artBootImageHostInfo]()
+
+// artBootImageHostInfo contains the install locations of the host variant of ART boot image
+// this contains both the primary and secondary arch locations
+type artBootImageHostInfo struct {
+	installs android.RuleBuilderInstalls
+}
+
 // Generate boot image build rules for a specific target.
 func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
 
@@ -1186,8 +1217,13 @@
 		return nil
 	}
 
-	defaultProfile := "frameworks/base/config/boot-image-profile.txt"
-	extraProfile := "frameworks/base/config/boot-image-profile-extra.txt"
+	defaultProfile := "frameworks/base/boot/boot-image-profile.txt"
+	// If ART is prebuilt, primarily in next release configs, this will still use
+	// the profile from source which represent the latest code, so it may not
+	// correspond to the BCP jars in the prebuilt APEX, but this is the profile we
+	// have access to.
+	artProfile := "art/build/boot/boot-image-profile.txt"
+	extraProfile := "frameworks/base/boot/boot-image-profile-extra.txt"
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -1202,6 +1238,9 @@
 		// Return nil and continue without profile.
 		return nil
 	}
+	if path := android.ExistentPathForSource(ctx, artProfile); path.Valid() {
+		profiles = append(profiles, path.Path())
+	}
 	if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() {
 		profiles = append(profiles, path.Path())
 	}
@@ -1259,7 +1298,7 @@
 		return nil, nil
 	}
 
-	defaultProfile := "frameworks/base/config/boot-profile.txt"
+	defaultProfile := "frameworks/base/boot/boot-profile.txt"
 	bootFrameworkProfile := android.PathForSource(ctx, defaultProfile)
 
 	profile := image.dir.Join(ctx, "boot.bprof")
@@ -1344,18 +1383,7 @@
 	}
 
 	image := d.defaultBootImage
-	if image != nil {
-		if profileInstallInfo, ok := android.OtherModuleProvider(ctx, d, profileInstallInfoProvider); ok {
-			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", profileInstallInfo.profileInstalls.String())
-			if profileInstallInfo.profileLicenseMetadataFile.Valid() {
-				ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", profileInstallInfo.profileLicenseMetadataFile.String())
-			}
-		}
-
-		if SkipDexpreoptBootJars(ctx) {
-			return
-		}
-
+	if image != nil && !SkipDexpreoptBootJars(ctx) {
 		global := dexpreopt.GetGlobalConfig(ctx)
 		dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
@@ -1396,3 +1424,70 @@
 		OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile),
 	}}
 }
+
+// artBootImages is a thin wrapper around `dex_bootjars`.
+// it creates the installation rules for the host variant of the ART boot image.
+type artBootImages struct {
+	android.ModuleBase
+
+	// A non-empty file that will be written as `LOCAL_SOONG_INSTALLED_MODULE` in out/soong/Android-*.mk
+	outputFile android.OptionalPath
+}
+
+func artBootImagesFactory() android.Module {
+	m := &artBootImages{}
+	android.InitAndroidMultiTargetsArchModule(m, android.HostSupported, android.MultilibCommon)
+	return m
+}
+
+func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image.
+	ctx.AddDependency(ctx.Module(), dexpreoptBootJarDepTag, "dex_bootjars")
+}
+
+func (d *artBootImages) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(m android.Module) {
+		hostInstallsInfo, ok := android.OtherModuleProvider(ctx, m, artBootImageHostInfoProvider)
+		if !ok {
+			ctx.ModuleErrorf("Could not find information about the host variant of ART boot image")
+		}
+		installs := d.installFile(ctx, hostInstallsInfo.installs)
+		if len(installs) > 0 {
+			d.outputFile = android.OptionalPathForPath(installs[0])
+			// Create a phony target that can ART run-tests can depend on.
+			ctx.Phony(d.Name(), installs...)
+		} else {
+			// this might be true e.g. when building with `WITH_DEXPREOPT=false`
+			// create an empty file so that the `art_boot_images` is known to the packaging system.
+			d.outputFile = android.OptionalPathForPath(android.PathForModuleOut(ctx, "undefined_art_boot_images"))
+		}
+	})
+}
+
+// Creates an installation rule for host variant of ART boot image files.
+// Returns the list of install locations (out/host/linux-x86/...)
+func (d *artBootImages) installFile(ctx android.ModuleContext, ruleBuilderInstalls android.RuleBuilderInstalls) android.Paths {
+	var ret android.Paths
+	for _, ruleBuilderInstall := range ruleBuilderInstalls {
+		installDir := android.PathForModuleInstall(
+			ctx,
+			strings.TrimPrefix(filepath.Dir(ruleBuilderInstall.To), "/"),
+		)
+		filename := filepath.Base(ruleBuilderInstall.To)
+		ctx.InstallFile(
+			installDir,
+			filename,
+			ruleBuilderInstall.From,
+		)
+		ret = append(ret, installDir.Join(ctx, filename))
+	}
+	return ret
+}
+
+// Set `OutputFile` expclitly so that this module does not get elided when generating out/soong/Android-*.mk
+func (d *artBootImages) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: d.outputFile,
+	}}
+}
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 33be603..c971565 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 
@@ -43,16 +45,12 @@
 type dexpreoptSystemserverCheck struct {
 	android.SingletonModuleBase
 
-	// Mapping from the module name to the install paths to the compilation artifacts.
-	artifactsByModuleName map[string][]string
-
 	// The install paths to the compilation artifacts.
 	artifacts []string
 }
 
 func dexpreoptSystemserverCheckFactory() android.SingletonModule {
 	m := &dexpreoptSystemserverCheck{}
-	m.artifactsByModuleName = make(map[string][]string)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
@@ -62,7 +60,25 @@
 		ctx, "", strings.TrimPrefix(location, "/"))
 }
 
-func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+type systemServerDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// systemServerJarDepTag willl be used for validation. Skip visiblility.
+func (b systemServerDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+var (
+	// dep tag for platform and apex system server jars
+	systemServerJarDepTag = systemServerDependencyTag{}
+)
+
+var _ android.ExcludeFromVisibilityEnforcementTag = systemServerJarDepTag
+
+// Add a depenendency on the system server jars. The dexpreopt files of those will be emitted to make.
+// The kati packaging system will verify that those files appear in installed files.
+// Adding the dependency allows the singleton module to determine whether an apex system server jar is system_ext specific.
+func (m *dexpreoptSystemserverCheck) DepsMutator(ctx android.BottomUpMutatorContext) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 	targets := ctx.Config().Targets[android.Android]
 
@@ -72,23 +88,27 @@
 		return
 	}
 
-	systemServerJars := global.AllSystemServerJars(ctx)
-	for _, jar := range systemServerJars.CopyOfJars() {
-		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar)
-		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType)
+	ctx.AddDependency(ctx.Module(), systemServerJarDepTag, global.AllSystemServerJars(ctx).CopyOfJars()...)
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	targets := ctx.Config().Targets[android.Android]
+
+	ctx.VisitDirectDepsWithTag(systemServerJarDepTag, func(systemServerJar android.Module) {
+		partition := "system"
+		if systemServerJar.InstallInSystemExt() && ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
+			partition = ctx.DeviceConfig().SystemExtPath() // system_ext
+		}
+		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, systemServerJar.Name())
+		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType, partition)
 		odexPath := getInstallPath(ctx, odexLocation)
 		vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex"))
-		m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()}
-	}
+		m.artifacts = append(m.artifacts, odexPath.String(), vdexPath.String())
+	})
 }
 
 func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	// Only keep modules defined in Soong.
-	ctx.VisitAllModules(func(module android.Module) {
-		if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok {
-			m.artifacts = append(m.artifacts, artifacts...)
-		}
-	})
 }
 
 func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) {
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 37c54b9..33c682b 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -1335,8 +1335,6 @@
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art
 DEXPREOPT_IMAGE_NAMES=art boot mainline
-DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof:/system/etc/boot-image.prof out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof
-DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 2929bb8..2980d91 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -373,8 +373,10 @@
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
 		case libTag, sdkLibTag:
-			if dep, ok := module.(SdkLibraryDependency); ok {
-				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+			if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
+				generatingLibsString := android.PrettyConcat(
+					getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or")
+				ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString)
 			} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
diff --git a/java/fuzz.go b/java/fuzz.go
index d37c558..e5f1f04 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -85,10 +85,11 @@
 
 func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if j.Os().Class.String() == deviceString {
-		j.testProperties.Jni_libs = append(j.testProperties.Jni_libs, artDeps...)
+		j.testProperties.Jni_libs.AppendSimpleValue(artDeps)
 	}
 
-	if len(j.testProperties.Jni_libs) > 0 {
+	jniLibs := j.testProperties.Jni_libs.GetOrDefault(ctx, nil)
+	if len(jniLibs) > 0 {
 		if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil {
 			config := &fuzz.FuzzConfig{}
 			j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config
@@ -98,7 +99,7 @@
 		j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true)
 		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
+			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, jniLibs...)
 		}
 	}
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 4144de8..3650058 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -298,13 +298,12 @@
 // available, or reports an error.
 func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
 	var dexJar OptionalDexJarPath
-	if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
+	if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 		if ctx.Config().ReleaseHiddenApiExportableStubs() {
-			dexJar = sdkLibrary.SdkApiExportableStubDexJar(ctx, kind)
+			dexJar = sdkLibrary.ExportableStubDexJarPaths[kind]
 		} else {
-			dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
+			dexJar = sdkLibrary.EverythingStubDexJarPaths[kind]
 		}
-
 	} else if j, ok := module.(UsesLibraryDependency); ok {
 		dexJar = j.DexJarBuildPath(ctx)
 	} else {
@@ -853,15 +852,15 @@
 			i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
 		}
 
-		if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
-			removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind)
+		if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
+			removedTxtFile := sdkLibrary.RemovedTxtFiles[sdkKind]
 			i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...)
 		}
 	}
 
 	// If the contents includes any java_sdk_library modules then add them to the stubs.
 	for _, module := range contents {
-		if _, ok := module.(SdkLibraryDependency); ok {
+		if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 			// Add information for every possible API scope needed by hidden API.
 			for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
 				addFromModule(ctx, module, apiScope)
diff --git a/java/java.go b/java/java.go
index 95f4fd8..018850f 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
 }
@@ -1296,7 +1301,7 @@
 	Test_options TestOptions
 
 	// Names of modules containing JNI libraries that should be installed alongside the test.
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 
 	// Install the test into a folder named for the module in all test suites.
 	Per_testcase_directory *bool
@@ -1480,10 +1485,11 @@
 		}
 	}
 
-	if len(j.testProperties.Jni_libs) > 0 {
+	jniLibs := j.testProperties.Jni_libs.GetOrDefault(ctx, nil)
+	if len(jniLibs) > 0 {
 		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
+			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, jniLibs...)
 		}
 	}
 
@@ -1789,8 +1795,7 @@
 	// Name of the class containing main to be inserted into the manifest as Main-Class.
 	Main_class *string
 
-	// Names of modules containing JNI libraries that should be installed alongside the host
-	// variant of the binary.
+	// Names of modules containing JNI libraries that should be installed alongside the binary.
 	Jni_libs []string `android:"arch_variant"`
 }
 
@@ -1803,6 +1808,8 @@
 
 	wrapperFile android.Path
 	binaryFile  android.InstallPath
+
+	androidMkNamesOfJniLibs []string
 }
 
 func (j *Binary) HostToolPath() android.OptionalPath {
@@ -1874,6 +1881,21 @@
 			ctx.ModuleName()+ext, j.wrapperFile)
 
 		setOutputFiles(ctx, j.Library.Module)
+
+		// Set the jniLibs of this binary.
+		// These will be added to `LOCAL_REQUIRED_MODULES`, and the kati packaging system will
+		// install these alongside the java binary.
+		ctx.VisitDirectDepsWithTag(jniInstallTag, func(jni android.Module) {
+			// Use the BaseModuleName of the dependency (without any prebuilt_ prefix)
+			bmn, _ := jni.(interface{ BaseModuleName() string })
+			j.androidMkNamesOfJniLibs = append(j.androidMkNamesOfJniLibs, bmn.BaseModuleName()+":"+jni.Target().Arch.ArchType.Bitness())
+		})
+		// Check that native libraries are not listed in `required`. Prompt users to use `jni_libs` instead.
+		ctx.VisitDirectDepsWithTag(android.RequiredDepTag, func(dep android.Module) {
+			if _, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider); hasSharedLibraryInfo {
+				ctx.ModuleErrorf("cc_library %s is no longer supported in `required` of java_binary modules. Please use jni_libs instead.", dep.Name())
+			}
+		})
 	}
 }
 
@@ -1882,11 +1904,9 @@
 		j.deps(ctx)
 	}
 	// These dependencies ensure the installation rules will install the jar file when the
-	// wrapper is installed, and the jni libraries on host when the wrapper is installed.
-	if ctx.Arch().ArchType != android.Common && ctx.Os().Class == android.Host {
-		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
-	}
+	// wrapper is installed, and the jni libraries when the wrapper is installed.
 	if ctx.Arch().ArchType != android.Common {
+		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
 		ctx.AddVariationDependencies(
 			[]blueprint.Variation{{Mutator: "arch", Variation: android.CommonArch.String()}},
 			binaryInstallTag, ctx.ModuleName())
@@ -2607,17 +2627,6 @@
 	return nil
 }
 
-func (j *Import) LintDepSets() LintDepSets {
-	return LintDepSets{}
-}
-
-func (j *Import) getStrictUpdatabilityLinting() bool {
-	return false
-}
-
-func (j *Import) setStrictUpdatabilityLinting(bool) {
-}
-
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
 	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs.GetOrDefault(ctx, nil)...)
@@ -2695,13 +2704,13 @@
 					transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 				}
 			}
-		} else if dep, ok := module.(SdkLibraryDependency); ok {
+		} else if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 			switch tag {
 			case libTag, sdkLibTag:
-				depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
-				flags.classpath = append(flags.classpath, depHeaderJars...)
-				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars,
-					android.NewDepSet(android.PREORDER, depHeaderJars, nil))
+				sdkInfo, _ := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider)
+				generatingLibsString := android.PrettyConcat(
+					getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or")
+				ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString)
 			}
 		}
 
@@ -2813,41 +2822,14 @@
 	}
 
 	if ctx.Device() {
-		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
-		// obtained from the associated deapexer module.
+		// Shared libraries deapexed from prebuilt apexes are no longer supported.
+		// Set the dexJarBuildPath to a fake path.
+		// This allows soong analysis pass, but will be an error during ninja execution if there are
+		// any rdeps.
 		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 		if ai.ForPrebuiltApex {
-			// Get the path of the dex implementation jar from the `deapexer` module.
-			di, err := android.FindDeapexerProviderForModule(ctx)
-			if err != nil {
-				// An error was found, possibly due to multiple apexes in the tree that export this library
-				// Defer the error till a client tries to call DexJarBuildPath
-				j.dexJarFileErr = err
-				j.initHiddenAPIError(err)
-				return
-			}
-			dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(j.BaseModuleName())
-			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
-				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
-				j.dexJarFile = dexJarFile
-				installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, ApexRootRelativePathToJavaLib(j.BaseModuleName()))
-				j.dexJarInstallFile = installPath
-
-				j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath)
-				setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
-				j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
-
-				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
-					j.dexpreopter.inputProfilePathOnHost = profilePath
-				}
-
-				// Initialize the hiddenapi structure.
-				j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex)
-			} else {
-				// This should never happen as a variant for a prebuilt_apex is only created if the
-				// prebuilt_apex has been configured to export the java library dex file.
-				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName())
-			}
+			j.dexJarFile = makeDexJarPathFromPath(android.PathForModuleInstall(ctx, "intentionally_no_longer_supported"))
+			j.initHiddenAPI(ctx, j.dexJarFile, outputFile, j.dexProperties.Uncompress_dex)
 		} else if Bool(j.dexProperties.Compile_dex) {
 			sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
 			if sdkDep.invalidVersion {
@@ -3120,21 +3102,10 @@
 	return nil
 }
 
-func (a *DexImport) LintDepSets() LintDepSets {
-	return LintDepSets{}
-}
-
 func (j *DexImport) IsInstallable() bool {
 	return true
 }
 
-func (j *DexImport) getStrictUpdatabilityLinting() bool {
-	return false
-}
-
-func (j *DexImport) setStrictUpdatabilityLinting(bool) {
-}
-
 func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(j.properties.Jars) != 1 {
 		ctx.PropertyErrorf("jars", "exactly one jar must be provided")
@@ -3304,15 +3275,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
@@ -3332,9 +3308,13 @@
 	depName := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(depModule))
 
 	var sdkLib *string
-	if lib, ok := depModule.(SdkLibraryDependency); ok && lib.sharedLibrary() {
+	if lib, ok := android.OtherModuleProvider(ctx, depModule, SdkLibraryInfoProvider); 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/java_test.go b/java/java_test.go
index e4e6bca..24dabdb1 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -670,7 +670,7 @@
 		java_library {
 			name: "foo",
 			srcs: ["a.java", ":stubs-source"],
-			libs: ["bar", "sdklib"],
+			libs: ["bar", "sdklib.stubs"],
 			static_libs: ["baz"],
 		}
 
@@ -2454,37 +2454,6 @@
 	}
 }
 
-func TestJavaExcludeStaticLib(t *testing.T) {
-	ctx, _ := testJava(t, `
-	java_library {
-		name: "bar",
-	}
-	java_library {
-		name: "foo",
-	}
-	java_library {
-		name: "baz",
-		static_libs: [
-			"foo",
-			"bar",
-		],
-		exclude_static_libs: [
-			"bar",
-		],
-	}
-	`)
-
-	// "bar" not included as dependency of "baz"
-	CheckModuleDependencies(t, ctx, "baz", "android_common", []string{
-		`core-lambda-stubs`,
-		`ext`,
-		`foo`,
-		`framework`,
-		`stable-core-platform-api-stubs-system-modules`,
-		`stable.core.platform.api.stubs`,
-	})
-}
-
 func TestJavaLibraryWithResourcesStem(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
     java_library {
@@ -2674,7 +2643,7 @@
 	android.AssertBoolEquals(t, "stub module expected to depend on from-source stub",
 		true, CheckModuleHasDependency(t, result.TestContext,
 			apiScopePublic.stubsLibraryModuleName("foo"), "android_common",
-			apiScopePublic.sourceStubLibraryModuleName("foo")))
+			apiScopePublic.sourceStubsLibraryModuleName("foo")))
 
 	android.AssertBoolEquals(t, "stub module expected to not depend on from-text stub",
 		false, CheckModuleHasDependency(t, result.TestContext,
@@ -3065,6 +3034,43 @@
 		"baz.jar", bazOutputPaths[0].Rel())
 }
 
+func TestCoverage(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		prepareForTestWithFrameworkJacocoInstrumentation,
+		PrepareForTestWithTransitiveClasspathEnabled,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "foo",
+			srcs: ["foo.java"],
+			static_libs: ["android.car"],
+			platform_apis: true,
+		}
+
+		// A library in InstrumentFrameworkModules
+		java_library {
+			name: "android.car",
+			srcs: ["android.car.java"],
+		}
+	`)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	androidCar := result.ModuleForTests("android.car", "android_common")
+
+	fooJacoco := foo.Rule("jacoco")
+	fooCombine := foo.Description("for javac")
+
+	androidCarJacoco := androidCar.Rule("jacoco")
+	androidCarJavac := androidCar.Rule("javac")
+
+	android.AssertStringEquals(t, "foo instrumentation rule inputs", fooJacoco.Input.String(), fooCombine.Output.String())
+	android.AssertStringEquals(t, "android.car instrumentation rule inputs", androidCarJacoco.Input.String(), androidCarJavac.Output.String())
+
+	// The input to instrumentation for the `foo` app contains the non-instrumented android.car classes.
+	android.AssertStringListContains(t, "foo combined inputs", fooCombine.Inputs.Strings(), androidCarJavac.Output.String())
+	android.AssertStringListDoesNotContain(t, "foo combined inputs", fooCombine.Inputs.Strings(), androidCarJacoco.Output.String())
+}
+
 func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTestOnly []string, expectedTopLevel []string) {
 	t.Helper()
 	actualTrueModules := []string{}
@@ -3095,3 +3101,37 @@
 		t.Errorf("top-level: Expected but not found: %v, Found but not expected: %v", left, right)
 	}
 }
+
+// Test that a dependency edge is created to the matching variant of a native library listed in `jni_libs` of java_binary
+func TestNativeRequiredDepOfJavaBinary(t *testing.T) {
+	findDepsOfModule := func(ctx *android.TestContext, module android.Module, depName string) []blueprint.Module {
+		var ret []blueprint.Module
+		ctx.VisitDirectDeps(module, func(dep blueprint.Module) {
+			if dep.Name() == depName {
+				ret = append(ret, dep)
+			}
+		})
+		return ret
+	}
+
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+java_binary {
+	name: "myjavabin",
+	main_class: "com.android.MyJava",
+	jni_libs: ["mynativelib"],
+}
+cc_library_shared {
+	name: "mynativelib",
+}
+`
+	res, _ := testJava(t, bp)
+	// The first variant installs the native library via the common variant, so check the deps of both variants.
+	nativeVariantDepsWithDups := findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_arm64_armv8-a").Module(), "mynativelib")
+	nativeVariantDepsWithDups = append(nativeVariantDepsWithDups, findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_common").Module(), "mynativelib")...)
+
+	nativeVariantDepsUnique := map[blueprint.Module]bool{}
+	for _, dep := range nativeVariantDepsWithDups {
+		nativeVariantDepsUnique[dep] = true
+	}
+	android.AssertIntEquals(t, "Create a dep on the first variant", 1, len(nativeVariantDepsUnique))
+}
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/lint.go b/java/lint.go
index 6782adc..2cbefc3 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -19,6 +19,7 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -90,7 +91,6 @@
 	compileSdkKind          android.SdkKind
 	javaLanguageLevel       string
 	kotlinLanguageLevel     string
-	outputs                 lintOutputs
 	properties              LintProperties
 	extraMainlineLintErrors []string
 	compile_data            android.Paths
@@ -100,68 +100,55 @@
 	buildModuleReportZip bool
 }
 
-type lintOutputs struct {
-	html              android.Path
-	text              android.Path
-	xml               android.Path
-	referenceBaseline android.Path
-
-	depSets LintDepSets
-}
-
-type lintOutputsIntf interface {
-	lintOutputs() *lintOutputs
-}
-
-type LintDepSetsIntf interface {
-	LintDepSets() LintDepSets
-
-	// Methods used to propagate strict_updatability_linting values.
-	GetStrictUpdatabilityLinting() bool
-	SetStrictUpdatabilityLinting(bool)
-}
-
 type LintDepSets struct {
-	HTML, Text, XML *android.DepSet[android.Path]
+	HTML, Text, XML, Baseline *android.DepSet[android.Path]
 }
 
 type LintDepSetsBuilder struct {
-	HTML, Text, XML *android.DepSetBuilder[android.Path]
+	HTML, Text, XML, Baseline *android.DepSetBuilder[android.Path]
 }
 
 func NewLintDepSetBuilder() LintDepSetsBuilder {
 	return LintDepSetsBuilder{
-		HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER),
-		Text: android.NewDepSetBuilder[android.Path](android.POSTORDER),
-		XML:  android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		HTML:     android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		Text:     android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		XML:      android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		Baseline: android.NewDepSetBuilder[android.Path](android.POSTORDER),
 	}
 }
 
-func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
+func (l LintDepSetsBuilder) Direct(html, text, xml android.Path, baseline android.OptionalPath) LintDepSetsBuilder {
 	l.HTML.Direct(html)
 	l.Text.Direct(text)
 	l.XML.Direct(xml)
+	if baseline.Valid() {
+		l.Baseline.Direct(baseline.Path())
+	}
 	return l
 }
 
-func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
-	if depSets.HTML != nil {
-		l.HTML.Transitive(depSets.HTML)
+func (l LintDepSetsBuilder) Transitive(info *LintInfo) LintDepSetsBuilder {
+	if info.TransitiveHTML != nil {
+		l.HTML.Transitive(info.TransitiveHTML)
 	}
-	if depSets.Text != nil {
-		l.Text.Transitive(depSets.Text)
+	if info.TransitiveText != nil {
+		l.Text.Transitive(info.TransitiveText)
 	}
-	if depSets.XML != nil {
-		l.XML.Transitive(depSets.XML)
+	if info.TransitiveXML != nil {
+		l.XML.Transitive(info.TransitiveXML)
+	}
+	if info.TransitiveBaseline != nil {
+		l.Baseline.Transitive(info.TransitiveBaseline)
 	}
 	return l
 }
 
 func (l LintDepSetsBuilder) Build() LintDepSets {
 	return LintDepSets{
-		HTML: l.HTML.Build(),
-		Text: l.Text.Build(),
-		XML:  l.XML.Build(),
+		HTML:     l.HTML.Build(),
+		Text:     l.Text.Build(),
+		XML:      l.XML.Build(),
+		Baseline: l.Baseline.Build(),
 	}
 }
 
@@ -209,24 +196,18 @@
 	},
 }
 
-func (l *linter) LintDepSets() LintDepSets {
-	return l.outputs.depSets
-}
+var LintProvider = blueprint.NewProvider[*LintInfo]()
 
-func (l *linter) GetStrictUpdatabilityLinting() bool {
-	return BoolDefault(l.properties.Lint.Strict_updatability_linting, false)
-}
+type LintInfo struct {
+	HTML              android.Path
+	Text              android.Path
+	XML               android.Path
+	ReferenceBaseline android.Path
 
-func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) {
-	l.properties.Lint.Strict_updatability_linting = &strictLinting
-}
-
-var _ LintDepSetsIntf = (*linter)(nil)
-
-var _ lintOutputsIntf = (*linter)(nil)
-
-func (l *linter) lintOutputs() *lintOutputs {
-	return &l.outputs
+	TransitiveHTML     *android.DepSet[android.Path]
+	TransitiveText     *android.DepSet[android.Path]
+	TransitiveXML      *android.DepSet[android.Path]
+	TransitiveBaseline *android.DepSet[android.Path]
 }
 
 func (l *linter) enabled() bool {
@@ -262,7 +243,9 @@
 	return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
 }
 
-func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths {
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path,
+	baselines android.Paths) lintPaths {
+
 	projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
 	// Lint looks for a lint.xml file next to the project.xml file, give it one.
 	configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
@@ -325,12 +308,10 @@
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	if l.GetStrictUpdatabilityLinting() {
+	if Bool(l.properties.Lint.Strict_updatability_linting) && len(baselines) > 0 {
 		// Verify the module does not baseline issues that endanger safe updatability.
-		if l.properties.Lint.Baseline_filename != nil {
-			cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
-			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
-		}
+		strictUpdatabilityChecksOutputFile := VerifyStrictUpdatabilityChecks(ctx, baselines)
+		cmd.Validation(strictUpdatabilityChecksOutputFile)
 	}
 
 	return lintPaths{
@@ -342,6 +323,22 @@
 
 }
 
+func VerifyStrictUpdatabilityChecks(ctx android.ModuleContext, baselines android.Paths) android.Path {
+	rule := android.NewRuleBuilder(pctx, ctx)
+	baselineRspFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check_baselines.rsp")
+	outputFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check.stamp")
+	rule.Command().Text("rm -f").Output(outputFile)
+	rule.Command().
+		BuiltTool("lint_strict_updatability_checks").
+		FlagWithArg("--name ", ctx.ModuleName()).
+		FlagWithRspFileInputList("--baselines ", baselineRspFile, baselines).
+		FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+	rule.Command().Text("touch").Output(outputFile)
+	rule.Build("lint_strict_updatability_checks", "lint strict updatability checks")
+
+	return outputFile
+}
+
 // generateManifest adds a command to the rule to write a simple manifest that contains the
 // minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
 func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
@@ -411,6 +408,26 @@
 	l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx,
 		"prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar"))
 
+	var baseline android.OptionalPath
+	if l.properties.Lint.Baseline_filename != nil {
+		baseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
+	}
+
+	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
+	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
+	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
+	referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
+
+	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline)
+
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+		if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok {
+			depSetsBuilder.Transitive(info)
+		}
+	})
+
+	depSets := depSetsBuilder.Build()
+
 	rule := android.NewRuleBuilder(pctx, ctx).
 		Sbox(android.PathForModuleOut(ctx, "lint"),
 			android.PathForModuleOut(ctx, "lint.sbox.textproto")).
@@ -437,20 +454,9 @@
 	srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
 	rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data)
 
-	lintPaths := l.writeLintProjectXML(ctx, rule, srcsList)
+	baselines := depSets.Baseline.ToList()
 
-	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
-	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
-	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
-	referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
-
-	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
-
-	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
-		if depLint, ok := dep.(LintDepSetsIntf); ok {
-			depSetsBuilder.Transitive(depLint.LintDepSets())
-		}
-	})
+	lintPaths := l.writeLintProjectXML(ctx, rule, srcsList, baselines)
 
 	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
@@ -505,8 +511,8 @@
 		cmd.FlagWithArg("--check ", checkOnly)
 	}
 
-	if l.properties.Lint.Baseline_filename != nil {
-		cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
+	if baseline.Valid() {
+		cmd.FlagWithInput("--baseline ", baseline.Path())
 	}
 
 	cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
@@ -530,25 +536,30 @@
 
 	rule.Build("lint", "lint")
 
-	l.outputs = lintOutputs{
-		html:              html,
-		text:              text,
-		xml:               xml,
-		referenceBaseline: referenceBaseline,
+	android.SetProvider(ctx, LintProvider, &LintInfo{
+		HTML:              html,
+		Text:              text,
+		XML:               xml,
+		ReferenceBaseline: referenceBaseline,
 
-		depSets: depSetsBuilder.Build(),
-	}
+		TransitiveHTML:     depSets.HTML,
+		TransitiveText:     depSets.Text,
+		TransitiveXML:      depSets.XML,
+		TransitiveBaseline: depSets.Baseline,
+	})
 
 	if l.buildModuleReportZip {
-		l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
+		l.reports = BuildModuleLintReportZips(ctx, depSets, nil)
 	}
 
 	// Create a per-module phony target to run the lint check.
 	phonyName := ctx.ModuleName() + "-lint"
 	ctx.Phony(phonyName, xml)
+
+	ctx.SetOutputFiles(android.Paths{xml}, ".lint")
 }
 
-func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
+func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets, validations android.Paths) android.Paths {
 	htmlList := android.SortedUniquePaths(depSets.HTML.ToList())
 	textList := android.SortedUniquePaths(depSets.Text.ToList())
 	xmlList := android.SortedUniquePaths(depSets.XML.ToList())
@@ -558,13 +569,13 @@
 	}
 
 	htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
-	lintZip(ctx, htmlList, htmlZip)
+	lintZip(ctx, htmlList, htmlZip, validations)
 
 	textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
-	lintZip(ctx, textList, textZip)
+	lintZip(ctx, textList, textZip, validations)
 
 	xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
-	lintZip(ctx, xmlList, xmlZip)
+	lintZip(ctx, xmlList, xmlZip, validations)
 
 	return android.Paths{htmlZip, textZip, xmlZip}
 }
@@ -642,7 +653,7 @@
 		return
 	}
 
-	var outputs []*lintOutputs
+	var outputs []*LintInfo
 	var dirs []string
 	ctx.VisitAllModules(func(m android.Module) {
 		if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
@@ -658,14 +669,14 @@
 			}
 		}
 
-		if l, ok := m.(lintOutputsIntf); ok {
-			outputs = append(outputs, l.lintOutputs())
+		if lintInfo, ok := android.OtherModuleProvider(ctx, m, LintProvider); ok {
+			outputs = append(outputs, lintInfo)
 		}
 	})
 
 	dirs = android.SortedUniqueStrings(dirs)
 
-	zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
+	zip := func(outputPath android.WritablePath, get func(*LintInfo) android.Path) {
 		var paths android.Paths
 
 		for _, output := range outputs {
@@ -674,20 +685,20 @@
 			}
 		}
 
-		lintZip(ctx, paths, outputPath)
+		lintZip(ctx, paths, outputPath, nil)
 	}
 
 	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
-	zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
+	zip(l.htmlZip, func(l *LintInfo) android.Path { return l.HTML })
 
 	l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
-	zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
+	zip(l.textZip, func(l *LintInfo) android.Path { return l.Text })
 
 	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
-	zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
+	zip(l.xmlZip, func(l *LintInfo) android.Path { return l.XML })
 
 	l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip")
-	zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline })
+	zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline })
 
 	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
 }
@@ -703,17 +714,9 @@
 func init() {
 	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
-
-	registerLintBuildComponents(android.InitRegistrationContext)
 }
 
-func registerLintBuildComponents(ctx android.RegistrationContext) {
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel()
-	})
-}
-
-func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
+func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath, validations android.Paths) {
 	paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
 
 	sort.Slice(paths, func(i, j int) bool {
@@ -725,19 +728,8 @@
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputPath).
 		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
-		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
+		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths).
+		Validations(validations)
 
 	rule.Build(outputPath.Base(), outputPath.Base())
 }
-
-// Enforce the strict updatability linting to all applicable transitive dependencies.
-func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) {
-	m := ctx.Module()
-	if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() {
-		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
-			if a, ok := d.(LintDepSetsIntf); ok {
-				a.SetStrictUpdatabilityLinting(true)
-			}
-		})
-	}
-}
diff --git a/java/lint_test.go b/java/lint_test.go
index b51753f..afe3914 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -164,7 +164,7 @@
 			sdk_version: "current",
 			lint: {
 				strict_updatability_linting: true,
-				baseline_filename: "lint-baseline.xml",
+				baseline_filename: "foo_lint_baseline.xml",
 			},
 		}
 
@@ -176,7 +176,7 @@
 			min_sdk_version: "29",
 			sdk_version: "current",
 			lint: {
-				baseline_filename: "lint-baseline.xml",
+				baseline_filename: "bar_lint_baseline.xml",
 			}
 		}
 	`
@@ -188,18 +188,13 @@
 		RunTestWithBp(t, bp)
 
 	foo := result.ModuleForTests("foo", "android_common")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+	strictUpdatabilityCheck := foo.Output("lint_strict_updatability_check.stamp")
+	if !strings.Contains(strictUpdatabilityCheck.RuleParams.Command,
+		"--disallowed_issues NewApi") {
 		t.Error("did not restrict baselining NewApi")
 	}
-
-	bar := result.ModuleForTests("bar", "android_common")
-	sboxProto = android.RuleBuilderSboxProtoForTests(t, result.TestContext, bar.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Error("did not restrict baselining NewApi")
-	}
+	android.AssertStringListContains(t, "strict updatability check baseline inputs", strictUpdatabilityCheck.Inputs.Strings(), "foo_lint_baseline.xml")
+	android.AssertStringListContains(t, "strict updatability check baseline inputs", strictUpdatabilityCheck.Inputs.Strings(), "bar_lint_baseline.xml")
 }
 
 func TestJavaLintDatabaseSelectionFull(t *testing.T) {
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_bootclasspath.go b/java/platform_bootclasspath.go
index d794e51..5bb7754 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -33,6 +33,7 @@
 	platformBootclasspathArtBootJarDepTag  = bootclasspathDependencyTag{name: "art-boot-jar"}
 	platformBootclasspathBootJarDepTag     = bootclasspathDependencyTag{name: "platform-boot-jar"}
 	platformBootclasspathApexBootJarDepTag = bootclasspathDependencyTag{name: "apex-boot-jar"}
+	platformBootclasspathImplLibDepTag     = dependencyTag{name: "impl-lib-tag"}
 )
 
 type platformBootclasspathModule struct {
@@ -111,12 +112,18 @@
 	// Add dependencies on all the ART jars.
 	global := dexpreopt.GetGlobalConfig(ctx)
 	addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art")
+
+	var bootImageModuleNames []string
+
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
 	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag)
+	bootImageModuleNames = append(bootImageModuleNames, global.ArtApexJars.CopyOfJars()...)
 
 	// Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable
 	// APEXes.
-	addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag)
+	platformJars := b.platformJars(ctx)
+	addDependenciesOntoBootImageModules(ctx, platformJars, platformBootclasspathBootJarDepTag)
+	bootImageModuleNames = append(bootImageModuleNames, platformJars.CopyOfJars()...)
 
 	// Add dependencies on all the updatable jars, except the ART jars.
 	apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -127,9 +134,17 @@
 	addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
 	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
+	bootImageModuleNames = append(bootImageModuleNames, apexJars.CopyOfJars()...)
 
 	// Add dependencies on all the fragments.
 	b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx)
+
+	for _, bootImageModuleName := range bootImageModuleNames {
+		implLibName := implLibraryModuleName(bootImageModuleName)
+		if ctx.OtherModuleExists(implLibName) {
+			ctx.AddFarVariationDependencies(nil, platformBootclasspathImplLibDepTag, implLibName)
+		}
+	}
 }
 
 func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tag bootclasspathDependencyTag) {
@@ -166,8 +181,15 @@
 	allModules = append(allModules, apexModules...)
 	b.configuredModules = allModules
 
+	// Do not add implLibModule to allModules as the impl lib is only used to collect the
+	// transitive source files
+	var implLibModule []android.Module
+	ctx.VisitDirectDepsWithTag(implLibraryTag, func(m android.Module) {
+		implLibModule = append(implLibModule, m)
+	})
+
 	var transitiveSrcFiles android.Paths
-	for _, module := range allModules {
+	for _, module := range append(allModules, implLibModule...) {
 		if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			if depInfo.TransitiveSrcFiles != nil {
 				transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
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/prebuilt_apis.go b/java/prebuilt_apis.go
index 00613ee..527e479 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -124,8 +124,8 @@
 	return
 }
 
-func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string {
-	return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
+func prebuiltApiModuleName(moduleName, module, scope, version string) string {
+	return fmt.Sprintf("%s_%s_%s_%s", moduleName, scope, version, module)
 }
 func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
 	props := struct {
@@ -135,7 +135,7 @@
 		Installable *bool
 		Compile_dex *bool
 	}{
-		Name:        proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)),
+		Name:        proptools.StringPtr(prebuiltApiModuleName(mctx.ModuleName(), module, scope, version)),
 		Jars:        []string{path},
 		Sdk_version: proptools.StringPtr(sdkVersion),
 		Installable: proptools.BoolPtr(false),
@@ -257,8 +257,8 @@
 		Name *string
 		Libs []string
 	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version))
-	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version))
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx.ModuleName(), "system_modules", scope, version))
+	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx.ModuleName(), "core-for-system-modules", scope, version))
 
 	mctx.CreateModule(systemModulesImportFactory, &props)
 }
diff --git a/java/ravenwood.go b/java/ravenwood.go
index bb136cf..4c9fdc2 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"
@@ -54,14 +54,20 @@
 }
 
 type ravenwoodTestProperties struct {
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]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 {
@@ -120,7 +126,7 @@
 	}
 
 	// Add jni libs
-	for _, lib := range r.ravenwoodTestProperties.Jni_libs {
+	for _, lib := range r.ravenwoodTestProperties.Jni_libs.GetOrDefault(ctx, nil) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
 
@@ -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...)
@@ -225,10 +238,13 @@
 type ravenwoodLibgroupProperties struct {
 	Libs []string
 
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]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 {
@@ -264,12 +280,9 @@
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
 		ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
 	}
-	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs {
+	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs.GetOrDefault(ctx, nil) {
 		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/robolectric.go b/java/robolectric.go
index 374fc5f..30c7203 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -76,7 +76,7 @@
 	// Use strict mode to limit access of Robolectric API directly. See go/roboStrictMode
 	Strict_mode *bool
 
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 }
 
 type robolectricTest struct {
@@ -131,7 +131,7 @@
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
 		roboRuntimesTag, "robolectric-android-all-prebuilts")
 
-	for _, lib := range r.robolectricProperties.Jni_libs {
+	for _, lib := range r.robolectricProperties.Jni_libs.GetOrDefault(ctx, nil) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
 }
diff --git a/java/rro_test.go b/java/rro_test.go
index 742c839..4d79130 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -282,7 +282,7 @@
 			enforceRROTargets: []string{"foo"},
 			rroDirs: map[string][]string{
 				"foo": {"product/vendor/blah/overlay/lib2/res"},
-				"bar": {"product/vendor/blah/overlay/lib2/res"},
+				"bar": nil,
 			},
 		},
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index b7aa4e5..dfbde0e 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -29,11 +29,6 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
-	"android/soong/etc"
-)
-
-const (
-	sdkXmlFileSuffix = ".xml"
 )
 
 // A tag to associated a dependency with a specific api scope.
@@ -248,7 +243,7 @@
 	return scope.stubsLibraryModuleName(baseName) + ".from-text"
 }
 
-func (scope *apiScope) sourceStubLibraryModuleName(baseName string) string {
+func (scope *apiScope) sourceStubsLibraryModuleName(baseName string) string {
 	return scope.stubsLibraryModuleName(baseName) + ".from-source"
 }
 
@@ -268,10 +263,6 @@
 	return baseName + ".stubs.source" + scope.moduleSuffix
 }
 
-func (scope *apiScope) apiModuleName(baseName string) string {
-	return baseName + ".api" + scope.moduleSuffix
-}
-
 func (scope *apiScope) String() string {
 	return scope.name
 }
@@ -830,16 +821,6 @@
 }
 
 type commonToSdkLibraryAndImportProperties struct {
-	// The naming scheme to use for the components that this module creates.
-	//
-	// If not specified then it defaults to "default".
-	//
-	// This is a temporary mechanism to simplify conversion from separate modules for each
-	// component that follow a different naming pattern to the default one.
-	//
-	// TODO(b/155480189) - Remove once naming inconsistencies have been resolved.
-	Naming_scheme *string
-
 	// Specifies whether this module can be used as an Android shared library; defaults
 	// to true.
 	//
@@ -915,8 +896,6 @@
 
 	scopePaths map[*apiScope]*scopePaths
 
-	namingScheme sdkLibraryComponentNamingScheme
-
 	commonSdkLibraryProperties commonToSdkLibraryAndImportProperties
 
 	// Paths to commonSdkLibraryProperties.Doctag_files
@@ -943,16 +922,7 @@
 	c.initSdkLibraryComponent(module)
 }
 
-func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
-	schemeProperty := proptools.StringDefault(c.commonSdkLibraryProperties.Naming_scheme, "default")
-	switch schemeProperty {
-	case "default":
-		c.namingScheme = &defaultNamingScheme{}
-	default:
-		ctx.PropertyErrorf("naming_scheme", "expected 'default' but was %q", schemeProperty)
-		return false
-	}
-
+func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied() bool {
 	namePtr := proptools.StringPtr(c.module.RootLibraryName())
 	c.sdkLibraryComponentProperties.SdkLibraryName = namePtr
 
@@ -974,62 +944,32 @@
 	return c.sharedLibrary()
 }
 
-func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) {
+func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) SdkLibraryInfo {
 	c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
-}
 
-func (c *commonToSdkLibraryAndImport) getImplLibraryModule() *Library {
-	return c.implLibraryModule
-}
+	everythingStubPaths := make(map[android.SdkKind]OptionalDexJarPath)
+	exportableStubPaths := make(map[android.SdkKind]OptionalDexJarPath)
+	removedApiFilePaths := make(map[android.SdkKind]android.OptionalPath)
+	for kind := android.SdkNone; kind <= android.SdkPrivate; kind += 1 {
+		everythingStubPath := makeUnsetDexJarPath()
+		exportableStubPath := makeUnsetDexJarPath()
+		removedApiFilePath := android.OptionalPath{}
+		if scopePath := c.findClosestScopePath(sdkKindToApiScope(kind)); scopePath != nil {
+			everythingStubPath = scopePath.stubsDexJarPath
+			exportableStubPath = scopePath.exportableStubsDexJarPath
+			removedApiFilePath = scopePath.removedApiFilePath
+		}
+		everythingStubPaths[kind] = everythingStubPath
+		exportableStubPaths[kind] = exportableStubPath
+		removedApiFilePaths[kind] = removedApiFilePath
+	}
 
-// Module name of the runtime implementation library
-func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
-	return c.module.RootLibraryName() + ".impl"
-}
-
-// Module name of the XML file for the lib
-func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
-	return c.module.RootLibraryName() + sdkXmlFileSuffix
-}
-
-// Name of the java_library module that compiles the stubs source.
-func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return c.namingScheme.stubsLibraryModuleName(apiScope, baseName)
-}
-
-// Name of the java_library module that compiles the exportable stubs source.
-func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return c.namingScheme.exportableStubsLibraryModuleName(apiScope, baseName)
-}
-
-// Name of the droidstubs module that generates the stubs source and may also
-// generate/check the API.
-func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
-}
-
-// Name of the java_api_library module that generates the from-text stubs source
-// and compiles to a jar file.
-func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return c.namingScheme.apiLibraryModuleName(apiScope, baseName)
-}
-
-// Name of the java_library module that compiles the stubs
-// generated from source Java files.
-func (c *commonToSdkLibraryAndImport) sourceStubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return c.namingScheme.sourceStubsLibraryModuleName(apiScope, baseName)
-}
-
-// Name of the java_library module that compiles the exportable stubs
-// generated from source Java files.
-func (c *commonToSdkLibraryAndImport) exportableSourceStubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return c.namingScheme.exportableSourceStubsLibraryModuleName(apiScope, baseName)
+	return SdkLibraryInfo{
+		EverythingStubDexJarPaths: everythingStubPaths,
+		ExportableStubDexJarPaths: exportableStubPaths,
+		RemovedTxtFiles:           removedApiFilePaths,
+		SharedLibrary:             c.sharedLibrary(),
+	}
 }
 
 // The component names for different outputs of the java_sdk_library.
@@ -1103,44 +1043,6 @@
 	return nil
 }
 
-func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-
-	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
-	if !sdkVersion.ApiLevel.IsPreview() {
-		return PrebuiltJars(ctx, c.module.RootLibraryName(), sdkVersion)
-	}
-
-	paths := c.selectScopePaths(ctx, sdkVersion.Kind)
-	if paths == nil {
-		return nil
-	}
-
-	return paths.stubsHeaderPath
-}
-
-// selectScopePaths returns the *scopePaths appropriate for the specific kind.
-//
-// If the module does not support the specific kind then it will return the *scopePaths for the
-// closest kind which is a subset of the requested kind. e.g. if requesting android.SdkModule then
-// it will return *scopePaths for android.SdkSystem if available or android.SdkPublic of not.
-func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleContext, kind android.SdkKind) *scopePaths {
-	apiScope := sdkKindToApiScope(kind)
-
-	paths := c.findClosestScopePath(apiScope)
-	if paths == nil {
-		var scopes []string
-		for _, s := range AllApiScopes {
-			if c.findScopePaths(s) != nil {
-				scopes = append(scopes, s.name)
-			}
-		}
-		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.RootLibraryName(), scopes)
-		return nil
-	}
-
-	return paths
-}
-
 // sdkKindToApiScope maps from android.SdkKind to apiScope.
 func sdkKindToApiScope(kind android.SdkKind) *apiScope {
 	var apiScope *apiScope
@@ -1159,37 +1061,6 @@
 	return apiScope
 }
 
-// to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath {
-	paths := c.selectScopePaths(ctx, kind)
-	if paths == nil {
-		return makeUnsetDexJarPath()
-	}
-
-	return paths.stubsDexJarPath
-}
-
-// to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath {
-	paths := c.selectScopePaths(ctx, kind)
-	if paths == nil {
-		return makeUnsetDexJarPath()
-	}
-
-	return paths.exportableStubsDexJarPath
-}
-
-// to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath {
-	apiScope := sdkKindToApiScope(kind)
-	paths := c.findScopePaths(apiScope)
-	if paths == nil {
-		return android.OptionalPath{}
-	}
-
-	return paths.removedApiFilePath
-}
-
 func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} {
 	componentProps := &struct {
 		SdkLibraryName              *string
@@ -1280,33 +1151,43 @@
 var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil)
 var _ SdkLibraryComponentDependency = (*SdkLibraryImport)(nil)
 
-// Provides access to sdk_version related files, e.g. header and implementation jars.
-type SdkLibraryDependency interface {
-	SdkLibraryComponentDependency
+type SdkLibraryInfo struct {
+	// GeneratingLibs is the names of the library modules that this sdk library
+	// generates. Note that this only includes the name of the modules that other modules can
+	// depend on, and is not a holistic list of generated modules.
+	GeneratingLibs []string
 
-	// Get the header jars appropriate for the supplied sdk_version.
-	//
-	// These are turbine generated jars so they only change if the externals of the
-	// class changes but it does not contain and implementation or JavaDoc.
-	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths
+	// Map of sdk kind to the dex jar for the "everything" stubs.
+	// It is needed by the hiddenapi processing tool which processes dex files.
+	EverythingStubDexJarPaths map[android.SdkKind]OptionalDexJarPath
 
-	// SdkApiStubDexJar returns the dex jar for the stubs for the prebuilt
-	// java_sdk_library_import module. It is needed by the hiddenapi processing tool which
-	// processes dex files.
-	SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
+	// Map of sdk kind to the dex jar for the "exportable" stubs.
+	// It is needed by the hiddenapi processing tool which processes dex files.
+	ExportableStubDexJarPaths map[android.SdkKind]OptionalDexJarPath
 
-	// SdkApiExportableStubDexJar returns the exportable dex jar for the stubs for
-	// java_sdk_library module. It is needed by the hiddenapi processing tool which processes
-	// dex files.
-	SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
+	// Map of sdk kind to the optional path to the removed.txt file.
+	RemovedTxtFiles map[android.SdkKind]android.OptionalPath
 
-	// SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind.
-	SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath
+	// Whether if this can be used as a shared library.
+	SharedLibrary bool
+}
 
-	// sharedLibrary returns true if this can be used as a shared library.
-	sharedLibrary() bool
+var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]()
 
-	getImplLibraryModule() *Library
+func getGeneratingLibs(ctx android.ModuleContext, sdkVersion android.SdkSpec, sdkLibraryModuleName string, sdkInfo SdkLibraryInfo) []string {
+	apiLevel := sdkVersion.ApiLevel
+	if apiLevel.IsPreview() {
+		return sdkInfo.GeneratingLibs
+	}
+
+	generatingPrebuilts := []string{}
+	for _, apiScope := range AllApiScopes {
+		scopePrebuiltModuleName := prebuiltApiModuleName("sdk", sdkLibraryModuleName, apiScope.name, apiLevel.String())
+		if ctx.OtherModuleExists(scopePrebuiltModuleName) {
+			generatingPrebuilts = append(generatingPrebuilts, scopePrebuiltModuleName)
+		}
+	}
+	return generatingPrebuilts
 }
 
 type SdkLibrary struct {
@@ -1322,12 +1203,13 @@
 	builtInstalledForApex []dexpreopterInstall
 }
 
-var _ SdkLibraryDependency = (*SdkLibrary)(nil)
-
 func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool {
 	return module.sdkLibraryProperties.Generate_system_and_test_apis
 }
 
+var _ UsesLibraryDependency = (*SdkLibrary)(nil)
+
+// To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	if module.implLibraryModule != nil {
 		return module.implLibraryModule.DexJarBuildPath(ctx)
@@ -1335,6 +1217,7 @@
 	return makeUnsetDexJarPath()
 }
 
+// To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarInstallPath() android.Path {
 	if module.implLibraryModule != nil {
 		return module.implLibraryModule.DexJarInstallPath()
@@ -1397,7 +1280,7 @@
 }
 
 func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) {
-	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) {
+	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.BaseModuleContext, do android.PayloadDepsCallback) {
 		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 			isExternal := !module.depIsInSameApex(ctx, child)
 			if am, ok := child.(android.ApexModule); ok {
@@ -1452,7 +1335,7 @@
 		ctx.AddVariationDependencies(nil, apiScope.exportableStubsTag, exportableStubModuleName)
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
-		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
+		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.droidstubsModuleName(apiScope))
 
 		if module.compareAgainstLatestApi(apiScope) {
 			// Add dependencies on the latest finalized version of the API .txt file.
@@ -1522,8 +1405,6 @@
 		module.HideFromMake()
 	}
 
-	module.generateCommonBuildActions(ctx)
-
 	module.stem = proptools.StringDefault(module.overridableProperties.Stem, ctx.ModuleName())
 
 	module.provideHiddenAPIPropertyInfo(ctx)
@@ -1556,11 +1437,11 @@
 			if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
 				module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...)
 				module.implLibraryModule = to.(*Library)
-				android.SetProvider(ctx, JavaInfoProvider, dep)
 			}
 		}
 	})
 
+	sdkLibInfo := module.generateCommonBuildActions(ctx)
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		module.hideApexVariantFromMake = true
@@ -1589,7 +1470,10 @@
 		module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary
 		module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip
 		module.linter.reports = module.implLibraryModule.linter.reports
-		module.linter.outputs.depSets = module.implLibraryModule.LintDepSets()
+
+		if lintInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, LintProvider); ok {
+			android.SetProvider(ctx, LintProvider, lintInfo)
+		}
 
 		if !module.Host() {
 			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
@@ -1631,9 +1515,21 @@
 	}
 	android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
 	module.setOutputFiles(ctx)
+
+	var generatingLibs []string
+	for _, apiScope := range AllApiScopes {
+		if _, ok := module.scopePaths[apiScope]; ok {
+			generatingLibs = append(generatingLibs, module.stubsLibraryModuleName(apiScope))
+		}
+	}
+
 	if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil {
+		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
 		setOutputFiles(ctx, module.implLibraryModule.Module)
 	}
+
+	sdkLibInfo.GeneratingLibs = generatingLibs
+	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
 func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
@@ -1740,411 +1636,6 @@
 	return visibility
 }
 
-// Creates the implementation java library
-func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
-	visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
-
-	staticLibs := module.properties.Static_libs.Clone()
-	staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
-	props := struct {
-		Name           *string
-		Visibility     []string
-		Libs           []string
-		Static_libs    proptools.Configurable[[]string]
-		Apex_available []string
-		Stem           *string
-	}{
-		Name:       proptools.StringPtr(module.implLibraryModuleName()),
-		Visibility: visibility,
-
-		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
-
-		Static_libs: staticLibs,
-		// Pass the apex_available settings down so that the impl library can be statically
-		// embedded within a library that is added to an APEX. Needed for updatable-media.
-		Apex_available: module.ApexAvailable(),
-
-		Stem: proptools.StringPtr(module.Name()),
-	}
-
-	properties := []interface{}{
-		&module.properties,
-		&module.protoProperties,
-		&module.deviceProperties,
-		&module.dexProperties,
-		&module.dexpreoptProperties,
-		&module.linter.properties,
-		&module.overridableProperties,
-		&props,
-		module.sdkComponentPropertiesForChildLibrary(),
-	}
-	mctx.CreateModule(LibraryFactory, properties...)
-}
-
-type libraryProperties struct {
-	Name           *string
-	Visibility     []string
-	Srcs           []string
-	Installable    *bool
-	Sdk_version    *string
-	System_modules *string
-	Patch_module   *string
-	Libs           []string
-	Static_libs    []string
-	Compile_dex    *bool
-	Java_version   *string
-	Openjdk9       struct {
-		Srcs       []string
-		Javacflags []string
-	}
-	Dist struct {
-		Targets []string
-		Dest    *string
-		Dir     *string
-		Tag     *string
-	}
-	Is_stubs_module       *bool
-	Stub_contributing_api *string
-}
-
-func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
-	props := libraryProperties{}
-	props.Visibility = []string{"//visibility:override", "//visibility:private"}
-	// sources are generated from the droiddoc
-	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
-	props.Sdk_version = proptools.StringPtr(sdkVersion)
-	props.System_modules = module.deviceProperties.System_modules
-	props.Patch_module = module.properties.Patch_module
-	props.Installable = proptools.BoolPtr(false)
-	props.Libs = module.sdkLibraryProperties.Stub_only_libs
-	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
-	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
-	// The stub-annotations library contains special versions of the annotations
-	// with CLASS retention policy, so that they're kept.
-	if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
-		props.Libs = append(props.Libs, "stub-annotations")
-	}
-	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
-	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
-	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
-	// interop with older developer tools that don't support 1.9.
-	props.Java_version = proptools.StringPtr("1.8")
-	props.Is_stubs_module = proptools.BoolPtr(true)
-	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
-
-	return props
-}
-
-// Creates a static java library that has API stubs
-func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-
-	props := module.stubsLibraryProps(mctx, apiScope)
-	props.Name = proptools.StringPtr(module.sourceStubsLibraryModuleName(apiScope))
-	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)}
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-// Create a static java library that compiles the "exportable" stubs
-func (module *SdkLibrary) createExportableStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-	props := module.stubsLibraryProps(mctx, apiScope)
-	props.Name = proptools.StringPtr(module.exportableSourceStubsLibraryModuleName(apiScope))
-	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope) + "{.exportable}"}
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-// Creates a droidstubs module that creates stubs source files from the given full source
-// files and also updates and checks the API specification files.
-func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
-	props := struct {
-		Name                             *string
-		Visibility                       []string
-		Srcs                             []string
-		Installable                      *bool
-		Sdk_version                      *string
-		Api_surface                      *string
-		System_modules                   *string
-		Libs                             proptools.Configurable[[]string]
-		Output_javadoc_comments          *bool
-		Arg_files                        []string
-		Args                             *string
-		Java_version                     *string
-		Annotations_enabled              *bool
-		Merge_annotations_dirs           []string
-		Merge_inclusion_annotations_dirs []string
-		Generate_stubs                   *bool
-		Previous_api                     *string
-		Aconfig_declarations             []string
-		Check_api                        struct {
-			Current       ApiToCheck
-			Last_released ApiToCheck
-
-			Api_lint struct {
-				Enabled       *bool
-				New_since     *string
-				Baseline_file *string
-			}
-		}
-		Aidl struct {
-			Include_dirs       []string
-			Local_include_dirs []string
-		}
-		Dists []android.Dist
-	}{}
-
-	// The stubs source processing uses the same compile time classpath when extracting the
-	// API from the implementation library as it does when compiling it. i.e. the same
-	// * sdk version
-	// * system_modules
-	// * libs (static_libs/libs)
-
-	props.Name = proptools.StringPtr(name)
-	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...)
-	props.Sdk_version = module.deviceProperties.Sdk_version
-	props.Api_surface = &apiScope.name
-	props.System_modules = module.deviceProperties.System_modules
-	props.Installable = proptools.BoolPtr(false)
-	// A droiddoc module has only one Libs property and doesn't distinguish between
-	// shared libs and static libs. So we need to add both of these libs to Libs property.
-	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
-	props.Libs.AppendSimpleValue(module.properties.Libs)
-	props.Libs.Append(module.properties.Static_libs)
-	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
-	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
-	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
-	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
-	props.Java_version = module.properties.Java_version
-
-	props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
-	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
-	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
-	props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
-
-	droidstubsArgs := []string{}
-	if len(module.sdkLibraryProperties.Api_packages) != 0 {
-		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
-	}
-	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
-	disabledWarnings := []string{"HiddenSuperclass"}
-	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
-		disabledWarnings = append(disabledWarnings,
-			"BroadcastBehavior",
-			"DeprecationMismatch",
-			"MissingPermission",
-			"SdkConstant",
-			"Todo",
-		)
-	}
-	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
-
-	// Output Javadoc comments for public scope.
-	if apiScope == apiScopePublic {
-		props.Output_javadoc_comments = proptools.BoolPtr(true)
-	}
-
-	// Add in scope specific arguments.
-	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
-	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
-	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
-
-	// List of APIs identified from the provided source files are created. They are later
-	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
-	// last-released (a.k.a numbered) list of API.
-	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
-	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
-	apiDir := module.getApiDir()
-	currentApiFileName = path.Join(apiDir, currentApiFileName)
-	removedApiFileName = path.Join(apiDir, removedApiFileName)
-
-	// check against the not-yet-release API
-	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
-	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
-
-	if module.compareAgainstLatestApi(apiScope) {
-		// check against the latest released API
-		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
-		props.Previous_api = latestApiFilegroupName
-		props.Check_api.Last_released.Api_file = latestApiFilegroupName
-		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
-			module.latestRemovedApiFilegroupName(apiScope))
-		props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
-			module.latestIncompatibilitiesFilegroupName(apiScope))
-
-		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
-			// Enable api lint.
-			props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
-			props.Check_api.Api_lint.New_since = latestApiFilegroupName
-
-			// If it exists then pass a lint-baseline.txt through to droidstubs.
-			baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
-			baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
-			paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
-			if err != nil {
-				mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
-			}
-			if len(paths) == 1 {
-				props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
-			} else if len(paths) != 0 {
-				mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
-			}
-		}
-	}
-
-	if !Bool(module.sdkLibraryProperties.No_dist) {
-		// Dist the api txt and removed api txt artifacts for sdk builds.
-		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
-		stubsTypeTagPrefix := ""
-		if mctx.Config().ReleaseHiddenApiExportableStubs() {
-			stubsTypeTagPrefix = ".exportable"
-		}
-		for _, p := range []struct {
-			tag     string
-			pattern string
-		}{
-			// "exportable" api files are copied to the dist directory instead of the
-			// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
-			// is set. Otherwise, the "everything" api files are copied to the dist directory.
-			{tag: "%s.api.txt", pattern: "%s.txt"},
-			{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
-		} {
-			props.Dists = append(props.Dists, android.Dist{
-				Targets: []string{"sdk", "win_sdk"},
-				Dir:     distDir,
-				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
-				Tag:     proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
-			})
-		}
-	}
-
-	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
-}
-
-func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-	props := struct {
-		Name              *string
-		Visibility        []string
-		Api_contributions []string
-		Libs              proptools.Configurable[[]string]
-		Static_libs       []string
-		System_modules    *string
-		Enable_validation *bool
-		Stubs_type        *string
-		Sdk_version       *string
-		Previous_api      *string
-	}{}
-
-	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
-	props.Visibility = []string{"//visibility:override", "//visibility:private"}
-
-	apiContributions := []string{}
-
-	// Api surfaces are not independent of each other, but have subset relationships,
-	// and so does the api files. To generate from-text stubs for api surfaces other than public,
-	// all subset api domains' api_contriubtions must be added as well.
-	scope := apiScope
-	for scope != nil {
-		apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution")
-		scope = scope.extends
-	}
-	if apiScope == apiScopePublic {
-		additionalApiContribution := module.apiLibraryAdditionalApiContribution()
-		if additionalApiContribution != "" {
-			apiContributions = append(apiContributions, additionalApiContribution)
-		}
-	}
-
-	props.Api_contributions = apiContributions
-
-	// Ensure that stub-annotations is added to the classpath before any other libs
-	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
-	props.Libs.AppendSimpleValue([]string{"stub-annotations"})
-	props.Libs.AppendSimpleValue(module.properties.Libs)
-	props.Libs.Append(module.properties.Static_libs)
-	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
-	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
-	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
-
-	props.System_modules = module.deviceProperties.System_modules
-	props.Enable_validation = proptools.BoolPtr(true)
-	props.Stubs_type = proptools.StringPtr("everything")
-
-	if module.deviceProperties.Sdk_version != nil {
-		props.Sdk_version = module.deviceProperties.Sdk_version
-	}
-
-	if module.compareAgainstLatestApi(apiScope) {
-		// check against the latest released API
-		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
-		props.Previous_api = latestApiFilegroupName
-	}
-
-	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
-	props := libraryProperties{}
-
-	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
-	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
-	props.Sdk_version = proptools.StringPtr(sdkVersion)
-
-	props.System_modules = module.deviceProperties.System_modules
-
-	// The imports need to be compiled to dex if the java_sdk_library requests it.
-	compileDex := module.dexProperties.Compile_dex
-	if module.stubLibrariesCompiledForDex() {
-		compileDex = proptools.BoolPtr(true)
-	}
-	props.Compile_dex = compileDex
-
-	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
-
-	if !Bool(module.sdkLibraryProperties.No_dist) && doDist {
-		props.Dist.Targets = []string{"sdk", "win_sdk"}
-		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
-		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
-		props.Dist.Tag = proptools.StringPtr(".jar")
-	}
-	props.Is_stubs_module = proptools.BoolPtr(true)
-
-	return props
-}
-
-func (module *SdkLibrary) createTopLevelStubsLibrary(
-	mctx android.DefaultableHookContext, apiScope *apiScope) {
-
-	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
-	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
-	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
-	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
-
-	// Add the stub compiling java_library/java_api_library as static lib based on build config
-	staticLib := module.sourceStubsLibraryModuleName(apiScope)
-	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
-		staticLib = module.apiLibraryModuleName(apiScope)
-	}
-	props.Static_libs = append(props.Static_libs, staticLib)
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
-	mctx android.DefaultableHookContext, apiScope *apiScope) {
-
-	// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
-	doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
-	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
-	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
-
-	staticLib := module.exportableSourceStubsLibraryModuleName(apiScope)
-	props.Static_libs = append(props.Static_libs, staticLib)
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
 func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
 	return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
 }
@@ -2170,104 +1661,6 @@
 	return proptools.BoolDefault(module.sdkLibraryProperties.Build_from_text_stub, true)
 }
 
-// Creates the xml file that publicizes the runtime library
-func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
-	moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
-	var moduleMinApiLevelStr = moduleMinApiLevel.String()
-	if moduleMinApiLevel == android.NoneApiLevel {
-		moduleMinApiLevelStr = "current"
-	}
-	props := struct {
-		Name                      *string
-		Lib_name                  *string
-		Apex_available            []string
-		On_bootclasspath_since    *string
-		On_bootclasspath_before   *string
-		Min_device_sdk            *string
-		Max_device_sdk            *string
-		Sdk_library_min_api_level *string
-		Uses_libs_dependencies    []string
-	}{
-		Name:                      proptools.StringPtr(module.xmlPermissionsModuleName()),
-		Lib_name:                  proptools.StringPtr(module.BaseModuleName()),
-		Apex_available:            module.ApexProperties.Apex_available,
-		On_bootclasspath_since:    module.commonSdkLibraryProperties.On_bootclasspath_since,
-		On_bootclasspath_before:   module.commonSdkLibraryProperties.On_bootclasspath_before,
-		Min_device_sdk:            module.commonSdkLibraryProperties.Min_device_sdk,
-		Max_device_sdk:            module.commonSdkLibraryProperties.Max_device_sdk,
-		Sdk_library_min_api_level: &moduleMinApiLevelStr,
-		Uses_libs_dependencies:    module.usesLibraryProperties.Uses_libs,
-	}
-
-	mctx.CreateModule(sdkLibraryXmlFactory, &props)
-}
-
-func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkSpec) android.Paths {
-	var ver android.ApiLevel
-	var kind android.SdkKind
-	if s.UsePrebuilt(ctx) {
-		ver = s.ApiLevel
-		kind = s.Kind
-	} else {
-		// We don't have prebuilt SDK for the specific sdkVersion.
-		// Instead of breaking the build, fallback to use "system_current"
-		ver = android.FutureApiLevel
-		kind = android.SdkSystem
-	}
-
-	dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String())
-	jar := filepath.Join(dir, baseName+".jar")
-	jarPath := android.ExistentPathForSource(ctx, jar)
-	if !jarPath.Valid() {
-		if ctx.Config().AllowMissingDependencies() {
-			return android.Paths{android.PathForSource(ctx, jar)}
-		} else {
-			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.Raw, jar)
-		}
-		return nil
-	}
-	return android.Paths{jarPath.Path()}
-}
-
-// Check to see if the other module is within the same set of named APEXes as this module.
-//
-// If either this or the other module are on the platform then this will return
-// false.
-func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	otherApexInfo, _ := android.OtherModuleProvider(ctx, other, android.ApexInfoProvider)
-	return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants)
-}
-
-func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	// If the client doesn't set sdk_version, but if this library prefers stubs over
-	// the impl library, let's provide the widest API surface possible. To do so,
-	// force override sdk_version to module_current so that the closest possible API
-	// surface could be found in selectHeaderJarsForSdkVersion
-	if module.defaultsToStubs() && !sdkVersion.Specified() {
-		sdkVersion = android.SdkSpecFrom(ctx, "module_current")
-	}
-
-	// Only provide access to the implementation library if it is actually built.
-	if module.requiresRuntimeImplementationLibrary() {
-		// Check any special cases for java_sdk_library.
-		//
-		// Only allow access to the implementation library in the following condition:
-		// * No sdk_version specified on the referencing module.
-		// * The referencing module is in the same apex as this.
-		if sdkVersion.Kind == android.SdkPrivate || withinSameApexesAs(ctx, module) {
-			return module.implLibraryHeaderJars
-		}
-	}
-
-	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
-}
-
-// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	return module.sdkJars(ctx, sdkVersion)
-}
-
 var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
 
 func javaSdkLibraries(config android.Config) *[]string {
@@ -2284,11 +1677,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
@@ -2338,10 +1726,10 @@
 
 	for _, scope := range generatedScopes {
 		// Use the stubs source name for legacy reasons.
-		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
+		module.createDroidstubs(mctx, scope, module.droidstubsModuleName(scope), scope.droidstubsArgs)
 
-		module.createStubsLibrary(mctx, scope)
-		module.createExportableStubsLibrary(mctx, scope)
+		module.createFromSourceStubsLibrary(mctx, scope)
+		module.createExportableFromSourceStubsLibrary(mctx, scope)
 
 		if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
 			module.createApiLibrary(mctx, scope)
@@ -2391,54 +1779,6 @@
 	return !proptools.Bool(module.sdkLibraryProperties.Api_only)
 }
 
-func (module *SdkLibrary) defaultsToStubs() bool {
-	return proptools.Bool(module.sdkLibraryProperties.Default_to_stubs)
-}
-
-// Defines how to name the individual component modules the sdk library creates.
-type sdkLibraryComponentNamingScheme interface {
-	stubsLibraryModuleName(scope *apiScope, baseName string) string
-
-	stubsSourceModuleName(scope *apiScope, baseName string) string
-
-	apiLibraryModuleName(scope *apiScope, baseName string) string
-
-	sourceStubsLibraryModuleName(scope *apiScope, baseName string) string
-
-	exportableStubsLibraryModuleName(scope *apiScope, baseName string) string
-
-	exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string
-}
-
-type defaultNamingScheme struct {
-}
-
-func (s *defaultNamingScheme) stubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.stubsLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) stubsSourceModuleName(scope *apiScope, baseName string) string {
-	return scope.stubsSourceModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.apiLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) sourceStubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.sourceStubLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) exportableStubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.exportableStubsLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.exportableSourceStubsLibraryModuleName(baseName)
-}
-
-var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
-
 func moduleStubLinkType(j *Module) (stub bool, ret sdkLinkType) {
 	kind := android.ToSdkKind(proptools.String(j.properties.Stub_contributing_api))
 	switch kind {
@@ -2500,7 +1840,7 @@
 			module.commonSdkLibraryProperties.Shared_library = proptools.BoolPtr(false)
 		}
 
-		if module.initCommonAfterDefaultsApplied(ctx) {
+		if module.initCommonAfterDefaultsApplied() {
 			module.CreateInternalModules(ctx)
 		}
 	})
@@ -2579,8 +1919,6 @@
 	installFile android.Path
 }
 
-var _ SdkLibraryDependency = (*SdkLibraryImport)(nil)
-
 // The type of a structure that contains a field of type sdkLibraryScopeProperties
 // for each apiscope in allApiScopes, e.g. something like:
 //
@@ -2636,7 +1974,7 @@
 	InitJavaModule(module, android.HostAndDeviceSupported)
 
 	module.SetDefaultableHook(func(mctx android.DefaultableHookContext) {
-		if module.initCommonAfterDefaultsApplied(mctx) {
+		if module.initCommonAfterDefaultsApplied() {
 			module.createInternalModules(mctx)
 		}
 	})
@@ -2690,86 +2028,6 @@
 	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
 }
 
-func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
-	// Creates a java import for the jar with ".stubs" suffix
-	props := struct {
-		Name                             *string
-		Source_module_name               *string
-		Created_by_java_sdk_library_name *string
-		Sdk_version                      *string
-		Libs                             []string
-		Jars                             []string
-		Compile_dex                      *bool
-		Is_stubs_module                  *bool
-
-		android.UserSuppliedPrebuiltProperties
-	}{}
-	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
-	props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
-	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
-	props.Sdk_version = scopeProperties.Sdk_version
-	// Prepend any of the libs from the legacy public properties to the libs for each of the
-	// scopes to avoid having to duplicate them in each scope.
-	props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
-	props.Jars = scopeProperties.Jars
-
-	// The imports are preferred if the java_sdk_library_import is preferred.
-	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
-
-	// The imports need to be compiled to dex if the java_sdk_library_import requests it.
-	compileDex := module.properties.Compile_dex
-	if module.stubLibrariesCompiledForDex() {
-		compileDex = proptools.BoolPtr(true)
-	}
-	props.Compile_dex = compileDex
-	props.Is_stubs_module = proptools.BoolPtr(true)
-
-	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
-	props := struct {
-		Name                             *string
-		Source_module_name               *string
-		Created_by_java_sdk_library_name *string
-		Srcs                             []string
-
-		android.UserSuppliedPrebuiltProperties
-	}{}
-	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope))
-	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
-	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
-	props.Srcs = scopeProperties.Stub_srcs
-
-	// The stubs source is preferred if the java_sdk_library_import is preferred.
-	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
-
-	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
-	api_file := scopeProperties.Current_api
-	api_surface := &apiScope.name
-
-	props := struct {
-		Name                             *string
-		Source_module_name               *string
-		Created_by_java_sdk_library_name *string
-		Api_surface                      *string
-		Api_file                         *string
-		Visibility                       []string
-	}{}
-
-	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope) + ".api.contribution")
-	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
-	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
-	props.Api_surface = api_surface
-	props.Api_file = api_file
-	props.Visibility = []string{"//visibility:override", "//visibility:public"}
-
-	mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
 // Add the dependencies on the child module in the component deps mutator so that it
 // creates references to the prebuilt and not the source modules.
 func (module *SdkLibraryImport) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2783,7 +2041,7 @@
 
 		if len(scopeProperties.Stub_srcs) > 0 {
 			// Add dependencies to the prebuilt stubs source library
-			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.stubsSourceModuleName(apiScope)))
+			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.droidstubsModuleName(apiScope)))
 		}
 	}
 }
@@ -2837,8 +2095,6 @@
 var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
 
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	module.generateCommonBuildActions(ctx)
-
 	// Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework
 	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
 
@@ -2868,6 +2124,7 @@
 			}
 		}
 	})
+	sdkLibInfo := module.generateCommonBuildActions(ctx)
 
 	// Populate the scope paths with information from the properties.
 	for apiScope, scopeProperties := range module.scopeProperties {
@@ -2882,70 +2139,38 @@
 	}
 
 	if ctx.Device() {
-		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
-		// obtained from the associated deapexer module.
+		// Shared libraries deapexed from prebuilt apexes are no longer supported.
+		// Set the dexJarBuildPath to a fake path.
+		// This allows soong analysis pass, but will be an error during ninja execution if there are
+		// any rdeps.
 		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 		if ai.ForPrebuiltApex {
-			// Get the path of the dex implementation jar from the `deapexer` module.
-			di, err := android.FindDeapexerProviderForModule(ctx)
-			if err != nil {
-				// An error was found, possibly due to multiple apexes in the tree that export this library
-				// Defer the error till a client tries to call DexJarBuildPath
-				module.dexJarFileErr = err
-				module.initHiddenAPIError(err)
-				return
-			}
-			dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(module.BaseModuleName())
-			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
-				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
-				module.dexJarFile = dexJarFile
-				installPath := android.PathForModuleInPartitionInstall(
-					ctx, "apex", ai.ApexVariationName, dexJarFileApexRootRelative)
-				module.installFile = installPath
-				module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
+			module.dexJarFile = makeDexJarPathFromPath(android.PathForModuleInstall(ctx, "intentionally_no_longer_supported"))
+			module.initHiddenAPI(ctx, module.dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
+		}
+	}
 
-				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath)
-				module.dexpreopter.isSDKLibrary = true
-				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &module.dexpreopter)
-
-				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
-					module.dexpreopter.inputProfilePathOnHost = profilePath
-				}
-			} else {
-				// This should never happen as a variant for a prebuilt_apex is only created if the
-				// prebuilt_apex has been configured to export the java library dex file.
-				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName())
+	var generatingLibs []string
+	for _, apiScope := range AllApiScopes {
+		if scopeProperties, ok := module.scopeProperties[apiScope]; ok {
+			if len(scopeProperties.Jars) == 0 {
+				continue
 			}
+			generatingLibs = append(generatingLibs, module.stubsLibraryModuleName(apiScope))
 		}
 	}
 
 	module.setOutputFiles(ctx)
 	if module.implLibraryModule != nil {
+		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
 		setOutputFiles(ctx, module.implLibraryModule.Module)
 	}
+
+	sdkLibInfo.GeneratingLibs = generatingLibs
+	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
-func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
-
-	// For consistency with SdkLibrary make the implementation jar available to libraries that
-	// are within the same APEX.
-	implLibraryModule := module.implLibraryModule
-	if implLibraryModule != nil && withinSameApexesAs(ctx, module) {
-		if headerJars {
-			return implLibraryModule.HeaderJars()
-		} else {
-			return implLibraryModule.ImplementationJars()
-		}
-	}
-
-	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
-}
-
-// to satisfy SdkLibraryDependency interface
-func (module *SdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	// This module is just a wrapper for the prebuilt stubs.
-	return module.sdkJars(ctx, sdkVersion, true)
-}
+var _ UsesLibraryDependency = (*SdkLibraryImport)(nil)
 
 // to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -2984,29 +2209,6 @@
 }
 
 // to satisfy apex.javaDependency interface
-func (module *SdkLibraryImport) LintDepSets() LintDepSets {
-	if module.implLibraryModule == nil {
-		return LintDepSets{}
-	} else {
-		return module.implLibraryModule.LintDepSets()
-	}
-}
-
-func (module *SdkLibraryImport) GetStrictUpdatabilityLinting() bool {
-	if module.implLibraryModule == nil {
-		return false
-	} else {
-		return module.implLibraryModule.GetStrictUpdatabilityLinting()
-	}
-}
-
-func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(strictLinting bool) {
-	if module.implLibraryModule != nil {
-		module.implLibraryModule.SetStrictUpdatabilityLinting(strictLinting)
-	}
-}
-
-// to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) Stem() string {
 	return module.BaseModuleName()
 }
@@ -3047,333 +2249,6 @@
 	return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided)
 }
 
-// java_sdk_library_xml
-type sdkLibraryXml struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-	android.ApexModuleBase
-
-	properties sdkLibraryXmlProperties
-
-	outputFilePath android.OutputPath
-	installDirPath android.InstallPath
-
-	hideApexVariantFromMake bool
-}
-
-type sdkLibraryXmlProperties struct {
-	// canonical name of the lib
-	Lib_name *string
-
-	// Signals that this shared library is part of the bootclasspath starting
-	// on the version indicated in this attribute.
-	//
-	// This will make platforms at this level and above to ignore
-	// <uses-library> tags with this library name because the library is already
-	// available
-	On_bootclasspath_since *string
-
-	// Signals that this shared library was part of the bootclasspath before
-	// (but not including) the version indicated in this attribute.
-	//
-	// The system will automatically add a <uses-library> tag with this library to
-	// apps that target any SDK less than the version indicated in this attribute.
-	On_bootclasspath_before *string
-
-	// Indicates that PackageManager should ignore this shared library if the
-	// platform is below the version indicated in this attribute.
-	//
-	// This means that the device won't recognise this library as installed.
-	Min_device_sdk *string
-
-	// Indicates that PackageManager should ignore this shared library if the
-	// platform is above the version indicated in this attribute.
-	//
-	// This means that the device won't recognise this library as installed.
-	Max_device_sdk *string
-
-	// The SdkLibrary's min api level as a string
-	//
-	// This value comes from the ApiLevel of the MinSdkVersion property.
-	Sdk_library_min_api_level *string
-
-	// Uses-libs dependencies that the shared library requires to work correctly.
-	//
-	// This will add dependency="foo:bar" to the <library> section.
-	Uses_libs_dependencies []string
-}
-
-// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
-// Not to be used directly by users. java_sdk_library internally uses this.
-func sdkLibraryXmlFactory() android.Module {
-	module := &sdkLibraryXml{}
-
-	module.AddProperties(&module.properties)
-
-	android.InitApexModule(module)
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
-
-	return module
-}
-
-func (module *sdkLibraryXml) UniqueApexVariations() bool {
-	// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
-	// mounted APEX, which contains the name of the APEX.
-	return true
-}
-
-// from android.PrebuiltEtcModule
-func (module *sdkLibraryXml) BaseDir() string {
-	return "etc"
-}
-
-// from android.PrebuiltEtcModule
-func (module *sdkLibraryXml) SubDir() string {
-	return "permissions"
-}
-
-var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
-
-// from android.ApexModule
-func (module *sdkLibraryXml) AvailableFor(what string) bool {
-	return true
-}
-
-func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// do nothing
-}
-
-var _ android.ApexModule = (*sdkLibraryXml)(nil)
-
-// Implements android.ApexModule
-func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
-	return nil
-}
-
-// File path to the runtime implementation library
-func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
-	implName := proptools.String(module.properties.Lib_name)
-	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
-		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
-		// In most cases, this works fine. But when apex_name is set or override_apex is used
-		// this can be wrong.
-		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
-	}
-	partition := "system"
-	if module.SocSpecific() {
-		partition = "vendor"
-	} else if module.DeviceSpecific() {
-		partition = "odm"
-	} else if module.ProductSpecific() {
-		partition = "product"
-	} else if module.SystemExtSpecific() {
-		partition = "system_ext"
-	}
-	return "/" + partition + "/framework/" + implName + ".jar"
-}
-
-func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
-	if value == nil {
-		return ""
-	}
-	apiLevel, err := android.ApiLevelFromUser(ctx, *value)
-	if err != nil {
-		// attributes in bp files have underscores but in the xml have dashes.
-		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
-		return ""
-	}
-	if apiLevel.IsCurrent() {
-		// passing "current" would always mean a future release, never the current (or the current in
-		// progress) which means some conditions would never be triggered.
-		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
-			`"current" is not an allowed value for this attribute`)
-		return ""
-	}
-	// "safeValue" is safe because it translates finalized codenames to a string
-	// with their SDK int.
-	safeValue := apiLevel.String()
-	return formattedOptionalAttribute(attrName, &safeValue)
-}
-
-// formats an attribute for the xml permissions file if the value is not null
-// returns empty string otherwise
-func formattedOptionalAttribute(attrName string, value *string) string {
-	if value == nil {
-		return ""
-	}
-	return fmt.Sprintf("        %s=\"%s\"\n", attrName, *value)
-}
-
-func formattedDependenciesAttribute(dependencies []string) string {
-	if dependencies == nil {
-		return ""
-	}
-	return fmt.Sprintf("        dependency=\"%s\"\n", strings.Join(dependencies, ":"))
-}
-
-func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
-	libName := proptools.String(module.properties.Lib_name)
-	libNameAttr := formattedOptionalAttribute("name", &libName)
-	filePath := module.implPath(ctx)
-	filePathAttr := formattedOptionalAttribute("file", &filePath)
-	implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
-	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
-	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
-	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
-	dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies)
-	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
-	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
-	var libraryTag string
-	if module.properties.Min_device_sdk != nil {
-		libraryTag = "    <apex-library\n"
-	} else {
-		libraryTag = "    <library\n"
-	}
-
-	return strings.Join([]string{
-		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
-		"<!-- Copyright (C) 2018 The Android Open Source Project\n",
-		"\n",
-		"    Licensed under the Apache License, Version 2.0 (the \"License\");\n",
-		"    you may not use this file except in compliance with the License.\n",
-		"    You may obtain a copy of the License at\n",
-		"\n",
-		"        http://www.apache.org/licenses/LICENSE-2.0\n",
-		"\n",
-		"    Unless required by applicable law or agreed to in writing, software\n",
-		"    distributed under the License is distributed on an \"AS IS\" BASIS,\n",
-		"    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
-		"    See the License for the specific language governing permissions and\n",
-		"    limitations under the License.\n",
-		"-->\n",
-		"<permissions>\n",
-		libraryTag,
-		libNameAttr,
-		filePathAttr,
-		implicitFromAttr,
-		implicitUntilAttr,
-		minSdkAttr,
-		maxSdkAttr,
-		dependenciesAttr,
-		"    />\n",
-		"</permissions>\n",
-	}, "")
-}
-
-func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
-
-	libName := proptools.String(module.properties.Lib_name)
-	module.selfValidate(ctx)
-	xmlContent := module.permissionsContents(ctx)
-
-	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
-	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
-
-	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
-	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
-
-	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
-}
-
-func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
-	if module.hideApexVariantFromMake {
-		return []android.AndroidMkEntries{{
-			Disabled: true,
-		}}
-	}
-
-	return []android.AndroidMkEntries{{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(module.outputFilePath),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
-			},
-		},
-	}}
-}
-
-func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
-	module.validateAtLeastTAttributes(ctx)
-	module.validateMinAndMaxDeviceSdk(ctx)
-	module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
-	module.validateOnBootclasspathBeforeRequirements(ctx)
-}
-
-func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
-	t := android.ApiLevelOrPanic(ctx, "Tiramisu")
-	module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
-	module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
-	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
-	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
-}
-
-func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
-	if attr != nil {
-		if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
-			// we will inform the user of invalid inputs when we try to write the
-			// permissions xml file so we don't need to do it here
-			if t.GreaterThan(level) {
-				ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
-			}
-		}
-	}
-}
-
-func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
-	if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
-		min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
-		max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
-		if minErr == nil && maxErr == nil {
-			// we will inform the user of invalid inputs when we try to write the
-			// permissions xml file so we don't need to do it here
-			if min.GreaterThan(max) {
-				ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
-			}
-		}
-	}
-}
-
-func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
-	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
-	if module.properties.Min_device_sdk != nil {
-		api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
-		if err == nil {
-			if moduleMinApi.GreaterThan(api) {
-				ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
-			}
-		}
-	}
-	if module.properties.Max_device_sdk != nil {
-		api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
-		if err == nil {
-			if moduleMinApi.GreaterThan(api) {
-				ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
-			}
-		}
-	}
-}
-
-func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
-	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
-	if module.properties.On_bootclasspath_before != nil {
-		t := android.ApiLevelOrPanic(ctx, "Tiramisu")
-		// if we use the attribute, then we need to do this validation
-		if moduleMinApi.LessThan(t) {
-			// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
-			if module.properties.Min_device_sdk == nil {
-				ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
-			}
-		}
-	}
-}
-
 type sdkLibrarySdkMemberType struct {
 	android.SdkMemberTypeBase
 }
@@ -3510,7 +2385,6 @@
 		}
 	}
 
-	s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme
 	s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary())
 	s.Compile_dex = sdk.dexProperties.Compile_dex
 	s.Doctag_paths = sdk.doctagPaths
@@ -3520,7 +2394,7 @@
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 
-	implLibrary := sdk.getImplLibraryModule()
+	implLibrary := sdk.implLibraryModule
 	if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
 		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
 	}
@@ -3607,19 +2481,3 @@
 		propertySet.AddProperty("doctag_files", dests)
 	}
 }
-
-// TODO(b/358613520): This can be removed when modules are no longer allowed to depend on the top-level library.
-func (s *SdkLibrary) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	s.Library.IDEInfo(ctx, dpInfo)
-	if s.implLibraryModule != nil {
-		dpInfo.Deps = append(dpInfo.Deps, s.implLibraryModule.Name())
-	} else {
-		// This java_sdk_library does not have an implementation (it sets `api_only` to true).
-		// Examples of this are `art.module.intra.core.api` (IntraCore api surface).
-		// Return the "public" stubs for these.
-		stubPaths := s.findClosestScopePath(apiScopePublic)
-		if len(stubPaths.stubsHeaderPath) > 0 {
-			dpInfo.Jars = append(dpInfo.Jars, stubPaths.stubsHeaderPath[0].String())
-		}
-	}
-}
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
deleted file mode 100644
index 4f83981..0000000
--- a/java/sdk_library_external.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2020 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 java
-
-import (
-	"android/soong/android"
-)
-
-type partitionGroup int
-
-// Representation of partition group for checking inter-partition library dependencies.
-// Between system and system_ext, there are no restrictions of dependencies,
-// so we can treat these partitions as the same in terms of inter-partition dependency.
-// Same policy is applied between vendor and odm partiton.
-const (
-	partitionGroupNone partitionGroup = iota
-	// group for system, and system_ext partition
-	partitionGroupSystem
-	// group for vendor and odm partition
-	partitionGroupVendor
-	// product partition
-	partitionGroupProduct
-)
-
-func (g partitionGroup) String() string {
-	switch g {
-	case partitionGroupSystem:
-		return "system"
-	case partitionGroupVendor:
-		return "vendor"
-	case partitionGroupProduct:
-		return "product"
-	}
-
-	return ""
-}
-
-// Get partition group of java module that can be used at inter-partition dependency check.
-// We currently have three groups
-//
-//	(system, system_ext) => system partition group
-//	(vendor, odm) => vendor partition group
-//	(product) => product partition group
-func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup {
-	// system and system_ext partition can be treated as the same in terms of inter-partition dependency.
-	if j.Platform() || j.SystemExtSpecific() {
-		return partitionGroupSystem
-	}
-
-	// vendor and odm partition can be treated as the same in terms of inter-partition dependency.
-	if j.SocSpecific() || j.DeviceSpecific() {
-		return partitionGroupVendor
-	}
-
-	// product partition is independent.
-	if j.ProductSpecific() {
-		return partitionGroupProduct
-	}
-
-	panic("Cannot determine partition type")
-}
-
-func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool {
-	return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
-}
-
-func (j *Module) syspropWithPublicStubs() bool {
-	return j.deviceProperties.SyspropPublicStub != ""
-}
-
-type javaSdkLibraryEnforceContext interface {
-	Name() string
-	allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
-	partitionGroup(ctx android.EarlyModuleContext) partitionGroup
-	syspropWithPublicStubs() bool
-}
-
-var _ javaSdkLibraryEnforceContext = (*Module)(nil)
-
-func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) {
-	if dep.allowListedInterPartitionJavaLibrary(ctx) {
-		return
-	}
-
-	if dep.syspropWithPublicStubs() {
-		return
-	}
-
-	// If product interface is not enforced, skip check between system and product partition.
-	// But still need to check between product and vendor partition because product interface flag
-	// just represents enforcement between product and system, and vendor interface enforcement
-	// that is enforced here by precondition is representing enforcement between vendor and other partitions.
-	if !ctx.Config().EnforceProductPartitionInterface() {
-		productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem
-		systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct
-
-		if productToSystem || systemToProduct {
-			return
-		}
-	}
-
-	// If module and dependency library is inter-partition
-	if j.partitionGroup(ctx) != dep.partitionGroup(ctx) {
-		errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead"
-		ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx))
-	}
-}
diff --git a/java/sdk_library_internal.go b/java/sdk_library_internal.go
new file mode 100644
index 0000000..ca088cf
--- /dev/null
+++ b/java/sdk_library_internal.go
@@ -0,0 +1,1017 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"android/soong/etc"
+	"fmt"
+	"path"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// ---------------------------------------------------------------------------------------------
+// Naming scheme of the submodules generated by java_sdk_library and java_sdk_library_import
+// ---------------------------------------------------------------------------------------------
+
+const (
+	sdkXmlFileSuffix = ".xml"
+	implLibSuffix    = ".impl"
+)
+
+func implLibraryModuleName(sdkLibName string) string {
+	return sdkLibName + implLibSuffix
+}
+
+// Module name of the runtime implementation library
+func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
+	return implLibraryModuleName(c.module.RootLibraryName())
+}
+
+// Module name of the XML file for the lib
+func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
+	return c.module.RootLibraryName() + sdkXmlFileSuffix
+}
+
+// Name of the java_library module that compiles the stubs source.
+func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.stubsLibraryModuleName(baseName)
+}
+
+// Name of the java_library module that compiles the exportable stubs source.
+func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.exportableStubsLibraryModuleName(baseName)
+}
+
+// Name of the droidstubs module that generates the stubs source and may also
+// generate/check the API.
+func (c *commonToSdkLibraryAndImport) droidstubsModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.stubsSourceModuleName(baseName)
+}
+
+// Name of the java_api_library module that generates the from-text stubs source
+// and compiles to a jar file.
+func (c *commonToSdkLibraryAndImport) fromTextStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.apiLibraryModuleName(baseName)
+}
+
+// Name of the java_library module that compiles the stubs
+// generated from source Java files.
+func (c *commonToSdkLibraryAndImport) fromSourceStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.sourceStubsLibraryModuleName(baseName)
+}
+
+// Name of the java_library module that compiles the exportable stubs
+// generated from source Java files.
+func (c *commonToSdkLibraryAndImport) exportableFromSourceStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.exportableSourceStubsLibraryModuleName(baseName)
+}
+
+// ---------------------------------------------------------------------------------------------
+// Build rules of the submodules generated by java_sdk_library.
+// java_sdk_library "framework-foo" generates the following submodules:
+//
+// - "framework-foo.impl" (type: [Library]): the implementation library, which generates the
+//		compilation outputs that include the implementation details and the private apis
+//		(i.e. class/methods that are annotated @hide).
+//
+// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [Droidstubs]): droidstubs module that
+//		generates the stubs and the api files for the given api scope.
+//
+// - "framework-foo.stubs.<[apiScope.name]>" (type: [Library]): stub library module that
+//		provides the compilation output of the stubs to the reverse dependencies. The module
+//		itself does not perform any compilation actions; the module statically depends on one of
+//		the from-source stub module or the from-text stub configuration based on the build
+// 		configuration.
+//
+// - "framework-foo.stubs.<[apiScope.name]>.from-source" (type: [Library]): stub library module
+//		that compiles the stubs generated by the droidstubs submodule. This module is a static
+//		dependency of the stub library module when
+//		[android/soong/android/config.BuildFromTextStub()] is false.
+//
+// - "framework-foo.stubs.<[apiScope.name]>.from-text" (type: [ApiLibrary]): api library module
+//		that generates and compiles the stubs from the api files checked in the tree instead of
+//		the source Java files (e.g. *-current.txt files). This module is a static dependency of
+//		the stub library module when [android/soong/android/config.BuildFromTextStub()] is true.
+//
+// - "framework-foo.stubs.exportable.<[apiScope.name]>" (type: [Library]): stub library module
+//		that provides the "exportable" stubs. "exportable" stubs are the stubs that do not
+//		include in-development flagged apis. This module is only used for SDK builds to generate
+//		the SDK artifacts, and not purposed for consumption for other modules.
+//
+// - "framework-foo.stubs.exportable.<[apiScope.name]>.from-source" (type: [Library]): stub
+//		library module that compiles the "exportable" stubs generated by the droidstubs
+//		submodule. This module is always a static dependency of the "exportable" stub library
+//		module given that from-text stubs cannot be used for SDK builds as it does not contain
+//		documentations.
+//
+// - "framework-foo.xml" (type: [sdkLibraryXml]): xml library that generates the permission xml
+//		file, which allows [SdkLibrary] to be used with <uses-permission> tag in the
+//		AndroidManifest.xml files.
+// ---------------------------------------------------------------------------------------------
+
+// Creates the implementation [Library] with ".impl" suffix.
+func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
+	visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
+
+	staticLibs := module.properties.Static_libs.Clone()
+	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]
+		Apex_available []string
+		Stem           *string
+	}{
+		Name:       proptools.StringPtr(module.implLibraryModuleName()),
+		Enabled:    module.EnabledProperty(),
+		Visibility: visibility,
+
+		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
+
+		Static_libs: staticLibs,
+		// Pass the apex_available settings down so that the impl library can be statically
+		// embedded within a library that is added to an APEX. Needed for updatable-media.
+		Apex_available: module.ApexAvailable(),
+
+		Stem: proptools.StringPtr(module.Name()),
+	}
+
+	properties := []interface{}{
+		&module.properties,
+		&module.protoProperties,
+		&module.deviceProperties,
+		&module.dexProperties,
+		&module.dexpreoptProperties,
+		&module.linter.properties,
+		&module.overridableProperties,
+		&props,
+		module.sdkComponentPropertiesForChildLibrary(),
+	}
+	mctx.CreateModule(LibraryFactory, properties...)
+}
+
+// Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs
+// source files from the given full source files and also updates and checks the API
+// specification files (i.e. "*-current.txt", "*-removed.txt" files).
+func (module *SdkLibrary) createDroidstubs(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
+	props := struct {
+		Name                             *string
+		Enabled                          proptools.Configurable[bool]
+		Visibility                       []string
+		Srcs                             []string
+		Installable                      *bool
+		Sdk_version                      *string
+		Api_surface                      *string
+		System_modules                   *string
+		Libs                             proptools.Configurable[[]string]
+		Output_javadoc_comments          *bool
+		Arg_files                        []string
+		Args                             *string
+		Java_version                     *string
+		Annotations_enabled              *bool
+		Merge_annotations_dirs           []string
+		Merge_inclusion_annotations_dirs []string
+		Generate_stubs                   *bool
+		Previous_api                     *string
+		Aconfig_declarations             []string
+		Check_api                        struct {
+			Current       ApiToCheck
+			Last_released ApiToCheck
+
+			Api_lint struct {
+				Enabled       *bool
+				New_since     *string
+				Baseline_file *string
+			}
+		}
+		Aidl struct {
+			Include_dirs       []string
+			Local_include_dirs []string
+		}
+		Dists []android.Dist
+	}{}
+
+	// The stubs source processing uses the same compile time classpath when extracting the
+	// API from the implementation library as it does when compiling it. i.e. the same
+	// * sdk version
+	// * system_modules
+	// * 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...)
+	props.Sdk_version = module.deviceProperties.Sdk_version
+	props.Api_surface = &apiScope.name
+	props.System_modules = module.deviceProperties.System_modules
+	props.Installable = proptools.BoolPtr(false)
+	// A droiddoc module has only one Libs property and doesn't distinguish between
+	// shared libs and static libs. So we need to add both of these libs to Libs property.
+	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
+	props.Libs.AppendSimpleValue(module.properties.Libs)
+	props.Libs.Append(module.properties.Static_libs)
+	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
+	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
+	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
+	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
+	props.Java_version = module.properties.Java_version
+
+	props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
+	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
+	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
+	props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
+
+	droidstubsArgs := []string{}
+	if len(module.sdkLibraryProperties.Api_packages) != 0 {
+		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
+	}
+	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
+	disabledWarnings := []string{"HiddenSuperclass"}
+	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
+		disabledWarnings = append(disabledWarnings,
+			"BroadcastBehavior",
+			"DeprecationMismatch",
+			"MissingPermission",
+			"SdkConstant",
+			"Todo",
+		)
+	}
+	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
+
+	// Output Javadoc comments for public scope.
+	if apiScope == apiScopePublic {
+		props.Output_javadoc_comments = proptools.BoolPtr(true)
+	}
+
+	// Add in scope specific arguments.
+	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
+	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
+	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
+
+	// List of APIs identified from the provided source files are created. They are later
+	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
+	// last-released (a.k.a numbered) list of API.
+	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
+	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
+	apiDir := module.getApiDir()
+	currentApiFileName = path.Join(apiDir, currentApiFileName)
+	removedApiFileName = path.Join(apiDir, removedApiFileName)
+
+	// check against the not-yet-release API
+	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+
+	if module.compareAgainstLatestApi(apiScope) {
+		// check against the latest released API
+		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+		props.Previous_api = latestApiFilegroupName
+		props.Check_api.Last_released.Api_file = latestApiFilegroupName
+		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+			module.latestRemovedApiFilegroupName(apiScope))
+		props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
+			module.latestIncompatibilitiesFilegroupName(apiScope))
+
+		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
+			// Enable api lint.
+			props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
+			props.Check_api.Api_lint.New_since = latestApiFilegroupName
+
+			// If it exists then pass a lint-baseline.txt through to droidstubs.
+			baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
+			baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
+			paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
+			if err != nil {
+				mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
+			}
+			if len(paths) == 1 {
+				props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
+			} else if len(paths) != 0 {
+				mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
+			}
+		}
+	}
+
+	if !Bool(module.sdkLibraryProperties.No_dist) {
+		// Dist the api txt and removed api txt artifacts for sdk builds.
+		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+		stubsTypeTagPrefix := ""
+		if mctx.Config().ReleaseHiddenApiExportableStubs() {
+			stubsTypeTagPrefix = ".exportable"
+		}
+		for _, p := range []struct {
+			tag     string
+			pattern string
+		}{
+			// "exportable" api files are copied to the dist directory instead of the
+			// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
+			// is set. Otherwise, the "everything" api files are copied to the dist directory.
+			{tag: "%s.api.txt", pattern: "%s.txt"},
+			{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
+		} {
+			props.Dists = append(props.Dists, android.Dist{
+				Targets: []string{"sdk", "win_sdk"},
+				Dir:     distDir,
+				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
+				Tag:     proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
+			})
+		}
+	}
+
+	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
+}
+
+type libraryProperties struct {
+	Name           *string
+	Enabled        proptools.Configurable[bool]
+	Visibility     []string
+	Srcs           []string
+	Installable    *bool
+	Sdk_version    *string
+	System_modules *string
+	Patch_module   *string
+	Libs           []string
+	Static_libs    []string
+	Compile_dex    *bool
+	Java_version   *string
+	Openjdk9       struct {
+		Srcs       []string
+		Javacflags []string
+	}
+	Dist struct {
+		Targets []string
+		Dest    *string
+		Dir     *string
+		Tag     *string
+	}
+	Is_stubs_module       *bool
+	Stub_contributing_api *string
+}
+
+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)
+	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.deviceProperties.System_modules
+	props.Patch_module = module.properties.Patch_module
+	props.Installable = proptools.BoolPtr(false)
+	props.Libs = module.sdkLibraryProperties.Stub_only_libs
+	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+	// The stub-annotations library contains special versions of the annotations
+	// with CLASS retention policy, so that they're kept.
+	if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
+		props.Libs = append(props.Libs, "stub-annotations")
+	}
+	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
+	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
+	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
+	// interop with older developer tools that don't support 1.9.
+	props.Java_version = proptools.StringPtr("1.8")
+	props.Is_stubs_module = proptools.BoolPtr(true)
+	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
+
+	return props
+}
+
+// Creates the from-source stub [Library] with ".stubs.<[apiScope.name]>.from-source" suffix.
+func (module *SdkLibrary) createFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	props := module.stubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.fromSourceStubsLibraryModuleName(apiScope))
+	props.Srcs = []string{":" + module.droidstubsModuleName(apiScope)}
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the "exportable" from-source stub [Library] with
+// ".stubs.exportable.<[apiScope.name]>" suffix.
+func (module *SdkLibrary) createExportableFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := module.stubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.exportableFromSourceStubsLibraryModuleName(apiScope))
+	props.Srcs = []string{":" + module.droidstubsModuleName(apiScope) + "{.exportable}"}
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the from-text stub [ApiLibrary] with ".stubs.<[apiScope.name]>.from-text" suffix.
+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]
+		Static_libs       []string
+		System_modules    *string
+		Enable_validation *bool
+		Stubs_type        *string
+		Sdk_version       *string
+		Previous_api      *string
+	}{}
+
+	props.Name = proptools.StringPtr(module.fromTextStubsLibraryModuleName(apiScope))
+	props.Enabled = module.EnabledProperty()
+	props.Visibility = []string{"//visibility:override", "//visibility:private"}
+
+	apiContributions := []string{}
+
+	// Api surfaces are not independent of each other, but have subset relationships,
+	// and so does the api files. To generate from-text stubs for api surfaces other than public,
+	// all subset api domains' api_contriubtions must be added as well.
+	scope := apiScope
+	for scope != nil {
+		apiContributions = append(apiContributions, module.droidstubsModuleName(scope)+".api.contribution")
+		scope = scope.extends
+	}
+	if apiScope == apiScopePublic {
+		additionalApiContribution := module.apiLibraryAdditionalApiContribution()
+		if additionalApiContribution != "" {
+			apiContributions = append(apiContributions, additionalApiContribution)
+		}
+	}
+
+	props.Api_contributions = apiContributions
+
+	// Ensure that stub-annotations is added to the classpath before any other libs
+	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
+	props.Libs.AppendSimpleValue([]string{"stub-annotations"})
+	props.Libs.AppendSimpleValue(module.properties.Libs)
+	props.Libs.Append(module.properties.Static_libs)
+	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
+	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+
+	props.System_modules = module.deviceProperties.System_modules
+	props.Enable_validation = proptools.BoolPtr(true)
+	props.Stubs_type = proptools.StringPtr("everything")
+
+	if module.deviceProperties.Sdk_version != nil {
+		props.Sdk_version = module.deviceProperties.Sdk_version
+	}
+
+	if module.compareAgainstLatestApi(apiScope) {
+		// check against the latest released API
+		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+		props.Previous_api = latestApiFilegroupName
+	}
+
+	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+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)
+
+	props.System_modules = module.deviceProperties.System_modules
+
+	// The imports need to be compiled to dex if the java_sdk_library requests it.
+	compileDex := module.dexProperties.Compile_dex
+	if module.stubLibrariesCompiledForDex() {
+		compileDex = proptools.BoolPtr(true)
+	}
+	props.Compile_dex = compileDex
+
+	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
+
+	if !Bool(module.sdkLibraryProperties.No_dist) && doDist {
+		props.Dist.Targets = []string{"sdk", "win_sdk"}
+		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
+		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
+		props.Dist.Tag = proptools.StringPtr(".jar")
+	}
+	props.Is_stubs_module = proptools.BoolPtr(true)
+
+	return props
+}
+
+// Creates the stub [Library] with ".stubs.<[apiScope.name]>" suffix.
+func (module *SdkLibrary) createTopLevelStubsLibrary(
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
+	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
+	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+
+	// Add the stub compiling java_library/java_api_library as static lib based on build config
+	staticLib := module.fromSourceStubsLibraryModuleName(apiScope)
+	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
+		staticLib = module.fromTextStubsLibraryModuleName(apiScope)
+	}
+	props.Static_libs = append(props.Static_libs, staticLib)
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the "exportable" stub [Library] with ".stubs.exportable.<[apiScope.name]>" suffix.
+func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
+	doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
+	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
+	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
+
+	staticLib := module.exportableFromSourceStubsLibraryModuleName(apiScope)
+	props.Static_libs = append(props.Static_libs, staticLib)
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the [sdkLibraryXml] with ".xml" suffix.
+func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
+	moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
+	var moduleMinApiLevelStr = moduleMinApiLevel.String()
+	if moduleMinApiLevel == android.NoneApiLevel {
+		moduleMinApiLevelStr = "current"
+	}
+	props := struct {
+		Name                      *string
+		Enabled                   proptools.Configurable[bool]
+		Lib_name                  *string
+		Apex_available            []string
+		On_bootclasspath_since    *string
+		On_bootclasspath_before   *string
+		Min_device_sdk            *string
+		Max_device_sdk            *string
+		Sdk_library_min_api_level *string
+		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,
+		On_bootclasspath_before:   module.commonSdkLibraryProperties.On_bootclasspath_before,
+		Min_device_sdk:            module.commonSdkLibraryProperties.Min_device_sdk,
+		Max_device_sdk:            module.commonSdkLibraryProperties.Max_device_sdk,
+		Sdk_library_min_api_level: &moduleMinApiLevelStr,
+		Uses_libs_dependencies:    module.usesLibraryProperties.Uses_libs,
+	}
+
+	mctx.CreateModule(sdkLibraryXmlFactory, &props)
+}
+
+// ---------------------------------------------------------------------------------------------
+// Build rules of the submodules generated by java_sdk_library_import.
+// Note that the java_sdk_library_import module does not generate the implementation library.
+// Instead, it will create a dependency to the source implemenetation library if one exists.
+// java_sdk_library_import "framework-foo" generates the following submodules:
+//
+// - "framework-foo.stubs.<[apiScope.name]>" (type: [Import]): prebuilt stub library module that
+//		provides the stub jar file checked in the tree.
+//
+// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [PrebuiltStubsSources]): prebuilt
+//		droidstubs module that provides the stub source jar file checked in the tree.
+//
+// - "framework-foo.stubs.source.<[apiScope.name]>.api.contribution"
+//		(type [JavaApiContributionImport]): prebuilt java_api_contribution module that provides
+//		the prebuilt api file for previously released from-text stub generation.
+// ---------------------------------------------------------------------------------------------
+
+// Creates the prebuilt stub [Import] with ".stubs.<[apiScope.name]>" suffix.
+func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	// Creates a java import for the jar with ".stubs" suffix
+	props := struct {
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Sdk_version                      *string
+		Libs                             []string
+		Jars                             []string
+		Compile_dex                      *bool
+		Is_stubs_module                  *bool
+
+		android.UserSuppliedPrebuiltProperties
+	}{}
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
+	props.Sdk_version = scopeProperties.Sdk_version
+	// Prepend any of the libs from the legacy public properties to the libs for each of the
+	// scopes to avoid having to duplicate them in each scope.
+	props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
+	props.Jars = scopeProperties.Jars
+
+	// The imports are preferred if the java_sdk_library_import is preferred.
+	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
+
+	// The imports need to be compiled to dex if the java_sdk_library_import requests it.
+	compileDex := module.properties.Compile_dex
+	if module.stubLibrariesCompiledForDex() {
+		compileDex = proptools.BoolPtr(true)
+	}
+	props.Compile_dex = compileDex
+	props.Is_stubs_module = proptools.BoolPtr(true)
+
+	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	props := struct {
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Srcs                             []string
+
+		android.UserSuppliedPrebuiltProperties
+	}{}
+	props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope))
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
+	props.Srcs = scopeProperties.Stub_srcs
+
+	// The stubs source is preferred if the java_sdk_library_import is preferred.
+	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
+
+	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the prebuilt api contribution [JavaApiContributionImport] with
+// ".stubs.source.<[apiScope.name]>.api.contribution" suffix.
+func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	api_file := scopeProperties.Current_api
+	api_surface := &apiScope.name
+
+	props := struct {
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Api_surface                      *string
+		Api_file                         *string
+		Visibility                       []string
+	}{}
+
+	props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope) + ".api.contribution")
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
+	props.Api_surface = api_surface
+	props.Api_file = api_file
+	props.Visibility = []string{"//visibility:override", "//visibility:public"}
+
+	mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// ---------------------------------------------------------------------------------------------
+// End of the build rules of the submodules generated by java_sdk_library_import.
+// ---------------------------------------------------------------------------------------------
+
+// Definition of the [sdkLibraryXml] module. The module generates the permissions xml file,
+// so that the apps can specify the java_sdk_library using <uses-permission> tag in the
+// AndroidManifest.xml file.
+type sdkLibraryXml struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	android.ApexModuleBase
+
+	properties sdkLibraryXmlProperties
+
+	outputFilePath android.OutputPath
+	installDirPath android.InstallPath
+
+	hideApexVariantFromMake bool
+}
+
+type sdkLibraryXmlProperties struct {
+	// canonical name of the lib
+	Lib_name *string
+
+	// Signals that this shared library is part of the bootclasspath starting
+	// on the version indicated in this attribute.
+	//
+	// This will make platforms at this level and above to ignore
+	// <uses-library> tags with this library name because the library is already
+	// available
+	On_bootclasspath_since *string
+
+	// Signals that this shared library was part of the bootclasspath before
+	// (but not including) the version indicated in this attribute.
+	//
+	// The system will automatically add a <uses-library> tag with this library to
+	// apps that target any SDK less than the version indicated in this attribute.
+	On_bootclasspath_before *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is below the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Min_device_sdk *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is above the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Max_device_sdk *string
+
+	// The SdkLibrary's min api level as a string
+	//
+	// This value comes from the ApiLevel of the MinSdkVersion property.
+	Sdk_library_min_api_level *string
+
+	// Uses-libs dependencies that the shared library requires to work correctly.
+	//
+	// This will add dependency="foo:bar" to the <library> section.
+	Uses_libs_dependencies []string
+}
+
+// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
+// Not to be used directly by users. java_sdk_library internally uses this.
+func sdkLibraryXmlFactory() android.Module {
+	module := &sdkLibraryXml{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitApexModule(module)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+	return module
+}
+
+func (module *sdkLibraryXml) UniqueApexVariations() bool {
+	// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
+	// mounted APEX, which contains the name of the APEX.
+	return true
+}
+
+// from android.PrebuiltEtcModule
+func (module *sdkLibraryXml) BaseDir() string {
+	return "etc"
+}
+
+// from android.PrebuiltEtcModule
+func (module *sdkLibraryXml) SubDir() string {
+	return "permissions"
+}
+
+var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
+
+// from android.ApexModule
+func (module *sdkLibraryXml) AvailableFor(what string) bool {
+	return true
+}
+
+func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// do nothing
+}
+
+var _ android.ApexModule = (*sdkLibraryXml)(nil)
+
+// Implements android.ApexModule
+func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
+	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
+	return nil
+}
+
+// File path to the runtime implementation library
+func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
+	implName := proptools.String(module.properties.Lib_name)
+	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
+		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
+		// In most cases, this works fine. But when apex_name is set or override_apex is used
+		// this can be wrong.
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
+	}
+	partition := "system"
+	if module.SocSpecific() {
+		partition = "vendor"
+	} else if module.DeviceSpecific() {
+		partition = "odm"
+	} else if module.ProductSpecific() {
+		partition = "product"
+	} else if module.SystemExtSpecific() {
+		partition = "system_ext"
+	}
+	return "/" + partition + "/framework/" + implName + ".jar"
+}
+
+func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
+	if value == nil {
+		return ""
+	}
+	apiLevel, err := android.ApiLevelFromUser(ctx, *value)
+	if err != nil {
+		// attributes in bp files have underscores but in the xml have dashes.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
+		return ""
+	}
+	if apiLevel.IsCurrent() {
+		// passing "current" would always mean a future release, never the current (or the current in
+		// progress) which means some conditions would never be triggered.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+			`"current" is not an allowed value for this attribute`)
+		return ""
+	}
+	// "safeValue" is safe because it translates finalized codenames to a string
+	// with their SDK int.
+	safeValue := apiLevel.String()
+	return formattedOptionalAttribute(attrName, &safeValue)
+}
+
+// formats an attribute for the xml permissions file if the value is not null
+// returns empty string otherwise
+func formattedOptionalAttribute(attrName string, value *string) string {
+	if value == nil {
+		return ""
+	}
+	return fmt.Sprintf("        %s=\"%s\"\n", attrName, *value)
+}
+
+func formattedDependenciesAttribute(dependencies []string) string {
+	if dependencies == nil {
+		return ""
+	}
+	return fmt.Sprintf("        dependency=\"%s\"\n", strings.Join(dependencies, ":"))
+}
+
+func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
+	libName := proptools.String(module.properties.Lib_name)
+	libNameAttr := formattedOptionalAttribute("name", &libName)
+	filePath := module.implPath(ctx)
+	filePathAttr := formattedOptionalAttribute("file", &filePath)
+	implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
+	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
+	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
+	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
+	dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies)
+	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
+	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
+	var libraryTag string
+	if module.properties.Min_device_sdk != nil {
+		libraryTag = "    <apex-library\n"
+	} else {
+		libraryTag = "    <library\n"
+	}
+
+	return strings.Join([]string{
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
+		"<!-- Copyright (C) 2018 The Android Open Source Project\n",
+		"\n",
+		"    Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+		"    you may not use this file except in compliance with the License.\n",
+		"    You may obtain a copy of the License at\n",
+		"\n",
+		"        http://www.apache.org/licenses/LICENSE-2.0\n",
+		"\n",
+		"    Unless required by applicable law or agreed to in writing, software\n",
+		"    distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+		"    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+		"    See the License for the specific language governing permissions and\n",
+		"    limitations under the License.\n",
+		"-->\n",
+		"<permissions>\n",
+		libraryTag,
+		libNameAttr,
+		filePathAttr,
+		implicitFromAttr,
+		implicitUntilAttr,
+		minSdkAttr,
+		maxSdkAttr,
+		dependenciesAttr,
+		"    />\n",
+		"</permissions>\n",
+	}, "")
+}
+
+func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
+
+	libName := proptools.String(module.properties.Lib_name)
+	module.selfValidate(ctx)
+	xmlContent := module.permissionsContents(ctx)
+
+	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
+	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
+
+	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
+	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
+
+	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
+}
+
+func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
+	if module.hideApexVariantFromMake {
+		return []android.AndroidMkEntries{{
+			Disabled: true,
+		}}
+	}
+
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(module.outputFilePath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_TAGS", "optional")
+				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
+			},
+		},
+	}}
+}
+
+func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
+	module.validateAtLeastTAttributes(ctx)
+	module.validateMinAndMaxDeviceSdk(ctx)
+	module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
+	module.validateOnBootclasspathBeforeRequirements(ctx)
+}
+
+func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
+	t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+	module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
+	module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
+	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
+	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
+}
+
+func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
+	if attr != nil {
+		if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
+			// we will inform the user of invalid inputs when we try to write the
+			// permissions xml file so we don't need to do it here
+			if t.GreaterThan(level) {
+				ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
+	if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
+		min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+		max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+		if minErr == nil && maxErr == nil {
+			// we will inform the user of invalid inputs when we try to write the
+			// permissions xml file so we don't need to do it here
+			if min.GreaterThan(max) {
+				ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
+	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+	if module.properties.Min_device_sdk != nil {
+		api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+		if err == nil {
+			if moduleMinApi.GreaterThan(api) {
+				ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+			}
+		}
+	}
+	if module.properties.Max_device_sdk != nil {
+		api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+		if err == nil {
+			if moduleMinApi.GreaterThan(api) {
+				ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
+	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+	if module.properties.On_bootclasspath_before != nil {
+		t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+		// if we use the attribute, then we need to do this validation
+		if moduleMinApi.LessThan(t) {
+			// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
+			if module.properties.Min_device_sdk == nil {
+				ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
+			}
+		}
+	}
+}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index bb63315..6031d72 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -22,8 +22,6 @@
 	"testing"
 
 	"android/soong/android"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func TestJavaSdkLibrary(t *testing.T) {
@@ -55,7 +53,7 @@
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
-			libs: ["foo", "bar.stubs"],
+			libs: ["foo.stubs.system", "bar.stubs"],
 			sdk_version: "system_current",
 		}
 		java_sdk_library {
@@ -92,25 +90,25 @@
 		java_library {
 		    name: "qux",
 		    srcs: ["c.java"],
-		    libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"],
+		    libs: ["baz", "fred.stubs", "quuz.stubs", "wilma.stubs", "barney.stubs.system", "betty.stubs.system"],
 		    sdk_version: "system_current",
 		}
 		java_library {
 			name: "baz-test",
 			srcs: ["c.java"],
-			libs: ["foo"],
+			libs: ["foo.stubs.test"],
 			sdk_version: "test_current",
 		}
 		java_library {
 			name: "baz-29",
 			srcs: ["c.java"],
-			libs: ["foo"],
+			libs: ["sdk_system_29_foo"],
 			sdk_version: "system_29",
 		}
 		java_library {
 			name: "baz-module-30",
 			srcs: ["c.java"],
-			libs: ["foo"],
+			libs: ["sdk_module-lib_30_foo"],
 			sdk_version: "module_30",
 		}
 	`)
@@ -162,11 +160,11 @@
 
 	baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
 	// tests if baz-29 is actually linked to the system 29 stubs lib
-	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar")
+	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/combined/sdk_system_29_foo.jar")
 
 	bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
 	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
-	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar")
+	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/combined/sdk_module-lib_30_foo.jar")
 
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := result.ModuleForTests("qux", "android_common")
@@ -422,7 +420,7 @@
 	for _, expectation := range expectations {
 		verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
 
-		stubName := apiScopePublic.sourceStubLibraryModuleName("sdklib")
+		stubName := apiScopePublic.sourceStubsLibraryModuleName("sdklib")
 		verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined)
 	}
 }
@@ -445,7 +443,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["b.java"],
-			libs: ["foo"],
+			libs: ["foo.stubs"],
 		}
 		`)
 
@@ -763,7 +761,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo-public", "foo-system", "foo-module-lib", "foo-system-server"],
+			libs: ["foo-public.stubs", "foo-system.stubs.system", "foo-module-lib.stubs.module_lib", "foo-system-server.stubs.system_server"],
 			sdk_version: "system_server_current",
 		}
 		`)
@@ -789,102 +787,26 @@
 	}
 }
 
-func TestJavaSdkLibrary_MissingScope(t *testing.T) {
-	prepareForJavaTest.
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
-		RunTestWithBp(t, `
-			java_sdk_library {
-				name: "foo",
-				srcs: ["a.java"],
-				public: {
-					enabled: false,
-				},
-			}
-
-			java_library {
-				name: "baz",
-				srcs: ["a.java"],
-				libs: ["foo"],
-				sdk_version: "module_current",
-			}
-		`)
-}
-
-func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			system: {
-				enabled: true,
-			},
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["a.java"],
-			libs: ["foo"],
-			// foo does not have module-lib scope so it should fallback to system
-			sdk_version: "module_current",
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			system: {
-				enabled: true,
-			},
-			default_to_stubs: true,
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["a.java"],
-			libs: ["foo"],
-			// does not have sdk_version set, should fallback to module,
-			// which will then fallback to system because the module scope
-			// is not enabled.
-		}
-		`)
-	// The baz library should depend on the system stubs jar.
-	bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
-		t.Errorf("expected %q, found %#q", expected, actual)
-	}
-}
-
 func TestJavaSdkLibraryImport(t *testing.T) {
 	result := prepareForJavaTest.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs"],
 			sdk_version: "current",
 		}
 
 		java_library {
 			name: "foo.system",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs.system"],
 			sdk_version: "system_current",
 		}
 
 		java_library {
 			name: "foo.test",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs.test"],
 			sdk_version: "test_current",
 		}
 
@@ -1017,7 +939,7 @@
 		java_library {
 			name: "public",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs"],
 			sdk_version: "current",
 		}
 		`)
@@ -1190,155 +1112,6 @@
 	}
 }
 
-func TestJavaSdkLibraryEnforce(t *testing.T) {
-	partitionToBpOption := func(partition string) string {
-		switch partition {
-		case "system":
-			return ""
-		case "vendor":
-			return "soc_specific: true,"
-		case "product":
-			return "product_specific: true,"
-		default:
-			panic("Invalid partition group name: " + partition)
-		}
-	}
-
-	type testConfigInfo struct {
-		libraryType                string
-		fromPartition              string
-		toPartition                string
-		enforceProductInterface    bool
-		enforceJavaSdkLibraryCheck bool
-		allowList                  []string
-	}
-
-	createPreparer := func(info testConfigInfo) android.FixturePreparer {
-		bpFileTemplate := `
-			java_library {
-				name: "foo",
-				srcs: ["foo.java"],
-				libs: ["bar"],
-				sdk_version: "current",
-				%s
-			}
-
-			%s {
-				name: "bar",
-				srcs: ["bar.java"],
-				sdk_version: "current",
-				%s
-			}
-		`
-
-		bpFile := fmt.Sprintf(bpFileTemplate,
-			partitionToBpOption(info.fromPartition),
-			info.libraryType,
-			partitionToBpOption(info.toPartition))
-
-		return android.GroupFixturePreparers(
-			PrepareForTestWithJavaSdkLibraryFiles,
-			FixtureWithLastReleaseApis("bar"),
-			android.FixtureWithRootAndroidBp(bpFile),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
-				variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
-				variables.InterPartitionJavaLibraryAllowList = info.allowList
-			}),
-		)
-	}
-
-	runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
-		t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
-			errorHandler := android.FixtureExpectsNoErrors
-			if expectedErrorPattern != "" {
-				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
-			}
-			android.GroupFixturePreparers(
-				prepareForJavaTest,
-				createPreparer(info),
-			).
-				ExtendWithErrorHandler(errorHandler).
-				RunTest(t)
-		})
-	}
-
-	errorMessage := "is not allowed across the partitions"
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: false,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    false,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-		allowList:                  []string{"bar"},
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "product",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "vendor",
-		toPartition:                "product",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-}
-
 func TestJavaSdkLibraryDist(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
@@ -1657,7 +1430,7 @@
 			name: "bar",
 			srcs: ["c.java", "b.java"],
 			libs: [
-				"foo",
+				"foo.stubs",
 			],
 			uses_libs: [
 				"foo",
@@ -1752,7 +1525,7 @@
 			name: "mymodule",
 			srcs: ["a.java"],
 			sdk_version: "current",
-			libs: ["sdklib",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt)
+			libs: ["sdklib.stubs",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt)
 		}
 	`
 
@@ -1893,3 +1666,111 @@
 		}
 		`)
 }
+
+func TestSdkLibDirectDependency(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo", "bar"),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
+		`module "baz" variant "android_common": cannot depend directly on java_sdk_library ` +
+			`"foo"; try depending on "foo.stubs", or "foo.impl" instead`,
+		`module "baz" variant "android_common": cannot depend directly on java_sdk_library ` +
+			`"prebuilt_bar"; try depending on "bar.stubs", or "bar.impl" instead`,
+	}),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "bar",
+			prefer: true,
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["b.java"],
+			libs: [
+				"foo",
+				"bar",
+			],
+		}
+	`)
+}
+
+func TestSdkLibDirectDependencyWithPrebuiltSdk(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_sdk_version = intPtr(34)
+			variables.Platform_sdk_codename = stringPtr("VanillaIceCream")
+			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+			variables.Platform_systemsdk_versions = []string{"33", "34", "VanillaIceCream"}
+			variables.DeviceSystemSdkVersions = []string{"VanillaIceCream"}
+		}),
+		FixtureWithPrebuiltApis(map[string][]string{
+			"33": {"foo"},
+			"34": {"foo"},
+			"35": {"foo"},
+		}),
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`module "baz" variant "android_common": cannot depend directly on java_sdk_library "foo"; `+
+			`try depending on "sdk_public_33_foo", "sdk_system_33_foo", "sdk_test_33_foo", `+
+			`"sdk_module-lib_33_foo", or "sdk_system-server_33_foo" instead`),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["b.java"],
+			libs: [
+				"foo",
+			],
+			sdk_version: "system_33",
+		}
+	`)
+}
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 924abd4..aad1060 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -127,6 +127,26 @@
 	configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
 	classpathJars = append(classpathJars, standaloneClasspathJars...)
 	s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+	s.setPartitionInfoOfLibraries(ctx)
+}
+
+// Map of java library name to their install partition.
+type LibraryNameToPartitionInfo struct {
+	LibraryNameToPartition map[string]string
+}
+
+// LibraryNameToPartitionInfoProvider will be used by the top-level apex to enforce that dexpreopt files
+// of apex system server jars are installed in the same partition as the top-level apex.
+var LibraryNameToPartitionInfoProvider = blueprint.NewProvider[LibraryNameToPartitionInfo]()
+
+func (s *SystemServerClasspathModule) setPartitionInfoOfLibraries(ctx android.ModuleContext) {
+	libraryNameToPartition := map[string]string{}
+	ctx.VisitDirectDepsWithTag(systemServerClasspathFragmentContentDepTag, func(m android.Module) {
+		libraryNameToPartition[m.Name()] = m.PartitionTag(ctx.DeviceConfig())
+	})
+	android.SetProvider(ctx, LibraryNameToPartitionInfoProvider, LibraryNameToPartitionInfo{
+		LibraryNameToPartition: libraryNameToPartition,
+	})
 }
 
 func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
diff --git a/java/testing.go b/java/testing.go
index 0c79e9f..988514d 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -30,6 +30,7 @@
 )
 
 const defaultJavaDir = "default/java"
+const testDefaultUpdatableModuleVersion = "340090000"
 
 // Test fixture preparer that will register most java build components.
 //
@@ -61,6 +62,7 @@
 		// Needed for the global lint checks provided from frameworks/base
 		"prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar": nil,
 	}.AddToFixture(),
+	android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion),
 )
 
 var prepareForTestWithFrameworkDeps = android.GroupFixturePreparers(
@@ -388,7 +390,6 @@
 	RegisterStubsBuildComponents(ctx)
 	RegisterSystemModulesBuildComponents(ctx)
 	registerSystemserverClasspathBuildComponents(ctx)
-	registerLintBuildComponents(ctx)
 	android.RegisterApexContributionsBuildComponents(ctx)
 }
 
@@ -424,7 +425,9 @@
 		"kotlin-stdlib-jdk8",
 		"kotlin-annotations",
 		"stub-annotations",
+
 		"aconfig-annotations-lib",
+		"aconfig_storage_reader_java",
 		"unsupportedappusage",
 	}
 
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/multitree/Android.bp b/multitree/Android.bp
deleted file mode 100644
index 78c4962..0000000
--- a/multitree/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-multitree",
-    pkgPath: "android/soong/multitree",
-    deps: [
-        "blueprint",
-        "soong-android",
-    ],
-    srcs: [
-        "api_imports.go",
-        "api_surface.go",
-        "export.go",
-        "metadata.go",
-        "import.go",
-    ],
-    pluginFor: ["soong_build"],
-}
diff --git a/multitree/api_imports.go b/multitree/api_imports.go
deleted file mode 100644
index 51b9e07..0000000
--- a/multitree/api_imports.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2022 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 multitree
-
-import (
-	"android/soong/android"
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-var (
-	apiImportNameSuffix = ".apiimport"
-)
-
-func init() {
-	RegisterApiImportsModule(android.InitRegistrationContext)
-	android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
-}
-
-func RegisterApiImportsModule(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("api_imports", apiImportsFactory)
-}
-
-type ApiImports struct {
-	android.ModuleBase
-	properties apiImportsProperties
-}
-
-type apiImportsProperties struct {
-	Shared_libs      []string // List of C shared libraries from API surfaces
-	Header_libs      []string // List of C header libraries from API surfaces
-	Apex_shared_libs []string // List of C shared libraries with APEX stubs
-}
-
-// 'api_imports' is a module which describes modules available from API surfaces.
-// This module is required to get the list of all imported API modules, because
-// it is discouraged to loop and fetch all modules from its type information. The
-// only module with name 'api_imports' will be used from the build.
-func apiImportsFactory() android.Module {
-	module := &ApiImports{}
-	module.AddProperties(&module.properties)
-	android.InitAndroidModule(module)
-	return module
-}
-
-func (imports *ApiImports) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// ApiImport module does not generate any build actions
-}
-
-type ApiImportInfo struct {
-	SharedLibs, HeaderLibs, ApexSharedLibs map[string]string
-}
-
-var ApiImportsProvider = blueprint.NewMutatorProvider[ApiImportInfo]("deps")
-
-// Store module lists into ApiImportInfo and share it over mutator provider.
-func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) {
-	generateNameMapWithSuffix := func(names []string) map[string]string {
-		moduleNameMap := make(map[string]string)
-		for _, name := range names {
-			moduleNameMap[name] = name + apiImportNameSuffix
-		}
-
-		return moduleNameMap
-	}
-
-	sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs)
-	headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
-	apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs)
-
-	android.SetProvider(ctx, ApiImportsProvider, ApiImportInfo{
-		SharedLibs:     sharedLibs,
-		HeaderLibs:     headerLibs,
-		ApexSharedLibs: apexSharedLibs,
-	})
-}
-
-func GetApiImportSuffix() string {
-	return apiImportNameSuffix
-}
-
-func makeVarsProvider(ctx android.MakeVarsContext) {
-	ctx.VisitAllModules(func(m android.Module) {
-		if i, ok := m.(*ApiImports); ok {
-			ctx.Strict("API_IMPORTED_SHARED_LIBRARIES", strings.Join(i.properties.Shared_libs, " "))
-			ctx.Strict("API_IMPORTED_HEADER_LIBRARIES", strings.Join(i.properties.Header_libs, " "))
-		}
-	})
-}
diff --git a/multitree/api_surface.go b/multitree/api_surface.go
deleted file mode 100644
index 0f605d8..0000000
--- a/multitree/api_surface.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package multitree
-
-import (
-	"android/soong/android"
-	"github.com/google/blueprint"
-)
-
-var (
-	pctx = android.NewPackageContext("android/soong/multitree")
-)
-
-func init() {
-	RegisterApiSurfaceBuildComponents(android.InitRegistrationContext)
-}
-
-var PrepareForTestWithApiSurface = android.FixtureRegisterWithContext(RegisterApiSurfaceBuildComponents)
-
-func RegisterApiSurfaceBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("api_surface", ApiSurfaceFactory)
-}
-
-type ApiSurface struct {
-	android.ModuleBase
-	ExportableModuleBase
-	properties apiSurfaceProperties
-
-	taggedOutputs map[string]android.Paths
-}
-
-type apiSurfaceProperties struct {
-	Contributions []string
-}
-
-func ApiSurfaceFactory() android.Module {
-	module := &ApiSurface{}
-	module.AddProperties(&module.properties)
-	android.InitAndroidModule(module)
-	InitExportableModule(module)
-	return module
-}
-
-func (surface *ApiSurface) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if surface.properties.Contributions != nil {
-		ctx.AddVariationDependencies(nil, nil, surface.properties.Contributions...)
-	}
-
-}
-func (surface *ApiSurface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	contributionFiles := make(map[string]android.Paths)
-	var allOutputs android.Paths
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if contribution, ok := child.(ApiContribution); ok {
-			copied := contribution.CopyFilesWithTag(ctx)
-			for tag, files := range copied {
-				contributionFiles[child.Name()+"#"+tag] = files
-			}
-			for _, paths := range copied {
-				allOutputs = append(allOutputs, paths...)
-			}
-			return false // no transitive dependencies
-		}
-		return false
-	})
-
-	// phony target
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   blueprint.Phony,
-		Output: android.PathForPhony(ctx, ctx.ModuleName()),
-		Inputs: allOutputs,
-	})
-
-	surface.taggedOutputs = contributionFiles
-
-	ctx.SetOutputFiles(allOutputs, "")
-}
-
-func (surface *ApiSurface) TaggedOutputs() map[string]android.Paths {
-	return surface.taggedOutputs
-}
-
-func (surface *ApiSurface) Exportable() bool {
-	return true
-}
-
-var _ Exportable = (*ApiSurface)(nil)
-
-type ApiContribution interface {
-	// copy files necessaryt to construct an API surface
-	// For C, it will be map.txt and .h files
-	// For Java, it will be api.txt
-	CopyFilesWithTag(ctx android.ModuleContext) map[string]android.Paths // output paths
-
-	// Generate Android.bp in out/ to use the exported .txt files
-	// GenerateBuildFiles(ctx ModuleContext) Paths //output paths
-}
diff --git a/multitree/export.go b/multitree/export.go
deleted file mode 100644
index 8be8f70..0000000
--- a/multitree/export.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2022 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 multitree
-
-import (
-	"android/soong/android"
-
-	"github.com/google/blueprint/proptools"
-)
-
-type moduleExportProperty struct {
-	// True if the module is exported to the other components in a multi-tree.
-	// Any components in the multi-tree can import this module to use.
-	Export *bool
-}
-
-type ExportableModuleBase struct {
-	properties moduleExportProperty
-}
-
-type Exportable interface {
-	// Properties for the exporable module.
-	exportableModuleProps() *moduleExportProperty
-
-	// Check if this module can be exported.
-	// If this returns false, the module will not be exported regardless of the 'export' value.
-	Exportable() bool
-
-	// Returns 'true' if this module has 'export: true'
-	// This module will not be exported if it returns 'false' to 'Exportable()' interface even if
-	// it has 'export: true'.
-	IsExported() bool
-
-	// Map from tags to outputs.
-	// Each module can tag their outputs for convenience.
-	TaggedOutputs() map[string]android.Paths
-}
-
-type ExportableModule interface {
-	android.Module
-	Exportable
-}
-
-func InitExportableModule(module ExportableModule) {
-	module.AddProperties(module.exportableModuleProps())
-}
-
-func (m *ExportableModuleBase) exportableModuleProps() *moduleExportProperty {
-	return &m.properties
-}
-
-func (m *ExportableModuleBase) IsExported() bool {
-	return proptools.Bool(m.properties.Export)
-}
diff --git a/multitree/import.go b/multitree/import.go
deleted file mode 100644
index 1e5c421..0000000
--- a/multitree/import.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2022 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 multitree
-
-import (
-	"android/soong/android"
-)
-
-var (
-	nameSuffix = ".imported"
-)
-
-type MultitreeImportedModuleInterface interface {
-	GetMultitreeImportedModuleName() string
-}
-
-func init() {
-	android.RegisterModuleType("imported_filegroup", importedFileGroupFactory)
-
-	android.PreArchMutators(RegisterMultitreePreArchMutators)
-}
-
-type importedFileGroupProperties struct {
-	// Imported modules from the other components in a multi-tree
-	Imported []string
-}
-
-type importedFileGroup struct {
-	android.ModuleBase
-
-	properties importedFileGroupProperties
-	srcs       android.Paths
-}
-
-func (ifg *importedFileGroup) Name() string {
-	return ifg.BaseModuleName() + nameSuffix
-}
-
-func importedFileGroupFactory() android.Module {
-	module := &importedFileGroup{}
-	module.AddProperties(&module.properties)
-
-	android.InitAndroidModule(module)
-	return module
-}
-
-var _ MultitreeImportedModuleInterface = (*importedFileGroup)(nil)
-
-func (ifg *importedFileGroup) GetMultitreeImportedModuleName() string {
-	// The base module name of the imported filegroup is used as the imported module name
-	return ifg.BaseModuleName()
-}
-
-var _ android.SourceFileProducer = (*importedFileGroup)(nil)
-
-func (ifg *importedFileGroup) Srcs() android.Paths {
-	return ifg.srcs
-}
-
-func (ifg *importedFileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// srcs from this module must not be used. Adding a dot path to avoid the empty
-	// source failure. Still soong returns error when a module wants to build against
-	// this source, which is intended.
-	ifg.srcs = android.PathsForModuleSrc(ctx, []string{"."})
-}
-
-func RegisterMultitreePreArchMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("multitree_imported_rename", MultitreeImportedRenameMutator).Parallel()
-}
-
-func MultitreeImportedRenameMutator(ctx android.BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(MultitreeImportedModuleInterface); ok {
-		name := m.GetMultitreeImportedModuleName()
-		if !ctx.OtherModuleExists(name) {
-			// Provide an empty filegroup not to break the build while updating the metadata.
-			// In other cases, soong will report an error to guide users to run 'm update-meta'
-			// first.
-			if !ctx.Config().TargetMultitreeUpdateMeta() {
-				ctx.ModuleErrorf("\"%s\" filegroup must be imported.\nRun 'm update-meta' first to import the filegroup.", name)
-			}
-			ctx.Rename(name)
-		}
-	}
-}
diff --git a/multitree/metadata.go b/multitree/metadata.go
deleted file mode 100644
index 0eb0efc..0000000
--- a/multitree/metadata.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2022 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 multitree
-
-import (
-	"android/soong/android"
-	"encoding/json"
-)
-
-func init() {
-	android.RegisterParallelSingletonType("update-meta", UpdateMetaSingleton)
-}
-
-func UpdateMetaSingleton() android.Singleton {
-	return &updateMetaSingleton{}
-}
-
-type jsonImported struct {
-	FileGroups map[string][]string `json:",omitempty"`
-}
-
-type metadataJsonFlags struct {
-	Imported jsonImported        `json:",omitempty"`
-	Exported map[string][]string `json:",omitempty"`
-}
-
-type updateMetaSingleton struct {
-	importedModules       []string
-	generatedMetadataFile android.OutputPath
-}
-
-func (s *updateMetaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	metadata := metadataJsonFlags{
-		Imported: jsonImported{
-			FileGroups: make(map[string][]string),
-		},
-		Exported: make(map[string][]string),
-	}
-	ctx.VisitAllModules(func(module android.Module) {
-		if ifg, ok := module.(*importedFileGroup); ok {
-			metadata.Imported.FileGroups[ifg.BaseModuleName()] = ifg.properties.Imported
-		}
-		if e, ok := module.(ExportableModule); ok {
-			if e.IsExported() && e.Exportable() {
-				for tag, files := range e.TaggedOutputs() {
-					// TODO(b/219846705): refactor this to a dictionary
-					metadata.Exported[e.Name()+":"+tag] = append(metadata.Exported[e.Name()+":"+tag], files.Strings()...)
-				}
-			}
-		}
-	})
-	jsonStr, err := json.Marshal(metadata)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-	s.generatedMetadataFile = android.PathForOutput(ctx, "multitree", "metadata.json")
-	android.WriteFileRule(ctx, s.generatedMetadataFile, string(jsonStr))
-}
-
-func (s *updateMetaSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict("MULTITREE_METADATA", s.generatedMetadataFile.String())
-}
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/bindgen.go b/rust/bindgen.go
index 31aa137..abb5181 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -198,18 +198,20 @@
 		cflags = append(cflags, "-D__ANDROID_VNDK__")
 		if ctx.RustModule().InVendor() {
 			cflags = append(cflags, "-D__ANDROID_VENDOR__")
-
-			vendorApiLevel := ctx.Config().VendorApiLevel()
-			if vendorApiLevel == "" {
-				// TODO(b/314036847): This is a fallback for UDC targets.
-				// This must be a build failure when UDC is no longer built
-				// from this source tree.
-				vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
-			}
-			cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 		} else if ctx.RustModule().InProduct() {
 			cflags = append(cflags, "-D__ANDROID_PRODUCT__")
 		}
+
+		// Define __ANDROID_VENDOR_API__ for both product and vendor variants
+		// because they both use the same LLNDK libraries.
+		vendorApiLevel := ctx.Config().VendorApiLevel()
+		if vendorApiLevel == "" {
+			// TODO(b/314036847): This is a fallback for UDC targets.
+			// This must be a build failure when UDC is no longer built
+			// from this source tree.
+			vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+		}
+		cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 	}
 
 	if ctx.RustModule().InRecovery() {
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/compiler.go b/rust/compiler.go
index a2546a1..fd86917 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -39,13 +39,13 @@
 	initialize(ctx ModuleContext)
 	compilerFlags(ctx ModuleContext, flags Flags) Flags
 	cfgFlags(ctx ModuleContext, flags Flags) Flags
-	featureFlags(ctx ModuleContext, flags Flags) Flags
+	featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags
 	compilerProps() []interface{}
 	compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	crateName() string
 	edition() string
-	features() []string
+	features(ctx android.ConfigurableEvaluatorContext, module *Module) []string
 	rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
 	Thinlto() bool
 
@@ -154,7 +154,7 @@
 
 	// list of rust automatic crate dependencies.
 	// Rustlibs linkage is rlib for host targets and dylib for device targets.
-	Rustlibs []string `android:"arch_variant"`
+	Rustlibs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of rust proc_macro crate dependencies
 	Proc_macros []string `android:"arch_variant"`
@@ -194,7 +194,7 @@
 	Crate_name string `android:"arch_variant"`
 
 	// list of features to enable for this crate
-	Features []string `android:"arch_variant"`
+	Features proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of configuration options to enable for this crate. To enable features, use the "features" property.
 	Cfgs proptools.Configurable[[]string] `android:"arch_variant"`
@@ -346,22 +346,23 @@
 	return flags
 }
 
-func (compiler *baseCompiler) features() []string {
-	return compiler.Properties.Features
+func (compiler *baseCompiler) features(ctx android.ConfigurableEvaluatorContext, module *Module) []string {
+	eval := module.ConfigurableEvaluator(ctx)
+	return compiler.Properties.Features.GetOrDefault(eval, nil)
 }
 
-func (compiler *baseCompiler) featuresToFlags() []string {
+func (compiler *baseCompiler) featuresToFlags(ctx android.ConfigurableEvaluatorContext, module *Module) []string {
 	flags := []string{}
-	for _, feature := range compiler.features() {
+	for _, feature := range compiler.features(ctx, module) {
 		flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
 	}
 
 	return flags
 }
 
-func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...)
-	flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...)
+func (compiler *baseCompiler) featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags {
+	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(ctx, module)...)
+	flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags(ctx, module)...)
 
 	return flags
 }
@@ -496,7 +497,7 @@
 
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
-	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
+	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs.GetOrDefault(ctx, nil)...)
 	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
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/arm64_device.go b/rust/config/arm64_device.go
index 9850570..94a4457 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -35,8 +35,13 @@
 		},
 		"armv8-2a":         []string{},
 		"armv8-2a-dotprod": []string{},
+
+		// branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard
 		"armv9-a": []string{
-			// branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard
+			"-Z branch-protection=bti,pac-ret",
+			"-Z stack-protector=none",
+		},
+		"armv9-2a": []string{
 			"-Z branch-protection=bti,pac-ret",
 			"-Z stack-protector=none",
 		},
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/config/x86_64_device.go b/rust/config/x86_64_device.go
index fee1923..3c484d8 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -29,6 +29,7 @@
 
 	x86_64ArchVariantRustFlags = map[string][]string{
 		"":                            []string{},
+		"alderlake":                   []string{"-C target-cpu=alderlake"},
 		"broadwell":                   []string{"-C target-cpu=broadwell"},
 		"goldmont":                    []string{"-C target-cpu=goldmont"},
 		"goldmont-plus":               []string{"-C target-cpu=goldmont-plus"},
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index 5d9d88a..3c597cc 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -27,6 +27,7 @@
 
 	x86ArchVariantRustFlags = map[string][]string{
 		"":                            []string{},
+		"alderlake":                   []string{"-C target-cpu=alderlake"},
 		"atom":                        []string{"-C target-cpu=atom"},
 		"broadwell":                   []string{"-C target-cpu=broadwell"},
 		"goldmont":                    []string{"-C target-cpu=goldmont"},
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/rust/project_json.go b/rust/project_json.go
index 24dcc89..6c1e320 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -151,7 +151,7 @@
 		crate.Env["OUT_DIR"] = rModule.compiler.cargoOutDir().String()
 	}
 
-	for _, feature := range rModule.compiler.features() {
+	for _, feature := range rModule.compiler.features(ctx, rModule) {
 		crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
 	}
 
diff --git a/rust/rust.go b/rust/rust.go
index 240c221..a044a99 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -29,7 +29,6 @@
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
 	"android/soong/fuzz"
-	"android/soong/multitree"
 	"android/soong/rust/config"
 )
 
@@ -921,7 +920,7 @@
 	if mod.compiler != nil {
 		flags = mod.compiler.compilerFlags(ctx, flags)
 		flags = mod.compiler.cfgFlags(ctx, flags)
-		flags = mod.compiler.featureFlags(ctx, flags)
+		flags = mod.compiler.featureFlags(ctx, mod, flags)
 	}
 	if mod.coverage != nil {
 		flags, deps = mod.coverage.flags(ctx, flags, deps)
@@ -1218,47 +1217,6 @@
 
 	skipModuleList := map[string]bool{}
 
-	var apiImportInfo multitree.ApiImportInfo
-	hasApiImportInfo := false
-
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		if dep.Name() == "api_imports" {
-			apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider)
-			hasApiImportInfo = true
-		}
-	})
-
-	if hasApiImportInfo {
-		targetStubModuleList := map[string]string{}
-		targetOrigModuleList := map[string]string{}
-
-		// Search for dependency which both original module and API imported library with APEX stub exists
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
-				targetStubModuleList[apiLibrary] = depName
-			}
-		})
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if origLibrary, ok := targetStubModuleList[depName]; ok {
-				targetOrigModuleList[origLibrary] = depName
-			}
-		})
-
-		// Decide which library should be used between original and API imported library
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := targetOrigModuleList[depName]; ok {
-				if cc.ShouldUseStubForApex(ctx, dep) {
-					skipModuleList[depName] = true
-				} else {
-					skipModuleList[apiLibrary] = true
-				}
-			}
-		})
-	}
-
 	var transitiveAndroidMkSharedLibs []*android.DepSet[string]
 	var directAndroidMkSharedLibs []string
 
@@ -1609,13 +1567,6 @@
 	deps := mod.deps(ctx)
 	var commonDepVariations []blueprint.Variation
 
-	apiImportInfo := cc.GetApiImports(mod, actx)
-	if mod.usePublicApi() || mod.useVendorApi() {
-		for idx, lib := range deps.SharedLibs {
-			deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs)
-		}
-	}
-
 	if ctx.Os() == android.Android {
 		deps.SharedLibs, _ = cc.FilterNdkLibs(mod, ctx.Config(), deps.SharedLibs)
 	}
@@ -1708,15 +1659,7 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		// For core variant, add a dep on the implementation (if it exists) and its .apiimport (if it exists)
-		// GenerateAndroidBuildActions will pick the correct impl/stub based on the api_domain boundary
-		if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
-			cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
-		}
-
-		if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
-			cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, apiLibraryName, version, false)
-		}
+		cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
 	}
 
 	for _, lib := range deps.WholeStaticLibs {
@@ -1813,6 +1756,16 @@
 
 var _ android.ApexModule = (*Module)(nil)
 
+// If a module is marked for exclusion from apexes, don't provide apex variants.
+// TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets.
+func (m *Module) CanHaveApexVariants() bool {
+	if m.ApexExclude() {
+		return false
+	} else {
+		return m.ApexModuleBase.CanHaveApexVariants()
+	}
+}
+
 func (mod *Module) MinSdkVersion() string {
 	return String(mod.Properties.Min_sdk_version)
 }
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 3d81b83..00b3ca5 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -184,12 +184,21 @@
     libs: ["ninja_rsp"],
 }
 
-python_test_host {
-    name: "lint_project_xml_test",
-    main: "lint_project_xml_test.py",
+python_binary_host {
+    name: "lint_strict_updatability_checks",
+    main: "lint_strict_updatability_checks.py",
     srcs: [
-        "lint_project_xml_test.py",
-        "lint_project_xml.py",
+        "lint_strict_updatability_checks.py",
+    ],
+    libs: ["ninja_rsp"],
+}
+
+python_test_host {
+    name: "lint_strict_updatability_checks_test",
+    main: "lint_strict_updatability_checks_test.py",
+    srcs: [
+        "lint_strict_updatability_checks_test.py",
+        "lint_strict_updatability_checks.py",
     ],
     libs: ["ninja_rsp"],
     test_suites: ["general-tests"],
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/lint_project_xml.py b/scripts/lint_project_xml.py
index c40b07d..ce6aa21 100755
--- a/scripts/lint_project_xml.py
+++ b/scripts/lint_project_xml.py
@@ -75,8 +75,6 @@
                       help='file containing the module\'s manifest.')
   parser.add_argument('--merged_manifest', dest='merged_manifest',
                       help='file containing merged manifest for the module and its dependencies.')
-  parser.add_argument('--baseline', dest='baseline_path',
-                      help='file containing baseline lint issues.')
   parser.add_argument('--library', dest='library', action='store_true',
                       help='mark the module as a library.')
   parser.add_argument('--test', dest='test', action='store_true',
@@ -94,8 +92,6 @@
                      help='treat a lint issue as a warning.')
   group.add_argument('--disable_check', dest='checks', action=check_action('ignore'), default=[],
                      help='disable a lint issue.')
-  group.add_argument('--disallowed_issues', dest='disallowed_issues', default=[],
-                     help='lint issues disallowed in the baseline file')
   return parser.parse_args()
 
 
@@ -140,30 +136,10 @@
   f.write("</lint>\n")
 
 
-def check_baseline_for_disallowed_issues(baseline, forced_checks):
-  issues_element = baseline.documentElement
-  if issues_element.tagName != 'issues':
-    raise RuntimeError('expected issues tag at root')
-  issues = issues_element.getElementsByTagName('issue')
-  disallowed = set()
-  for issue in issues:
-    id = issue.getAttribute('id')
-    if id in forced_checks:
-      disallowed.add(id)
-  return disallowed
-
-
 def main():
   """Program entry point."""
   args = parse_args()
 
-  if args.baseline_path:
-    baseline = minidom.parse(args.baseline_path)
-    disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues)
-    if disallowed_issues:
-      sys.exit('disallowed issues %s found in lint baseline file %s for module %s'
-                         % (disallowed_issues, args.baseline_path, args.name))
-
   if args.project_out:
     with open(args.project_out, 'w') as f:
       write_project_xml(f, args)
diff --git a/scripts/lint_strict_updatability_checks.py b/scripts/lint_strict_updatability_checks.py
new file mode 100755
index 0000000..5b5dfd8
--- /dev/null
+++ b/scripts/lint_strict_updatability_checks.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""This file checks baselines passed to Android Lint for checks that must not be baselined."""
+
+import argparse
+import sys
+from xml.dom import minidom
+
+from ninja_rsp import NinjaRspFileReader
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  def convert_arg_line_to_args(arg_line):
+    for arg in arg_line.split():
+      if arg.startswith('#'):
+        return
+      if not arg.strip():
+        continue
+      yield arg
+
+  parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
+  parser.convert_arg_line_to_args = convert_arg_line_to_args
+  parser.add_argument('--name', dest='name',
+                      help='name of the module.')
+  parser.add_argument('--baselines', dest='baselines', action='append', default=[],
+                      help='file containing whitespace separated list of baseline files.')
+  parser.add_argument('--disallowed_issues', dest='disallowed_issues', default=[],
+                     help='lint issues disallowed in the baseline file')
+  return parser.parse_args()
+
+
+def check_baseline_for_disallowed_issues(baseline, forced_checks):
+  issues_element = baseline.documentElement
+  if issues_element.tagName != 'issues':
+    raise RuntimeError('expected issues tag at root')
+  issues = issues_element.getElementsByTagName('issue')
+  disallowed = set()
+  for issue in issues:
+    id = issue.getAttribute('id')
+    if id in forced_checks:
+      disallowed.add(id)
+  return disallowed
+
+
+def main():
+  """Program entry point."""
+  args = parse_args()
+
+  error = False
+  for baseline_rsp_file in args.baselines:
+    for baseline_path in NinjaRspFileReader(baseline_rsp_file):
+      baseline = minidom.parse(baseline_path)
+      disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues)
+      if disallowed_issues:
+        print('disallowed issues %s found in lint baseline file %s for module %s'
+                % (disallowed_issues, baseline_path, args.name))
+        error = True
+
+  if error:
+    sys.exit(1)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/lint_project_xml_test.py b/scripts/lint_strict_updatability_checks_test.py
old mode 100644
new mode 100755
similarity index 88%
rename from scripts/lint_project_xml_test.py
rename to scripts/lint_strict_updatability_checks_test.py
index 344691d..fd8610f
--- a/scripts/lint_project_xml_test.py
+++ b/scripts/lint_strict_updatability_checks_test.py
@@ -15,12 +15,12 @@
 # limitations under the License.
 #
 
-"""Unit tests for lint_project_xml.py."""
+"""Unit tests for lint_strict_updatability_checks.py."""
 
 import unittest
 from xml.dom import minidom
 
-import lint_project_xml
+import lint_strict_updatability_checks
 
 
 class CheckBaselineForDisallowedIssuesTest(unittest.TestCase):
@@ -44,7 +44,7 @@
       '</issues>\n')
 
   def test_check_baseline_for_disallowed_issues(self):
-    disallowed_issues = lint_project_xml.check_baseline_for_disallowed_issues(self.baseline_xml, ["foo", "bar", "qux"])
+    disallowed_issues = lint_strict_updatability_checks.check_baseline_for_disallowed_issues(self.baseline_xml, ["foo", "bar", "qux"])
     self.assertEqual({"foo", "bar"}, disallowed_issues)
 
 
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/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 8b994eb..34e11f0 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -46,8 +46,9 @@
 				],
 			}
 		`, apex, fragment, extraFragments)),
-		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
-		android.FixtureAddFile("frameworks/base/config/boot-image-profile.txt", nil),
+		android.FixtureAddFile("frameworks/base/boot/boot-profile.txt", nil),
+		android.FixtureAddFile("frameworks/base/boot/boot-image-profile.txt", nil),
+		android.FixtureAddFile("art/build/boot/boot-image-profile.txt", nil),
 		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
 	)
 }
@@ -204,7 +205,19 @@
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar
 		`),
-		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+		snapshotTestPreparer(checkSnapshotWithoutSource, android.GroupFixturePreparers(
+			preparerForSnapshot,
+			// Flag ART prebuilts
+			android.FixtureMergeMockFs(android.MockFS{
+				"apex_contributions/Android.bp": []byte(`
+				apex_contributions {
+					name: "prebuilt_art_contributions",
+					contents: ["prebuilt_com.android.art"],
+					api_domain: "com.android.art",
+				}
+			`)}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
+		)),
 
 		// Check the behavior of the snapshot without the source.
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
@@ -212,8 +225,8 @@
 			checkBootJarsPackageCheckRule(t, result,
 				append(
 					[]string{
-						"out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
-						"out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
+						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_com.android.art/deapexer/javalib/core1.jar",
+						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_com.android.art/deapexer/javalib/core2.jar",
 						"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
 					},
 					java.ApexBootJarDexJarPaths...,
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0fb5c70..15e13db 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1360,10 +1360,9 @@
 }
 `),
 		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
-			ctx := android.ModuleInstallPathContextForTesting(result.Config)
 			dexJarBuildPath := func(name string, kind android.SdkKind) string {
-				dep := result.Module(name, "android_common").(java.SdkLibraryDependency)
-				path := dep.SdkApiExportableStubDexJar(ctx, kind).Path()
+				sdkLibInfo, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), result.Module(name, "android_common"), java.SdkLibraryInfoProvider)
+				path := sdkLibInfo.ExportableStubDexJarPaths[kind].Path()
 				return path.RelativeToTop().String()
 			}
 
@@ -1703,61 +1702,6 @@
 	)
 }
 
-func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
-		sdk {
-			name: "mysdk",
-			java_sdk_libs: ["myjavalib"],
-		}
-
-		java_sdk_library {
-			name: "myjavalib",
-			apex_available: ["//apex_available:anyapex"],
-			srcs: ["Test.java"],
-			sdk_version: "current",
-			naming_scheme: "default",
-			public: {
-				enabled: true,
-			},
-		}
-	`)
-
-	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-apex_contributions_defaults {
-    name: "mysdk.contributions",
-    contents: ["prebuilt_myjavalib"],
-}
-
-java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    naming_scheme: "default",
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-}
-`),
-		checkAllCopyRules(`
-.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-`),
-		checkMergeZips(
-			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
-		),
-	)
-}
-
 func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 2fb3a3f..aa82abb 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -84,20 +84,6 @@
 
 	// True if this is a module_exports (or module_exports_snapshot) module type.
 	Module_exports bool `blueprint:"mutated"`
-
-	// The additional visibility to add to the prebuilt modules to allow them to
-	// reference each other.
-	//
-	// This can only be used to widen the visibility of the members:
-	//
-	// * Specifying //visibility:public here will make all members visible and
-	//   essentially ignore their own visibility.
-	// * Specifying //visibility:private here is an error.
-	// * Specifying any other rule here will add it to the members visibility and
-	//   be output to the member prebuilt in the snapshot. Duplicates will be
-	//   dropped. Adding a rule to members that have //visibility:private will
-	//   cause the //visibility:private to be discarded.
-	Prebuilt_visibility []string
 }
 
 // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
@@ -130,8 +116,6 @@
 
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties, &traitsWrapper)
 
-	// Make sure that the prebuilt visibility property is verified for errors.
-	android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility)
 	android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(s)
 	android.AddLoadHook(s, func(ctx android.LoadHookContext) {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 416dce6..2532a25 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -53,9 +53,6 @@
 				// generated sdk_snapshot.
 				":__subpackages__",
 			],
-			prebuilt_visibility: [
-				"//prebuilts/mysdk",
-			],
 			java_header_libs: [
 				"myjavalib",
 				"mypublicjavalib",
@@ -162,18 +159,6 @@
 `))
 }
 
-func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) {
-	testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, `
-		sdk {
-			name: "mysdk",
-			prebuilt_visibility: [
-				"//foo",
-				"//visibility:private",
-			],
-		}
-`)
-}
-
 func TestSdkInstall(t *testing.T) {
 	sdk := `
 		sdk {
diff --git a/sdk/update.go b/sdk/update.go
index 93bb861..7f4f80a 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -22,7 +22,6 @@
 	"sort"
 	"strings"
 
-	"android/soong/apex"
 	"android/soong/cc"
 	"android/soong/java"
 
@@ -1137,9 +1136,6 @@
 			apexAvailable = []string{android.AvailableToPlatform}
 		}
 
-		// Add in any baseline apex available settings.
-		apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...)
-
 		// Remove duplicates and sort.
 		apexAvailable = android.FirstUniqueStrings(apexAvailable)
 		sort.Strings(apexAvailable)
@@ -1993,14 +1989,6 @@
 	return m.builder.targetBuildRelease.EarlierThan(buildReleaseT)
 }
 
-func (m *memberContext) Config() android.Config {
-	return m.sdkMemberContext.Config()
-}
-
-func (m *memberContext) OtherModulePropertyErrorf(module android.Module, property string, fmt string, args ...interface{}) {
-	m.sdkMemberContext.OtherModulePropertyErrorf(module, property, fmt, args)
-}
-
 var _ android.SdkMemberContext = (*memberContext)(nil)
 
 func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) {
diff --git a/soong_ui.bash b/soong_ui.bash
index 7737880..be78b68 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -14,18 +14,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh
+require_top
+
 # To track how long we took to startup.
 case $(uname -s) in
   Darwin)
-    export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
+    export TRACE_BEGIN_SOONG=`$TOP/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
     ;;
   *)
     export TRACE_BEGIN_SOONG=$(date +%s%N)
     ;;
 esac
 
-source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh
-require_top
+setup_cog_env_if_needed
 
 # Save the current PWD for use in soong_ui
 export ORIGINAL_PWD=${PWD}
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..8ecd037
--- /dev/null
+++ b/tests/build_action_caching_test.sh
@@ -0,0 +1,190 @@
+#!/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 "$@" 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() {
+  local test_dir="${OUTPUT_DIR}/test_build_action_restoring"
+  mkdir -p ${test_dir}
+  run_soong_clean
+  cat > ${test_dir}/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["my_little_binary_host.py"],
+}
+EOF
+  touch ${test_dir}/my_little_binary_host.py
+  run_soong_build --incremental-build-actions
+  local dir_before="${test_dir}/before"
+  mkdir -p ${dir_before}
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_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 >> ${test_dir}/Android.bp <<'EOF'
+// new comments
+EOF
+  run_soong_build --incremental-build-actions
+  local dir_after="${test_dir}/after"
+  mkdir -p ${dir_after}
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/after
+
+  compare_incremental_files $dir_before $dir_after
+  rm -rf "$test_dir"
+  echo "test_build_action_restoring test passed"
+}
+
+function test_incremental_build_parity() {
+  local test_dir="${OUTPUT_DIR}/test_incremental_build_parity"
+  run_soong_clean
+  run_soong_build
+  local dir_before="${test_dir}/before"
+  mkdir -p ${dir_before}
+  cp -pr out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/before
+
+  # Now run clean build with incremental enabled
+  run_soong_clean
+  run_soong_build --incremental-build-actions
+  local dir_after="${test_dir}/after"
+  mkdir -p ${dir_after}
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/after
+
+  compare_files_parity $dir_before $dir_after
+  rm -rf "$test_dir"
+  echo "test_incremental_build_parity test passed"
+}
+
+function compare_files_parity() {
+  local dir_before=$1; shift
+  local dir_after=$1; shift
+  count=0
+  for file_before in ${dir_before}/*.mk; do
+    file_after="${dir_after}/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    ((count++))
+  done
+  echo "Compared $count mk files"
+
+  combined_before_file="${dir_before}/combined_files.ninja"
+  count=0
+  for file in ${dir_before}/build.aosp_arm.*.ninja; do
+    cat $file >> $combined_before_file
+    ((count++))
+  done
+  echo "Combined $count ninja files from normal build"
+
+  combined_after_file="${dir_after}/combined_files.ninja"
+  count=0
+  for file in ${dir_after}/build.aosp_arm.*.ninja; do
+    cat $file >> $combined_after_file
+    ((count++))
+  done
+  echo "Combined $count ninja files from incremental build"
+
+  combined_incremental_ninjas="${dir_after}/combined_incremental_files.ninja"
+  count=0
+  for file in ${dir_after}/build_aosp_arm_ninja_incremental/*.ninja; do
+    cat $file >> $combined_incremental_ninjas
+    ((count++))
+  done
+  echo "Combined $count incremental ninja files"
+
+  cat $combined_incremental_ninjas >> $combined_after_file
+  sort $combined_after_file -o $combined_after_file
+  sort $combined_before_file -o $combined_before_file
+  assert_files_equal $combined_before_file $combined_after_file
+}
+
+function compare_incremental_files() {
+  local dir_before=$1; shift
+  local dir_after=$1; shift
+  count=0
+  for file_before in ${dir_before}/*.ninja; do
+    file_after="${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 ${dir_before}/*.mk; do
+    file_after="${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 ${dir_before}/build_aosp_arm_ninja_incremental/*.ninja; do
+    file_after="${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_incremental_build_parity
+test_build_action_restoring
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index ddd0a80..e230795 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -196,16 +196,16 @@
 }
 
 var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
-	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}",
+	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs} ${extraTestRunnerConfigs}",
 	CommandDeps: []string{
 		"${AutoGenTestConfigScript}",
 		"${EmptyTestConfig}",
 		"$template",
 	},
-}, "name", "template", "extraConfigs")
+}, "name", "template", "extraConfigs", "extraTestRunnerConfigs")
 
 func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path {
+	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config, testRunnerConfigs []Option) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
 	var configStrings []string
 	if autogenPath != nil {
@@ -220,15 +220,26 @@
 		extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
 		extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs)
 
+		var testRunnerConfigStrings []string
+		for _, config := range testRunnerConfigs {
+			testRunnerConfigStrings = append(testRunnerConfigStrings, config.Config())
+		}
+		extraTestRunnerConfigs := strings.Join(testRunnerConfigStrings, fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent))
+		if len(extraTestRunnerConfigs) > 0 {
+			extraTestRunnerConfigs += fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
+		}
+		extraTestRunnerConfigs = fmt.Sprintf("--extra-test-runner-configs '%s'", extraTestRunnerConfigs)
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        autogenInstrumentationTest,
 			Description: "test config",
 			Input:       manifest,
 			Output:      autogenPath,
 			Args: map[string]string{
-				"name":         ctx.ModuleName(),
-				"template":     template,
-				"extraConfigs": extraConfigs,
+				"name":                   ctx.ModuleName(),
+				"template":               template,
+				"extraConfigs":           extraConfigs,
+				"extraTestRunnerConfigs": extraTestRunnerConfigs,
 			},
 		})
 		return autogenPath
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index ef18131..7a04c19 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -242,6 +242,7 @@
 
 				entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", m.provider.IsUnitTest)
 				entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
+				entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
 
 				// The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk.
 				// Normally, this copies the "package.apk" from the intermediate directory here.
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index 1510a03..f76a152 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -40,6 +40,7 @@
 			name: "base",
 			sdk_version: "current",
                         data: [":HelperApp", "data/testfile"],
+                        host_required: ["other-module"],
                         test_suites: ["general-tests"],
 		}
 
@@ -80,6 +81,7 @@
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
 
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_REQUIRED_MODULES"], []string{"base"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_HOST_REQUIRED_MODULES"], []string{"other-module"})
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_CERTIFICATE"], []string{"build/make/target/product/security/testkey.x509.pem"})
 	android.AssertStringEquals(t, "", entries.Class, "APPS")
 
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index 2bad5a8..2ec8972 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -25,6 +25,8 @@
 	"dalvik/",
 	"developers/",
 	"development/",
+	"device/common/",
+	"device/google_car/",
 	"device/sample/",
 	"frameworks/",
 	// Do not block other directories in kernel/, see b/319658303.
@@ -41,6 +43,15 @@
 	"trusty/",
 	// Add back toolchain/ once defensive Android.mk files are removed
 	//"toolchain/",
+	"vendor/google_contexthub/",
+	"vendor/google_data/",
+	"vendor/google_elmyra/",
+	"vendor/google_mhl/",
+	"vendor/google_pdk/",
+	"vendor/google_testing/",
+	"vendor/partner_testing/",
+	"vendor/partner_tools/",
+	"vendor/pdk/",
 }
 
 func blockAndroidMks(ctx Context, androidMks []string) {
diff --git a/ui/build/config.go b/ui/build/config.go
index f02222e..75edfcd 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 {
@@ -88,6 +98,7 @@
 	buildFromSourceStub      bool
 	incrementalBuildActions  bool
 	ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
+	partialCompileFlags      partialCompileFlags
 
 	// From the product config
 	katiArgs        []string
@@ -123,9 +134,18 @@
 	// 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 partialCompileFlags struct {
+	// Is partial compilation enabled at all?
+	enabled bool
+
+	// Whether to use d8 instead of r8
+	use_d8 bool
+
+	// Add others as needed.
 }
 
 type NinjaWeightListSource uint
@@ -284,12 +304,22 @@
 		ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false")
 	}
 
+	ret.partialCompileFlags = parsePartialCompileFlags(ctx)
+
 	if os.Getenv("GENERATE_SOONG_DEBUG") == "true" {
 		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,8 +379,10 @@
 		// 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",
+		"SOONG_PARTIAL_COMPILE",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {
@@ -469,6 +501,78 @@
 	return c
 }
 
+// Parse SOONG_PARTIAL_COMPILE.
+//
+// The user-facing documentation shows:
+//
+// - empty or not set: "The current default state"
+// - "true" or "on": enable all stable partial compile features.
+// - "false" or "off": disable partial compile completely.
+//
+// What we actually allow is a comma separated list of tokens, whose first
+// character may be "+" (enable) or "-" (disable).  If neither is present, "+"
+// is assumed.  For example, "on,+use_d8" will enable partial compilation, and
+// additionally set the use_d8 flag (regardless of whether it is opt-in or
+// opt-out).
+//
+// To add a new feature to the list, add the field in the struct
+// `partialCompileFlags` above, and then add the name of the field in the
+// switch statement below.
+func parsePartialCompileFlags(ctx Context) partialCompileFlags {
+	defaultFlags := partialCompileFlags{
+		// Set any opt-out flags here.  Opt-in flags are off by default.
+		enabled: false,
+	}
+	value, ok := os.LookupEnv("SOONG_PARTIAL_COMPILE")
+
+	if !ok {
+		return defaultFlags
+	}
+
+	ret := defaultFlags
+	tokens := strings.Split(strings.ToLower(value), ",")
+	makeVal := func(state string, defaultValue bool) bool {
+		switch state {
+		case "":
+			return defaultValue
+		case "-":
+			return false
+		case "+":
+			return true
+		}
+		return false
+	}
+	for _, tok := range tokens {
+		var state string
+		switch tok[0:1] {
+		case "":
+			// Ignore empty tokens.
+			continue
+		case "-", "+":
+			state = tok[0:1]
+			tok = tok[1:]
+		default:
+			// Treat `feature` as `+feature`.
+			state = "+"
+		}
+		switch tok {
+		case "true", "on", "yes":
+			ret = defaultFlags
+			ret.enabled = true
+		case "false", "off", "no":
+			// Set everything to false.
+			ret = partialCompileFlags{}
+		case "enabled":
+			ret.enabled = makeVal(state, defaultFlags.enabled)
+		case "use_d8":
+			ret.use_d8 = makeVal(state, defaultFlags.use_d8)
+		default:
+			ctx.Fatalln("Unknown SOONG_PARTIAL_COMPILE value:", value)
+		}
+	}
+	return ret
+}
+
 // NewBuildActionConfig returns a build configuration based on the build action. The arguments are
 // processed based on the build action and extracts any arguments that belongs to the build action.
 func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config {
@@ -1375,8 +1479,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,13 +1747,17 @@
 	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) {
-			asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
-			if _, err := os.Stat(asan); err == nil {
-				return asan
-			}
+	if c.environ.IsEnvTrue("SANITIZE_BUILD_TOOL_PREBUILTS") {
+		asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
+		if _, err := os.Stat(asan); err == nil {
+			return asan
 		}
 	}
 	return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
@@ -1745,6 +1855,10 @@
 	return c.ensureAllowlistIntegrity
 }
 
+func (c *configImpl) PartialCompileFlags() partialCompileFlags {
+	return c.partialCompileFlags
+}
+
 // Returns a Time object if one was passed via a command-line flag.
 // Otherwise returns the passed default.
 func (c *configImpl) BuildStartedTimeOrDefault(defaultTime time.Time) time.Time {
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..f70d9b7 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -27,6 +27,7 @@
 	"strings"
 	"sync"
 	"sync/atomic"
+	"syscall"
 	"time"
 
 	"android/soong/ui/tracer"
@@ -588,19 +589,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 +608,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 +649,6 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
-		ninjaCmd := config.NinjaBin()
-		if config.useN2 {
-			ninjaCmd = config.N2Bin()
-		}
 
 		cmd := Command(ctx, config, "soong bootstrap",
 			ninjaCmd, ninjaArgs...)
@@ -744,7 +766,11 @@
 	globsChan := make(chan pathtools.GlobResult)
 	errorsChan := make(chan error)
 	wg := sync.WaitGroup{}
+
 	hasChangedGlobs := false
+	var changedGlobNameMutex sync.Mutex
+	var changedGlobName string
+
 	for i := 0; i < runtime.NumCPU()*2; i++ {
 		wg.Add(1)
 		go func() {
@@ -759,7 +785,7 @@
 				hasNewDep := false
 				for _, dep := range cachedGlob.Deps {
 					info, err := os.Stat(dep)
-					if errors.Is(err, fs.ErrNotExist) {
+					if errors.Is(err, fs.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) {
 						hasNewDep = true
 						break
 					} else if err != nil {
@@ -782,6 +808,13 @@
 				} else {
 					if !slices.Equal(result.Matches, cachedGlob.Matches) {
 						hasChangedGlobs = true
+
+						changedGlobNameMutex.Lock()
+						defer changedGlobNameMutex.Unlock()
+						changedGlobName = result.Pattern
+						if len(result.Excludes) > 0 {
+							changedGlobName += " (excluding " + strings.Join(result.Excludes, ", ") + ")"
+						}
 					}
 				}
 			}
@@ -833,6 +866,7 @@
 
 	if hasChangedGlobs {
 		fmt.Fprintf(os.Stdout, "Globs changed, rerunning soong...\n")
+		fmt.Fprintf(os.Stdout, "One culprit glob (may be more): %s\n", changedGlobName)
 		// Write the current time to the glob_results file. We just need
 		// some unique value to trigger a rerun, it doesn't matter what it is.
 		err = os.WriteFile(
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"],
 }